# 浅克隆

    var obj = {
        name : 'abc',
        age : 123,
        sex : 'female'
    }
    var obj1 = {};
//写一个方法将obj的属性属性名全部克隆到obj1里面去。
    function clone(origin,target) {
//origin 代表被克隆者,target 代表克隆者
        var target = target || { };
// 传不传第二个参数也行,工厂给提供对象 target=target或空对象
//如果用户传递第二个参数了就用用户的如果没传就用空对象接收
        for(var prop in origin) {
            target[prop] = origin[prop];
            //用 for in循环循环 obj 的值 = obj1 的值
        }
        return target;//最后把克隆完的对象返回回去
    }
    clone(obj,obj1);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

浅克隆

只拷贝了原始值。

给obj.name赋予新的值,obj1.name不改变,原始值只拷贝了属性。

# 深克隆

深克隆是如果obj里面有引用值属性,比如obj.card。obj1克隆过去也有obj1.card,但是两个是不一样的人。obj1.card里面的值改 obj.card里面的值不改。 浅克隆是如果给obj1.card给方法和属性,obj(被克隆者也会有相同的属性和方法)

        var obj = {
            name : "abc",
            age : 123,
            card : ['visa','master'],
            wife : {
                name : "bcd",
                son : {
                    name : "aaa"
                }
            }
        }
        var obj1 = {
            name : obj.name,
            age : 123,
            card : [obj.card[0],obj.card[1]],
            wife : {
                name : "bcd",
                son : {
                    name : "aaa"
                }
            }
        }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 深度克隆步骤

1.把obj里面的属性全部拷贝到obj1当中,先实现对象的遍历。 数组也可以用for in循环 因为数组也是特定的对象。

var arr = ['a','b','c']
    for(var prop in arr) {
        cosole.log(arr[prop]);
        //prop此时代表的arr的下标位
        //打印 a b c
}
1
2
3
4
5
6

2.判断是不是原始值,typeof() 如果是object的话就是引用值 如果不是 基本上是原始值

3.判断是不是引用值(判断是不是数组或者对象) instanceof toString constructor

4.建立相应的数组或对象,把obj里面的引用值的值['master','visa'],{bac,aaa}当做原始值(被拷贝对象),把建立的数组或者对象当做拷贝者,再一次进行深度拷贝。

5.递归

