总结:
以下的几种绑定都离不开广为流传的一句话: this指向调用者。
- 不论函数定义在哪个作用域, 是作为变量(
var attr)还是对象属性, 函数中的this, 只需要关注调用时是否有调用者, 如果有便指向 调用者 , 没有便指向window, 严格模式下指向undefined; call,apply,bind。他们能显示的改变this的指向。new调用的函数中的this指向当前实例。
注意: 不需要关注函数是在作为什么方式创建, 只需要关注调用时的调用者。
看到这里这篇文章已基本算结束了, 下面是一些详细的内容(选看):
1. 默认绑定
独立函数调用时(没有显式的调用者), this 指向全局对象。
this 为 null 和 undefined 都会指向 window。
严格模式下, 函数无法使用默认绑定, this 绑定至 undefined。
//无论foo是在全局作用域, 函数作用域, 或是作为回调函数, 没有显式的调用者都看做默认绑定
function foo() {
console.log(this);
}
foo();//this 指向 Window
2. 隐式绑定
当函数作为对象的方法, 或 dom 的事件驱动函数时, 隐式绑定规则会把函数调用中的 this 绑定到相关的上下文对象(也就是我们经常说的 this 指向调用者 )。
var obj = {
foo() {
console.log(this);
}
};
obj.foo(); //this 指向 obj
事件作为标签的属性定义:
<p onclick="myClick"></p>, 不会隐式绑定this, 所以一般我们看到在标签上绑定事件时, 都会传递this作为参数:<p onclick="myClick(this)"></p>。
3. 隐式丢失
隐式丢失指函数中的 this 丢失绑定对象, 它会应用第1条的默认绑定规则。以下情况会发生隐式丢失:
- 作为对象的方法被赋值给一个新的函数, 然后调用这个函数时。
var obj = {
foo: function() {
console.log(this);
}
}
var foo = obj.foo;
foo() // window
- 对象的方法, 当作回调时:
var obj = {
foo() {
console.log(this);
}
};
function doFoo(fn) {
fn();
}
doFun(obj.foo); //window
这里就印证了上述所说的, 只需要关注调用时有没有显式的调用者。
4. 显示绑定
- 被
call和apply指定this, 称为显示绑定。 如果call或apply的第一个参数( 指定this的参数 )为基本类型, 则会转为包装类型。
直接使用显示绑定无法解决绑定丢失(隐式丢失)问题。例:
var obj = {};
foo.call(obj);//this 指向 obj
foo();//this 指向 Window
function foo() {
console.log(this);
}
也就是说显式绑定, 不是永久改变函数的this
- 硬绑定: 硬绑定是显示绑定的一种方式, 他可以解决隐式丢失的问题。
典型的应用场景为创建一个包裹函数, 传入所有的参数并返回接收到的所有值:
var obj = {};
function foo(something) {
console.log(this);
}
function bar() {
return foo.call(obj, arguments)
}
bar(); //this 指向 obj
// 因为bar内部调用的函数绑定了this
// 所以无论如何指定bar的this
// 都不会影响内部指定的this
由于硬绑定比较常见, 所以 ES5 提供了 Function.prototype.bind , 通过 bind 绑定的对象等于硬绑定了 this。
var obj = {};
function foo(){
console.log(this);
}
var bar = foo.bind(obj);
bar();//this指向obj
bar.call(this);//显式绑定也无法改变bar的this指向
API中的显示绑定:JS自身以及许多第三方库的函数都提供了一个可选的参数, 用于指定回调函数的this其作用和bind的相似。
var arr = [];
arr.forEach(function() {
// 在函数中直接调用的回调一般都会发生隐式丢失,
// 所以在非严格模式下会指向window
// 若指定了forEach的第二个参数, 会指定回调中this的指向]
// 其本质仍是显式绑定
console.log(this);
}, obj(指定this))
5. new 绑定
this 指向当前实例。
注意: 构造函数中若返回自定义的对象作为实例, 则该实例与构造函数无关:
function P(name) {
this.name = name;
return obj;
}
P.prototype.foo = function() {
console.log('foo...');
};
let obj = {
age: 18
};
let obj1 = new P('zs');
// __proto__指向Object的prototype, 没有name属性, 也没法调用foo方法
obj1.name; // undefined
obj1.foo; //undefined