携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第2天,点击查看活动详情
大家好!我是前端爬楼工程师🚹,一个野生程序员。好奇新技术,仰慕牛大佬。如果喜欢我的文章,可以关注➕点赞,为我注入能量,与我一同成长吧~
前篇文章说明了,js有编译和运行两个步骤, 作用域的创建是在编译时期完成的,this
则是在函数调用时绑定的。
在函数调用的时候,此时创建了活动记录(又名为上下文)。记录里包含了函数在哪调用(调用栈),参数,调用方式等信息,this
就是其中的一个。
那么我们将函数调用的位置作为切入点,判断this是如何绑定的。
判断规则之前需要了解两个东西: 函数调用栈和调用位置;
调用位置:很简单 ,如下代码所示,各个函数调用位置
调用栈:就是为了到达调用位置所调用的所有函数(通过谷歌调试工具就能很清晰观察到调用栈)
// 以谷歌调试工具为例
function baz(){
// 调用栈是 :baz -> anonymous
// baz调用位置是全局作用域
console.log('baz')
bar() // <=调用位置 说明到达bar的执行位置,调用了baz函数 所以的调用栈是 baz -> anonymous
}
function bar(){
console.log('bar')
// 调用栈是 :bar -> baz -> anonymous
// bar的调用位置在baz中
foo() // <=调用位置
}
function foo(){
// 调用栈是 :foo -> bar -> baz -> anonymous
// foo的调用位置在bar中
console.log('foo')
}
// 调用栈:anonymous 说明:这里到baz调用位置没有其他函数,所以是匿名栈
baz()// <=调用位置
this绑定的规则有四种:
默认绑定: 后三种规则都不适用的情况下,使用这个规则。这个规则下的this
指向的是全局对象(严格模式下指向undefined
)。
隐式绑定: 看调用位置是不是有上下文。这个规则下的this
指向上下文对象。(如果丢失了绑定对象,则应用默认绑定规则)
function foo(){
console.log(this.a)
}
var obj = {
a:2,
foo:foo
}
obj.foo() // <= 调用位置 使用了obj来引用函数,符合隐式绑定规则, 打印2
// 另外一种情况
function foo(){
console.log(this.a)
}
var obj = {
a:2,
foo:foo
}
var a = 'global a !'
var bar = obj.foo
bar() // <= 调用位置 此时的bar没有修饰,所以应用默认绑定规则 ,打印 global a !
// 回调函数的情况
function foo(){
console.log(this.a)
}
function doFoo(fn){
fn()
}
var obj = {
a:2,
foo:foo
}
var a = 'global a !'
doFoo(obj.foo) // 这里我们认为obj.foo赋值给了doFoo的参数fn,和上一种情况一样,应用默认绑定规则,,打印 global a !
// 使用内置函数调用
setTimeout(obj.foo, 100) //也是global a !
显示绑定:使用 call
, apply
来解决
function foo(){
console.log(this.a)
}
var obj = {
a:1,
foo:foo
}
foo.call(obj) // 1
foo.call('2')// 如果传入的参数是原始类型,不是obj,这个原始类型会帮你转换成对象形式(也叫装箱),string => new String
-
硬绑定
在隐式绑定中会存在丢失对象的情况,可以做如下改造
function foo(){ console.log(this.a) } var obj = { a:1 foo:foo } var bar = function (){ return foo.call(obj) } bar()
其实es5 提供了bind方法
-
js的api调用的上下文
function foo(){
console.log(this.id)
}
var obj = {id: 'id'};
[1,2,3].forEach(foo, obj) // id id id
new绑定: new在js里,new调用的就是普通函数;this
指向的是函数返回的对象
function foo(a){
this.a = a
}
var bar = new foo(2) //用new 调用构造函数foo 把bar绑定到this上
console.log(bar.a) // 2
了解的这些规则后,我们就可以判断this
了
首先看new ,然后看显式绑定,然后是隐式绑定,最后就是默认绑定了;
例外:
- 被忽略的
this
function foo(){
console.log(this.a)
}
var a = 2
foo.call(null) // 2