-
this 的四种绑定策略( 默认绑定、隐式绑定、显式绑定、new 绑定 )
默认绑定:this 一般默认指向全局作用域 隐式绑定:使用对象调用函数进行绑定( obj.fun() ) * 隐式绑定可能丢失 * 显示绑定:使用 apply call bind 进行强制绑定 new 绑定: 将 this 绑定到对象实例
-
this 绑定的优先级别
this 的三篇文章按照顺序复习 this基本概念 -> this绑定规则 -> this 应用实例
包含 this 的函数 的 调用位置决定了 内部 this 的指向对象, 所以 确定 this 的指向就是寻找包含 this 的函数的调用位置
寻找函数的调用栈和调用位置( 浏览器的开发者工具 )
this 的 四种绑定策略
默认绑定策略
-
this 默认指向全局对象
function foo() { console.log(this.a) } var a = 2; foo(); // 2
** 严格模式 ”use strict“ ( 无论是函数内部严格模式或者是整体文件严格模式 ) 不能将全局对象用于默认绑定,this 将会绑定到 undefined 上 **
隐式绑定( 对象调用的绑定 )隐式绑定可能 丢失
-
隐式绑定会把函数中的 this 绑定到这个 调用的 上下文环境中
function foo() { console.log(this.a); } var baz = { a: 2, foo: foo } baz.foo(); // 2
作为 foo 函数的调用对象 baz 成为 this 的上下文对象
( 这样描述不是很恰当 )
- 隐式绑定的 this 丢失的
两种情况
主动赋值丢失 ( 本质相当于为函数起了 别名 )
函数传参(将函数作为参数
)丢失( 回调函数丢失 ) 函数传参也是一种隐式的赋值操作
-
主动赋值丢失
主动赋值丢失本质是将 函数的引用暴露在全局作用域下 function foo() { console.log(this.a); } var baz = { a: 2, foo: foo } var bar = baz.foo; // 主动赋值 ** 不是主动执行 ** (只是地址的一次 引用) bar(); // 输出 undefined 或者 window
-
函数传参丢失( 将函数作为参数传递 也是一次引擎的右查询 )
** 回调函数本质也是将函数作为参数传递,是一次隐式的赋值操作 **function foo() { console.log(this.a); } var baz = { a: 2, foo: foo } var bar = baz.foo; // 这里其实不是绑定,只是一次 地址的引用( 函数也是一个对象 ) setTimeout(bar, 100); // 函数传参 回调函数 丢失绑定 // 输出 undefined 或者 window
主动赋值操作 是因为进行了 函数地址的引用, 将函数的 上下文环境改变了
** 区别于函数的调用执行 **
显示绑定 和 硬绑定策略
使用call 和 apply 可以强制改变 this 的指向
但是看看下面这种情况
function foo() {
console.log(this.a);
}
var baz = {
a: 2,
foo: foo
}
var jon = {
a: 1,
foo: foo
}
var bar = baz.foo;
bar.call(baz); // 2
bar.call(jon); // 1
这 bar 函数内部的 this 指向 好像更改的很随意呀
我们看看怎样使得其 能从一而终的指向 呢??( 找一个内部没有 this 的函数试试 )
function foo() {
console.log(this.a);
}
var baz = {
a: 2,
foo: foo
}
var jon = {
a: 1,
foo: foo
}
var bar = function() {
foo.call(baz);
};
bar(); // 2
bar.call(baz) // 2
bar.call(jon); // 2
这次函数 foo 内部 的 this指向就从一而终了
** 硬绑定的策略是一旦绑定this, this 的指向不再修改。( 和显示绑定不同 ) **
重点: 我们使用了一个包装函数 负责函数的指向设置
硬绑定策略
-
创建一个包裹函数 负责接收参数并返回值
function something(something) { console.log(this.a, something); return this.a = something; } function bind(fun, obj){ return function (arg) { return fun.call(obj, arg); } } var obj = { a: 2 } var bar = bind(something, obj); bar(3); // 2 3 var b = bar(3); // 3 3 console.log(b); // 3
硬绑定策略就是一旦为函数绑定了内部 this 的指向对象,这个指向对象便不再会被改变( 当然对象中的值是可以更改的
)
-
es5 提供了 内置的硬绑定方法 Function.prototype.bind
function foo(something) { console.log(this.a, something); return this.a + something; } var baa = { a: 2 } var bar = foo.bind(baa); var bb = bar(3); // 2 3 console.log(bb); // 5
API 调用的上下文(
内置函数或者第三方库 中提供 硬绑定 this指向的解决方案
)
许多JavaScript 内置的函数或者第三方库函数能够提供一个绑定上下文环境的 可选参数( 使用权在开发人员手中
),
称其为上下文( context ),作用和 bind 一样,或者其内部就是使用 bind 实现的
方便你的代码实现( 让你少些一些代码 )
// forEach
var arr = [1, 3, 5, 6, 7];
var obj = {
id: "qwe"
}
arr.forEach(function(item){
console.log(item, this.id)
}, obj) // 这里的 obj 就是显示绑定上下文的 对象
news 绑定策略
使用 new 调用函数,或者 函数构造时会发生 this 的绑定行为
详见对象的构造函数一章
四种绑定策略的优先级
-
new 构造函数调用和 bind 硬绑定策略的优先级比较
function foo(something) { this.a = something; } var obj = {} var bar = foo.bind(obj) bar(2); console.log(obj.a); // 2 var baz = new bar(3); console.log(baz.a); // 3 console.log(obj.a); // 2
分析结果: new 绑定策略似乎是和 bind 绑定策略 互相不干扰的? 真的是吗??
-
查阅函数原型链可知
- new 构造函数调用时会产生新的 原型对象保证 this 的指向
- *** new 构造函数的原型链中并没有 硬绑定策略绑定的 this 指向对象***
-
根据 MDN bind 函数的实现方式来看
new 绑定策略和 bind 一同使用时,若硬绑定的函数被new 调用,就会 ** 使用 new 新创建的 this 替换 硬绑定策略的 this **
-
bind 绑定 和 new 绑定策略 共同使用 到底想干什么??
请看下一节 中的 ** 奇怪的绑定对象却有巧妙的用法 **
5 . this 的绑定策略优先级 现在自己猜猜看
.