2-5、对象的深克隆
对象的深克隆
- 复习什么是深克隆:
克隆对象的全貌,不论对象的属性值是否又是引用类型值,都能将它们实现克隆
- 和数组的深克隆类似,对象的深克隆需要使用
递归
- 面试时经常会考察深克隆算法,必须掌握
代码案例
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>2-5、对象的深克隆</title>
</head>
<body>
<script>
var obj1 = {
a: 1,
b: 2,
c: [33, 44, {
m: 55,
n: 66,
p: [77, 88]
}]
}
// 深克隆
function deepClone(o) {
// 判断o是对象还是数组
if (Array.isArray(o)) {
// 数组
var result = [];
for (var i = 0; i < o.length; i++) {
result.push(deepClone(o[i]));
}
} else if (typeof o == "object") {
// 对象
var result = {};
for (var k in o) {
result[k] = deepClone(o[k]);
}
} else {
// 基本类型值
var result = o
}
return result;
}
var obj2 = deepClone(obj1);
console.log(obj2);
console.log(obj1.c == obj2.c); // false
obj1.c.push(99);
console.log(obj1);
console.log(obj2); // obj2不变的,因为没有“藕断丝连”的现象
obj1.c[2].p.push(999);
console.log(obj1);
console.log(obj2); // obj2不变的,因为没有“藕断丝连”的现象
</script>
</body>
</html>
2-6、认识上下文
函数的上下文
- 函数中可以使用
this关键字,它表示函数的上下文
- 与中文中“这”类似,函数中的this具体指代什么
必须通过调用函数时的“前言后语”来判断
函数中的this

函数的上下文由调用方式决定
- 同一个函数,用不同的形式调用它,则函数的上下文不同

-
- 函数只有被调用,它的上下文才能被确定

代码案例
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>2-6、认识上下文</title>
</head>
<body>
<script>
var obj = {
a: 1,
b: 2,
fn: function () {
console.log(this.a + this.b);
console.log(this === window);
console.log(this === obj);
}
}
// obj.fn(); //这个时候的this代表obj对象,所以输出的结果会是3
// var fn = obj.fn(); // 如果这个fn加了括号,将是直接调用函数,而不是赋值,变量fn代表的就是undefined
var fn = obj.fn; // 这个时候的fn已经被单独提了出来,此时再去调用时this将代表window对象,而window对象的所有属性都是全局变量
fn() // NaN
// 假设这个时候刚好有全局变量,那么这时的this.a将会调用全局变量a
var a = 10;
var b = 5;
fn(); // 15
</script>
</body>
</html>
2-7、上下文规则1
函数的上下文由调用函数的方式决定
- 函数的上下文(this关键字)由
调用函数的方式决定,function是“运行时上下文”策略
- 函数如果不调用,则不能确定函数的上下文
规则一
- 规则1:
对象打点调用它的方法函数,则函数的上下文是这个打点的对象

规则1题目举例 - 第1小题

规则1题目举例 - 第2小题

规则1题目举例 - 第3小题

规则1题目举例 - 第4小题

代码案例
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>2-7、上下文规则1</title>
</head>
<body>
<script>
// 案例1
function fn () {
console.log(this.a + this.b);
}
var obj = {
a: 1,
b: 2,
fn: fn
};
obj.fn(); // 3
// 案例2
var obj1 = {
a: 1,
b: 2,
fn: function () {
console.log(this.a + this.b);
}
};
var obj2 = {
a: 3,
b: 4,
fn: obj1.fn
};
obj2.fn(); // 7
// 案例3
function outer() {
var a = 11;
var b =22;
return {
a: 33,
b: 44,
fn: function () {
console.log(this.a + this.b);
}
};
}
// 因为当调用完毕outer函数的时候就outer()已经是返回值中的那个对象了,所以此时的this指代的就是这个对象
outer().fn(); // 77
// 案例4
function fun() {
console.log(this.a + this.b);
}
var obj = {
a: 1,
b: 2,
c: [{
a: 3,
b: 4,
c: fun
}]
};
var a = 5;
obj.c[0].c();
</script>
</body>
</html>
2-8、上下文规则2
规则2
- 规则2:
圆括号直接调用函数,则函数的上下文是window对象

规则2题目举例 - 第1小题

规则2题目举例 - 第2小题

