You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Function.prototype.bind = function (obj) {
if (typeof this !== "function") {
throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
}
var args = Array.prototype.slice.call(arguments, 1), // 截取参数
self = this, // 保存对象上下文
F = function () {},
fBound = function () {
return self.apply(
this instanceof F && obj // 1
? this
: obj || window,
args.concat(Array.prototype.slice.call(arguments)); // 将bind方法的参数和fBound方法的参数合并
);
}
F.prototype = this.prototype;
fBound.prototype = new F(); // 2
return fBound;
};
JavaScript this指向总结
this是在运行时绑定的、而不是在定义时绑定,它的上下文取决于函数调用时的各种条件。this的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。
当一个函数被调用时,会创建一个活动记录(也称执行上下文)。这个记录会包含函数在哪里被调用(调用栈)、函数的调用方法、传入的参数等信息。this就是记录的其中一个属性,会在函数执行的过程中用到。
绑定规则
判断this的指向,我们需要先找到函数的调用位置,然后判断应用下面四条规则中的哪一条:
foo()是直接使用不带任何修饰的函数引用进行调用的,因此只能使用默认绑定规则、this指向全局对象。
注意:如果foo在严格模式下,默认绑定将绑定到undefined、而不是全局对象。(只要foo不在严格模式下,此处this就可以利用默认规则绑定到全局对象;只有foo是严格模式时、此处this绑定到undefined)
调用位置会使用obj上下文来引用函数,因此可以说函数被调用时obj对象“拥有”或者“包含”foo函数(实际上foo不属于obj对象,它只是被当做引用属性添加到obj中)。
当函数引用有上下文对象时,隐式绑定规则会把函数调用中的this绑定到这个上下文对象。因为调用foo()时this被绑定到obj,因此this.a和obj.a是一样的。
对象属性引用链中,只有最后一个调用者(直接调用者)会影响调用位置:
隐式丢失
有时隐式绑定的函数会丢失绑定对象、改用默认绑定:
虽然bar是obj.foo的一个引用,但实际上它引用的是foo函数本身,因此此时的bar()是一个不带任何修饰的函数调用,因此应用了默认绑定规则。
传入回调函数时也会隐式丢失绑定对象:
JavaScript环境中内置的setTimeout()函数实现和下面的代码类似:
所以回调函数(如setTimeout里的回调函数)丢失this绑定是非常常见的。
利用call()和apply()方法:
通过foo.call(obj);,可以在调用foo时强制把它的this绑定到obj上。
如果你传入一个原始值(字符串类型,布尔类型或者数字类型)来当做this的绑定对象,这个原始值会被转换成它的对象形式(也就是new String(),new Boolean()或者new Number()),这通常被称为“装箱”。
但是显示绑定仍无法解决我们之前遇到的丢失绑定的问题,有2个方法可以解决:
bar函数内强制把foo的this绑定到了obj,无论之后如何调用函数bar,它总会在obj上调用foo。这种绑定是一种显式绑定,因此我们称之为硬绑定。
ES5种提供的Function.prototype.bind也是一种硬绑定。
注意:绑定函数作为构造函数时,其this需继承自原函数,并且需要继承原函数的原型链方法,上述代码中的1和2处就是做这两件事。
第三方库的许多函数,以及JavaScript语言和宿主环境中许多新的内置函数,都提供了一个可选的参数,通常被称为”上下文“(context),其作用和bind一样,确保你的回调函数使用指定的this。
这些函数实际上就是通过call或者apply实现了显示绑定。
使用new来调用函数,或者说发生构造函数调用时,会自动执行下面的操作:
绑定规则的优先级
判断函数在某个位置应用的是哪条规则,可以按照下面的顺序来进行判断:
但是,有几个例外情况不适用上述4个规则:
赋值表达式p.foo = o.foo的返回值是目标函数的应用,因此调用位置是foo()而不是p.foo()或o.foo(),所以这里用了默认绑定。
一些封装函数,以实现可以给默认绑定指定一个全局对象和undefined以外的值,那就可以实现和硬绑定相同的效果,同时保留隐式绑定或者显示绑定修改this的能力。
this词法
上述的4条规则适用于普通正常函数,但是ES6给出了一种无法使用这些规则的特殊函数类型:箭头函数。箭头函数的this是根据外层(函数或者全局)作用域来决定的。
箭头函数的绑定无法被修改(new也不行),它本质上是用this的词法形式(根据当前的词法作用域来决定this,具体来说,箭头函数会继承外层函数调用的this绑定,这其实和我们常写的self = this;机制一样)替代this机制(取决于调用位置和条件)。
参考:《你不知道的JavaScript(上)》
The text was updated successfully, but these errors were encountered: