参考
指向规则
不同于作用域的静态特性,this的值是动态的,在代码实际执行时(执行上下文的创建阶段)才能被确定。其指向规则见下文。
默认绑定
// 适用情况:默认情况下
// 指向规则:非严格模式下指向全局对象,严格模式下指向undefined
var a = "a";
function foo() {
console.log(this.a);
}
foo(); // "a"(非严格模式下)
隐式绑定
// 适用情况:调用函数时,有上下文对象
// 指向规则:指向调用链中的最后一层,即函数的直接调用者
var a = "a",
obj1 = { a: "a1", foo },
obj2 = { a: "a2", obj1, foo };
function foo() {
console.log(this.a);
}
obj1.foo(); // 'a1'
obj2.foo(); // 'a2'
obj2.obj1.foo(); // 'a1'
// 在下面的例子中,虽然bar指向obj1.obj2.foo,
// 但在实际调用bar时,未带任何上下文对象,
// 所以会导致隐式丢失,并应用默认绑定。
var bar = obj2.obj1.foo;
bar(); // 'a'(非严格模式下)
显式绑定
// 适用情况:通过call、bind、bind手动改变了this指向
// 指向规则:指向call、apply、bind的第一个参数
var a = "global_a",
b = "global_b",
obj = {
a: "obj_a",
b: "obj_b",
};
function foo(a, b) {
console.log(this.a, this.b, a, b);
}
foo(a, b); // undefined, undefined, global_a, global_b
foo.call(obj, a, b); // obj_a, obj_b, global_a, global_b
foo.apply(obj, [a, b]); // obj_a, obj_b, global_a, global_b
const fooBind = foo.bind(obj);
fooBind(a, b); // obj_a, obj_b, global_a, global_b
new 绑定
// 适用情况:使用new和构造函数创建实例时
// 指向规则:构造函数内的this指向被创建实例
function Foo(a) {
this.a = a;
}
var obj = new Foo("a"),
console.log(obj.a); // 'a'
箭头函数
// 适用场合:使用箭头函数时
// 指向规则:指向箭头函数外层的this
// 注意:箭头函数没有自己的this,所以this指向不能被call、apply、bind等改变
var a = "a",
obj = {
a: "obj",
foo: function () {
console.log(this.a);
(() => {
console.log(this.a);
})();
},
bar: () => {
console.log(this.a);
},
};
obj.foo(); // 'obj', 'obj'
obj.bar(); // 'a'(非严格模式下)
改变指向
方法和原理
通过Function.prototype.call/apply/bind可以显式改变this指向,其区别见这里。其实现原理在于,使目标对象成为调用函数时的上下文对象,使this被隐式绑定到目标对象上。
// 以Array.prototype.forEach为例,
// 因为调用forEach时,上下文对象为arr,所以this指向arr。
const arr = ["h", "e", "l", "l", "o"];
arr.forEach();
// 将Array的forEach方法,给到String.prototype上。
// 如此一来,String也有自己的forEach方法了。
String.prototype.forEach = Array.prototype.forEach
// 此时,调用forEach的上下文对象为str,所以this指向str。
const str = String("hello"),
str.forEach()
实现 call
// @params { Any } obj: this绑定到的目标对象
// @params { Any } ...args: 参数
Function.prototype.myCall = function (obj, ...args) {
// 如果无上下文对象,则应用默认绑定
obj = obj || window;
// 如果obj为基本类型,先将其包装成对象
obj = Object(obj);
// 避免func与obj原有属性发生命名冲突
const func = Symbol();
// 将this,即function给到obj上
obj[func] = this;
// 调用funcion,此时的上下文对象是obj,this也绑定在obj上
const result = obj[func](...args);
// 用完记得删除[func]这一属性
delete obj[func];
// 返回结果
return result;
};
const str = "hello";
Array.prototype.forEach.myCall(str, function (val) {
console.log(val);
});
实现 apply
// @params { Any } obj: this绑定到的目标对象
// @params { Array } args: 参数
Function.prototype.myApply = function (obj, args) {
obj = obj || window;
obj = Object(obj);
const func = Symbol();
obj[func] = this;
const result = obj[func](...args);
delete obj[func];
return result;
};
const str = "hello";
Array.prototype.forEach.myApply(str, [
function (val) {
console.log(val);
},
]);
实现 bind
// @params { Any } obj: this绑定到的目标对象
// @params { Any } ...args: 参数
Function.prototype.myBind = function (obj, ...args) {
obj = obj || window;
obj = Object(obj);
const func = Symbol();
obj[func] = this;
// 支持函数柯里化
return function (...newArgs) {
obj[func](...args, ...newArgs);
};
};
const str = "hello";
const strForEach = Array.prototype.forEach.myBind(str);
strForEach(function (val) {
console.log(val);
});