代码案例
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>2-8、上下文规则2</title>
</head>
<body>
<script>
// 案例1
var obj1 = {
a: 1,
b: 2,
fn: function() {
console.log(this.a +this.b);
},
};
var a = 3;
var b = 4;
var fn = obj1.fn;
fn(); // 7
// 案例2
function fun () {
return this.a + this.b;
}
var a = 1;
var b = 2;
var obj = {
a: 3,
// 这里是直接调用函数,又因为这个函数中的this指代的是window对象,所以等于两个全局变量相加,等于3
b: fun(),
fun: fun
};
var result = obj.fun();
console.log(result); // 6
</script>
</body>
</html>
2-9、上下文规则3
规则3
- 规则3:
数组(类数组对象)枚举出函数进行调用,上下文是这个数组(类数组对象)

规则3题目举例 - 第1小题

类数组对象
- 什么是类数组对象:所有键名为自然数序列(从0开始),且有length属性的对象
- argument对象是最常见的类数组对象,它是函数的实参列表
规则3题目举例 - 第2小题

代码案例
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>2-9、上下文规则3</title>
</head>
<body>
<script>
// 案例1
var arr = ["A","B","C",function () {
console.log(this[0]);
}];
arr[3](); // "A"
// 案例2
// 这段代码的含义是声明一个函数,函数里面调用传入进来的参数的下标为3的函数
function fun() {
arguments[3]();
}
// 这段代码的含义是调用函数,实参中下标为3的地方声明了一个匿名函数,里面内容是输出this指代的fun对象中的实参下标为一的值
fun("A","B","C",function () {
console.log(this[1]); // "B"
});
</script>
</body>
</html>
2-10、上下文规则4
规则4
- 规则4:
IIFE中的函数,上下文是window对象

规则4题目举例

代码案例
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>2-10、上下文规则4</title>
</head>
<body>
<script>
var a = 1;
var obj = {
a: 2,
// 这里的fun其实是匿名函数里面的返回值函数,因为这个匿名函数是IIFE,会直接调用并不先进入对象
fun: (function () {
// 这里因为是还没进入对象所以此时的this指的是window对象
var a = this.a;
return function () {
console.log(a + this.a); // 1(全局变量) + 2(对象键)
}
})()
};
// 这里调用是用对象点函数调用的,所以返回函数中的this指代的是obj对象
obj.fun(); // 3
</script>
</body>
</html>
2-11、上下文规则5
规则5
- 规则5:
定时器、延时器调用函数,上下文是window对象

规则5题目举例


代码案例
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>2-11、上下文规则5</title>
</head>
<body>
<script>
// 案例1
var obj = {
a: 1,
b: 2,
fun: function () {
console.log(this.a + this.b); // 7
},
};
var a = 3;
var b = 4;
setTimeout(obj.fun,2000) // 适用规则5,此时的this指代的是window对象
// 案例2
var obj = {
a: 1,
b: 2,
fun: function () {
console.log(this.a + this.b); // 3
},
};
var a = 3;
var b = 4;
setTimeout(function() {
obj.fun(); // 适用规则1,而此时的this指代的就变为了obj
},2000);
</script>
</body>
</html>
2-12、上下文规则6
规则6
- 规则6:
事件处理函数的上下文是绑定事件的DOM元素

规则6 - 小案例1
- 请实现效果:点击哪个盒子,哪个盒子就变红,要求使用同一个事件处理函数实现
规则6 - 小案例2
- 请实现效果:点击哪个盒子,哪个盒子在2000毫秒后就变红,要求使用同一个事件处理函数实现
代码案例
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>2-12、上下文规则6</title>
<style>
div {
width: 100px;
height: 100px;
display: inline-block;
border: solid 3px blue;
}
</style>
</head>
<body>
<div id="box1"></div>
<div id="box2"></div>
<div id="box3"></div>
<script>
var box1 = document.getElementById("box1");
var box2 = document.getElementById("box2");
var box3 = document.getElementById("box3");
// 案例1 点击哪个盒子哪个盒子就变红(要求:用同一个事件实现)
// function setColorToRed() {
// if (this.style.backgroundColor == "red") {
// this.style.backgroundColor = "";
// } else {
// this.style.backgroundColor = "red";
// }
// }
// box1.onclick = setColorToRed;
// box2.onclick = setColorToRed;
// box3.onclick = setColorToRed;
// 案例2 点击哪个盒子,哪个盒子就在2秒后变红
function setColorToRed() {
// 复制上下文
var self = this;
setTimeout(function() {
if (self.style.backgroundColor == "red") {
self.style.backgroundColor = "";
} else {
self.style.backgroundColor = "red";
}
},2000)
}
box1.onclick = setColorToRed;
box2.onclick = setColorToRed;
box3.onclick = setColorToRed;
</script>
</body>
</html>
2-13、call和apply
call和apply能指定函数的上下文


call和apply的区别

到底使用call还是apply (apply)

上下文规则总结