function deepClone(origin, target) {
            // 防止用户传第二个参数或者不传
       var target = target || {},
            // 用来判断是数组还是对象用
             toStr = Object.prototype.toString,
            // arrStr是数组的话用来比对
             arrStr = "[object Array]";
            // 开始遍历obj
for (var prop in origin) {
             //防止拷贝obj原型链上的属性
 if (origin.hasOwnProperty(prop)) {
                //判断引用值    不是null而且 typeof还是object
   if (origin[prop] !== "null" && typeof (origin[prop]) == 'object') {
                // if(toStr.call(origin[prop]) == arrStr) {
                //     target[prop] = [];
                // }else {
                //     target[prop] = {};
                // }
//三目,要返回target[prop],前面写条件,成立就建立数组否则就对象
     target[prop] = toStr.call(origin[prop]) == arrStr ? [] : {};
// 把拷贝下来的obj的属性和obj1的属性再进行深拷贝递归判断
                deepClone(origin[prop], target[prop]);
           } else {
                //直到递归拷贝成功
                  target[prop] = origin[prop];
             }
         }
     }
            //避免用户没传第二个参数返回第二个参数
            return target;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

# 三目运算符

执行过程

? :

条件判断? 是 :否 并且返回值。

问号前面是一个条件判断,如果判断是,走冒号前面的。如果不是,走冒号后面的。并且会返回值。

# 例子

var num = 1 >0 ? 2 + 2 : 1+1; 
//num=4
1
2
var num = 1 > 0 ? ("10" > "9" ? 1 : 0) : 2
//字符串比较的是ask码,会逐位进行比较
//一零字符串小于9字符串 所以结果是0
1
2
3

# 数组

# 定义数组

数组字面量 var arr = []; 构造方法定义数组 var arr = new Array();

var arr = [,,]不会报错 这种写法叫稀松数组

var arr = new Array[1,2,3,4,5]; 可以进行传参。

var arr = new Array(10); 传进去一个10 arr数组里面有10个空值

# 数组的读和写

var arr = []; arr[10] 打印undefined

提示

数组在 JavaScript 里面,几乎是没有报错的可能。

# 赋值

arr[10] = "abc" 往空数组里面写 arr[10] 会返回 [undefined * 10 ,abc],此刻arr的length为11

# 数组常用方法

# 改变原数组

改变原数组(在原数组的基础上添加或者删除)

push,pop,shift,unshift,sort,reverse splice

# push方法

在数组的最后一位添加值arr.push(9) 可以同时添加多个。

# push方法原理

push是把数据放到数组的最后一位,数据的最后一位就等于arr.length

    Array.prototype.push = function () {
        //把想要传递的实参全部都遍历下,往后面传参
        for(var i = 0;i < arguments.length; i++) {
        //谁调用它this就是谁所以写this
            this[this.length] = arguments[i];
        }
        return this.length;
    }
1
2
3
4
5
6
7
8

# pop方法

把数组的最后一位剪切出去,arr.pop(),不用传参,因为只能删除最后一位。

# unshift

在数组的最前面添加 arr.unshift(0) , 打印 0123,跟push方向相反

# shift

在数组的最前面减 arr.shift() ,打印 23

# reverse方法

倒序,会把原数组返回出来 arr.reverse() ,打印 321

# splice方法(切片)

               //0 1 2 3 4 5
    var arr = [1,1,2,2,3,3];
//arr.splice(下标,下标)
//从第几位开始,截取多少的长度,在切口处添加新的数据
//会把截取完的片段返回回来,还改变原数组
    arr.splice(1,2); //第三个参数传不传都行,打印1 2 3 3
    arr.splice(0,3);     //打印 2 3 3
    arr.splice(1,1,0,0,0); //打印100002233
1
2
3
4
5
6
7
8

# splice 例子

把4传到3到5的中间。

    var arr = [1,2,3,5];
    arr.splice(3,0,4);
1
2

负数的话就加length,-1位+4=3,第三位就是5

# sort方法(数组排序)

var arr = [-2,3,5,2,1,6]
arr.sort()//数组升序排序  -2,1,2,3,5,6
//降序arr.sort().reverse()
//调用下升序,再倒序
1
2
3
4

sort

arr.sort()方法默认是按ask码排序的,所以有时候排序会不准确。

# 例子

var arr = [1,3,5,10,2,6];
arr.sort()  //[1,10,2,3,5,6]
1
2

# 封装 sort

封装一个以数字排序的sort()方法。(冒泡排序)

var arr = [1,3,5,10,2,6];
//第一次调用函数会把数组的第一个数和第二个数传入实参来
//通过比较返回值来进行
//传参顺序是 13      15      1 10   12    16     35  34  310
//以此类推
//第一轮结束会把第一轮最小的数放在前面,第二轮第三轮以此类推
    arr.sort(function(a,b){
        if(a > b) {
            return 1;
        }else {
            return -1;
        }
    });
1
2
3
4
5
6
7
8
9
10
11
12
13

过程

1.必须写两个形参。

2.看返回值。

当返回值为负数时,那么前面的数放在前面(原封不动),当为正数时,那么后面的数在前(当场调换位置),为0时,不动。

分析

第一轮进行完会把第一轮最小的数放在最前面,第二轮进行完会把第二轮最小的数放在最前面,以此类推,形成升序排序

当a<b时,函数称为降序,此类写法可以换另一种书写方式a>b要返回正数,a<b要返回负数。

a>b相当于a-b>0,当else的时候要返回一个负数,当else的时候表示a不大于b,b>a,b>a,a-b还是个负数

所以无论啥都是返回a-b

最终写法:

var arr = [1,3,5,10,2,6];
arr.sort(function (a,b) {
    return a - b; //升序
    //return b - a; 降序
})
1
2
3
4
5

升序a-b 降序b-a

# 乱序

给一个有序的数组乱序。

var arr = [1,2,3,4,5,6,7];
    // Math.random(); 产生一个0-1之间的开区间数
    //开区间(0,1) 不包含括号里面的
    //闭区间[0,1]包含括号里面的
    arr.sort.(function(){
       return Mtah.random() - 0.5;    //返回数组的乱序
    })
1
2
3
4
5
6
7

# 例题

var cheng = {
        name : "cheng",
        age : 18,
        sex : "male",
        face : "handsome"
    }
    var deng = {
        name : "deng",
        age : 40,
        sex : undefined,
        face : "amazing"
    }
    var zhang = {
        name : "zhang",
        age : 20,
        sex : "male"
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 把三个对象放到一个数组里面 按照年龄升序排序。

var arr = [cheng,deng,zhang];
    arr.sort(function(a,b) {
        return a.age - b.age
    })
    //给字符串按照长度来排序
1
2
3
4
5

# 按照字符串长度排序

var arr = ['ab','ssacas','fdff','wwewqq''qwerewr''hrth'];
arr.sort(function(a,b) {
    return a.length - b.length;
});
1
2
3
4

# 返回字节长度

返回字节长度方法

function retBytes(str) {
    var num = str.length;
    for(var i = 0; i < str.length; i ++) {
        if(str.charCodeAt(i) > 255) {
            num ++;
        }
    }
    return num;
}
// var str = 'helloworld'
// retBytes(str)  10 中文的话两个字节
var arr = ['a邓','试试','qwq','阿萨德','wqqqd','发发发','fff','我我我',]
arr.sort(function(a,b) {
    return retBytes(a) - retBytes(b);
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 不改变原数组

不改变原数组

concat``.join -- > split,toString,slice

# concat方法(连接两个数组)

var arr = [1,2,3,4,5,6];
var arr1 = [7,8,9];
arr.concat(arr1)  //arr[1,2,3,4,5,6,7,8,9] 
//会把传进来的数组拼接到自己的后面返回一个新数组
//不改变前面两个的原数组
1
2
3
4
5

# toString方法(变成字符串)

数组的toString是把数组里面的值全部变成字符串返回出来。

var arr = [1,2,3,4,5,6];
arr.toString()  //"1,2,3,4,5,6"
1
2

# slice方法

把字符串截取出来,功能和splice有点像但用法不一样。

slice里面可以不传参数,可以传一个参数,可以传两个参数

传两个参数规则: slice(从该位开始截取,截取到该位(不包含该位左闭右开))

var arr = [1,2,3,4,5,6]
//两个参数规则:从第几位开始截取,截取到该位,不包含开始位
arr.slice(1,3)      // 23
//一个参数规则:从第几位开始截取,一直截取到最后,不包含开始位
arr.slice(1)    //23456
//也可以填负数,负数就是+length (-几加上总位数。)
//不写参数: 整个截取下来(把一个类数组转换成数组,截取整个的长度)
1
2
3
4
5
6
7

# join方法

会把数组里面的每一位连接起来并且返回成一个字符串,按照传入的参数来连接,必须传入字符串形式的

    var arr = [1,2,3,4,5,6]
    arr.join("-")   //[“1-2-3-4-5-6”]
    //与其对应的有个字符串的方法split,用-拆分,互逆
    str.split("-") //["1","2","3","4","5","6"]
    //splice按哪个拆就把哪个数值拆了
    str.splice("4") //["1-2-3-","-5-6"]
1
2
3
4
5
6

# join用法

将字符串拼接(纯字母拼接)

    var str = "alibaba";
    var str1 = "baidu";
    var str2 = "tencent";
    var str3 = "toutiao";
    var str4 = "wangyi";
    var str5 = "xiaowang";
    var str6 = 'nv';
    
    var arr = [str,str1,str3,str4,str5,str6];
    arr.join(""); 
    //不需要什么东西来连接,就传空串,不传值是按逗号,来连接的
1
2
3
4
5
6
7
8
9
10
11

# 类数组

长的像数组,其实是对象,也可以当数组用,但不是数组.

1.可以利用属性名模拟数组的特性

2.可以动态的增长length属性

3.如果强行让类数组调用push方法,则会根据length属性值的位置进行属性的扩充。

function test() {
        console.log(arguments);  
        //实参列表 数组123456789
    }
    test(1,2,3,4,5,6,7,8,9);
1
2
3
4
5

# 类数组的组成

var obj = {
        "0" : 'a',
        "1" : 'b',
        "2" : 'c',
        "length" : 3,
        "push" : Array.prototype.push,
        "splice" : Array.prototype.splice
    }
1
2
3
4
5
6
7
8

# 类数组特点

1.属性要为索引(数字)属性,一定有length属性,最好加上push。 2.给一个对象加上splice属性后,这个对象就长的跟数组一模一样了。 3.是对象,可以当数组一样用

# push原理

Array.prototype.push = function (target) {
        obj[obj.length] = target;
        obj.length ++;
    }
    obj.push('c')   //3 : c
    obj.push('d')   //4 : d
//类数组的关键点在length,
//push方法的话会把原本的属性覆盖掉(从前面加)
//类数组必须写length属性,既能像数组一样用,也能像对象一样用。
1
2
3
4
5
6
7
8
9

# 封装type

封装一个 type 函数, typeof([]) -- > array typeof({}) -- > object typeof(function) -- >function typeof(new Number()) -- > number object typeof(123) -- > number

    function type(target) {
        //1.分两类 原始值 引用值
        //2.原始值通过typeof()可以返回,引用值要区分
        var template = {
            "[object Array]" : "array",
            "[object Object]" : "object",
            "[object Number]" : "number - object",
            "[object Boolean]" : "boolean - object",
            "[object String]" : "string - object",
        }
        if(target === null) {
            return "null";
        }
        if(typeof(target) == "object") {
            var str = Object.prototype.toString.call(target);
            return template[str];
        }else {
            return typeof(target);
        }
    }
//先判断是null的话就返回null
//再判断是function的话返回function,不是返回object,
//如果是object,可能是几种类型(数组,对象,包装类(Object.prototype.toString))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 数组去重

要求在原型链上编程。

要求:

Array.prototype.unique = function () {

    }
    var arr = [1,1,1,1,0,0,0,'a','b','b','a'];
    arr.unique() // [0,1,'a','b']
1
2
3
4
5

实现思路:

var arr = [1,1,1,1,1,2,2,2,2,,2,1,1,1,1,];
    var obj = {
        "1" : 'abc'"2" : 'abc'
    }
1
2
3
4
5

实现思路

利用对象的特性,同一个属性名对象不可能出现两次,写一个对象,对象里面存属性,属性就是数组的下标,从数组的第一个下标开始建立属性名,第二个下标如果和第一个下标数组值重复则不用建立,不重复继续建立。对象构造完毕只需要把属性名取出来就去重完毕。通过hash数组的方式完成去重。

Array.prototype.unique = function () {
     var temp = {};
        //定义一个对象一会儿去比对
          arr = [];  
          //数组去重返回一个全新的数组,不影响原来的数组
          len = this.length; 
          //把this.length定义成一个值,减少循环取值的次数

        //遍历数组里面的值
        for(var i = 0; i < len; i ++) {
        //判断数组的第i位到对象里面判断有没有值
        //如果取到了,就不操作,(不重复)
        //如果没取到数组里面的值(undefined),也就是取反 非,
            if(!temp[this[i]]){
                temp[this[i]] = "abc";
                arr.push(this[i]);
                    }
            }
        return arr;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
详情

判断temp对象里面的this.i位(数组里面的第几位),数组的第几位 当做对象的属性名添加进去,看看是否有值。如果取到了啥也不干,如果没取到或者说取到undefined,取到undefined干脆反着写,写非取到!。if判断的就是没取到.能走到temp里面的值都是没经过重复的值,因为已经声明好数组arr了,所以把里面的值全部push到arr里 arr.push(this[i]);