this详细用法介绍与例题试炼
总结如下图:
注意:
全局上下文的 this
指向非常明确,不管有没有启用严格模式,都指向 window
对象。
apply
和 call
的功能完全一样,只是传参形式不一样,call
是传多个参数,apply
是只传参数集合。
function add(x, y, z) {
return this.x + this.y + this.z;
}
const obj = {
x: 1,
y: 2,
z: 3,
};
console.log(add.call(obj, 1, 2, 3)); // 输出 6
console.log(add.apply(obj, [1, 2, 3])); // 输出 6,只是传参形式不同而已
bind
和 call
、apply
的区别是,函数调用 call
和 apply
会直接调用,而调用 bind
是创建一个新的函数,必须手动去再调用一次,才会生效。
function add(x, y, z) {
return this.x + this.y + this.z;
}
const obj = {
x: 1,
y: 2,
z: 3,
};
const add1 = add.bind(obj, 1, 2, 3); // bind 会返回一个新的函数
console.log(add1()); // 执行新的函数,输出 6
new
调用 > call
、apply
、bind
调用 > 对象上的函数调用 > 普通函数调用
例题试炼
测试题1:
const o1 = {
text: "o1",
fn() {
return this.text;
},
};
const o2 = {
text: "o2",
fn() {
return o1.fn();
},
};
const o3 = {
text: "o3",
fn() {
var fn = o1.fn;
return fn();
},
};
console.log(o1.fn()); // ?
console.log(o2.fn()); // ?
console.log(o3.fn()); // ?
输出:
"o1";
"o1";
undefined;
原因:o1.fn()
中的 this
指向 o1
对象,所以输出的 this.text
为 'o1'
。
执行 o2.fn()
等价于执行 o1.fn()
,所以输出 'o1'
。
o3.fn()
理解起来稍微复杂一点,o3
对象中的代码等价于下面的代码:
const o3 = {
text: "o3",
fn() {
return function () {
return this.text;
};
},
};
console.log(o3.fn()());
所以 this
指向为 window
,window.text
为 undefined
。
测试题2:
const o1 = {
text: "o1",
fn() {
return this.text;
},
};
const o2 = {
text: "o2",
fn: o1.fn,
};
console.log(o1.fn()); // ?
console.log(o2.fn()); // ?
输出:
"o1";
"o2";
原因:o1.fn()
中的 this
指向 o1
对象,所以输出的 this.text
为 'o1'
。
o2
对象中的代码等价于下面的代码:
const o2 = {
text: "o2",
fn() {
return this.text;
},
};
所以 this
指向为 o2
对象,所以输出的 this.text
为 'o2'
。
测试题3:
var userName = "xxx";
const obj1 = {
userName: "zhangsan",
fn() {
setTimeout(function () {
console.log(this.userName); // ?
});
},
};
const obj2 = {
userName: "zhangsan",
fn() {
setTimeout(() => {
console.log(this.userName); // ?
});
},
};
obj1.fn();
obj2.fn();
输出:
"xxx";
"zhangsan";
原因:obj1
中调用的 this
是函数嵌套函数中的 this
,所以指向 window
,输出 window.userName
为 'xxx'
。
obj2
中因为箭头函数的原因,this
指向上层作用域,所以指向对象 obj2
,输出 obj2.userName
为 'zhangsan'
。
测试题4:
var userName = "xxx";
const obj1 = {
userName: "lin",
fn() {
return () => {
console.log(this.userName); // ?
};
},
};
const obj2 = {
userName: "lin",
fn() {
return () => {
return () => {
console.log(this.userName); // ?
};
};
},
};
obj1.fn()();
obj2.fn()()();
输出:
"lin";
"lin";
原因:因为有箭头函数,嵌套多少层,最终都会一层一层地读到第一层,所以 this
指向为对象本身。
测试题5:
function foo() {
return () => {
console.log(this.a);
};
}
const obj1 = {
a: 2,
};
const obj2 = {
a: 3,
};
const bar = foo.call(obj1); // ?
bar.call(obj2); // ?
输出:
2;
原因:foo.call(obj1)
输出 2 是因为 call
显式改变 this
指向。
bar.call(obj2)
啥也不输出,因为箭头函数的绑定无法被修改。
测试题6:
var a = 100;
const foo = () => () => {
console.log(this.a);
};
const obj1 = {
a: 2,
};
const obj2 = {
a: 3,
};
const bar = foo.call(obj1); // ?
bar.call(obj2); // ?
输出:
100;
原因:foo.call(obj1)
输出 2 是因为 call
显式改变 this
指向。
bar.call(obj2)
啥也不输出,因为箭头函数的绑定无法被修改。
记住前面的判断方法,大部分的this指向你就都可以弄懂啦