# 浅克隆
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);
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"
}
}
}
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
}
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;
}
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
2
var num = 1 > 0 ? ("10" > "9" ? 1 : 0) : 2
//字符串比较的是ask码,会逐位进行比较
//一零字符串小于9字符串 所以结果是0
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;
}
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
2
3
4
5
6
7
8
# splice 例子
把4传到3到5的中间。
var arr = [1,2,3,5];
arr.splice(3,0,4);
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()
//调用下升序,再倒序
2
3
4
sort
arr.sort()
方法默认是按ask码排序的,所以有时候排序会不准确。
# 例子
var arr = [1,3,5,10,2,6];
arr.sort() //[1,10,2,3,5,6]
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;
}
});
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; 降序
})
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; //返回数组的乱序
})
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"
}
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
})
//给字符串按照长度来排序
2
3
4
5
# 按照字符串长度排序
var arr = ['ab','ssacas','fdff','wwewqq''qwerewr''hrth'];
arr.sort(function(a,b) {
return a.length - b.length;
});
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);
})
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]
//会把传进来的数组拼接到自己的后面返回一个新数组
//不改变前面两个的原数组
2
3
4
5
# toString方法(变成字符串)
数组的toString是把数组里面的值全部变成字符串返回出来。
var arr = [1,2,3,4,5,6];
arr.toString() //"1,2,3,4,5,6"
2
# slice方法
把字符串截取出来,功能和splice有点像但用法不一样。
slice
里面可以不传参数,可以传一个参数,可以传两个参数
传两个参数规则
: slice(从该位开始截取,截取到该位(不包含该位左闭右开))
var arr = [1,2,3,4,5,6]
//两个参数规则:从第几位开始截取,截取到该位,不包含开始位
arr.slice(1,3) // 23
//一个参数规则:从第几位开始截取,一直截取到最后,不包含开始位
arr.slice(1) //23456
//也可以填负数,负数就是+length (-几加上总位数。)
//不写参数: 整个截取下来(把一个类数组转换成数组,截取整个的长度)
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"]
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("");
//不需要什么东西来连接,就传空串,不传值是按逗号,来连接的
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);
2
3
4
5
# 类数组的组成
var obj = {
"0" : 'a',
"1" : 'b',
"2" : 'c',
"length" : 3,
"push" : Array.prototype.push,
"splice" : Array.prototype.splice
}
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属性,既能像数组一样用,也能像对象一样用。
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))
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']
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'
}
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;
}
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]);