我们都会知道的Javascript:this的绑定规则

JavaScript中this绑定规则解析
本文节选总结自《你不知道的JavaScript》中this和对象原型一章,介绍了JavaScript里this的几种绑定规则,包括默认绑定、隐式绑定、隐式丢失、显式绑定、new绑定等,还说明了各规则优先级,指出找到函数调用对象并分析其上下文可解决this相关问题。

前言

近期在翻《你不知道的Javacript》,读来大有裨益,记忆短促,只有一边记录,一边体会,才能融会贯通。同时,也向大家分享这些你知道或不知道的知识点,希望“你不知道的Javascript”,会成为我们都知道的Javascript。

简介

本片内容节选和总结自书籍第二部分,this和对象原型一章。

默认绑定(this指向window)

function foo() {
    console.log(this.a);
}

var a = 2;
foo(); // 2
复制代码

分析:先根据词法静态分析,foo所在作用域是全局作用域,声明foo函数会向window对象添加一个foo的属性。调用foo方法相当于调用window.foo方法,所以此时foo中的this指向window对象,所以this.a的值即为外部变量a的值。

function foo() {
    'use strict';
    console.log(this.a);
}

var a = 2;
foo(); // TypeError
复制代码

But,如果在严格模式下运行代码,this并不会默认绑定到全局对象上。

隐式绑定(绑定至调用对象)

funtion foo() {
    console.log(this.a);
}

var obj = {
    a: 2,
    foo: foo
};
obj.foo(); //2
复制代码

分析:对象obj的foo属性引用了foo函数,并且调用foo函数,是通过obj.foo来访问的,因此foo中的this指向了调用它的对象obj。可以想象一下,在另一门xxxSript语言中,它所有的语法和语义和Javascript完全一致,唯独它的全局对象名称叫“obj”。那么你应该发现了,隐式绑定其实和上面的默认绑定是一样的,只不过默认绑定有了window的默认语义。

function foo() { 
 console.log( this.a ); 
} 

var obj2 = { 
 a: 42, 
 foo: foo 
}; 
var obj1 = { 
 a: 2, 
 obj2: obj2 
}; 
obj1.obj2.foo(); // 42
复制代码

由码可见,在通过一系列对象引用调用函数时,函数中this仅仅绑定到它的直接调用对象上,简单的讲,谁牵着foo的小手,谁就是foo的小主子~。

隐式丢失(多级引用的迷惑)

function foo() { 
  console.log( this.a )
}; 

var obj = { 
  a: 2, 
  foo: foo 
}; 
var bar = obj.foo; 
var a = "oops, global"; 
bar(); // "oops, global"
复制代码

不看答案,你有没有觉得这块代码会输出2?先来看bar是如何被调用的。首先obj对象的foo属性引用了foo函数,然后又将这个引用赋予了bar变量,最终我们调用了bar方法。我们知道js中对象和函数的赋值都是以引用的方式进行的,那么这里无论是obj.foo还是bar,都仅仅只是对foo函数的一个引用,在内存中的模样,仅仅只是记录了foo函数的内存地址。foo函数定义在全局环境中,显而易见,此处调用bar方法,也就是调用了window.foo函数。切记,不要被函数引用迷惑了双眼~

显式绑定

function foo() { 
 console.log( this.a ); 
} 

var obj = { 
 a:2 
}; 
foo.call( obj ); // 2
复制代码

Javascript中提供了两个方法可以进行上下文(this)的显式绑定,call和apply这两个大名鼎鼎的方法想必大家都使用过或有所耳闻。在上述代码中,通过call方法调用了foo函数,并传入了obj对象作为foo函数的上下文,那么foo函数中的this也就指向了这个上下文环境的宿主,即obj对象。否则这里就要输出undefined了~

  1. 硬绑定
function foo() { 
  console.log( this.a ); 
}

var obj = { 
  a:2 
}; 
var bar = function() { 
  foo.call( obj ); 
}; 
bar(); // 2 
setTimeout( bar, 100 ); // // 硬绑定的 bar 不可能再修改
bar.call( window ); //
复制代码

通过call或apply方法强制绑定上下文,并通过函数二次调用,便可以避免this的默认绑定和绑定丢失。foo的this指针被绑定到obj对象中,并通过匿名函数来调用,这样无论如何都不会丢失obj的上下文环境了。

new绑定

function foo(a) { 
 this.a = a; 
} 

var bar = new foo(2); 
console.log( bar.a ); // 2
复制代码

通过构造函数创建的对象,构造函数的this会自动指向这个生成对象。

优先级

以上的几种this绑定规则的优先级,可以通过这些方法来判断:

  1. 函数是否通过 new 调用(new 绑定)?如果是的话 this 绑定的是新创建的对象。 var bar = new foo()
  2. 函数是否通过 call、apply(显式绑定)或者硬绑定调用?如果是的话,this 绑定的是 指定的对象。 var bar = foo.call(obj2)
  3. 函数是否在某个上下文对象中调用(隐式绑定)?如果是的话,this 绑定的是那个上 下文对象。 var bar = obj1.foo()
  4. 如果都不是的话,使用默认绑定。如果在严格模式下,就绑定到 undefined,否则绑定 到全局对象。 var bar = foo()

总结

this实际是函数执行时,上下文环境的指针变量。通常情况下,找到函数的调用对象,再分析这个对象的上下文环境,便可以解决与this相关的问题。

转载于:https://juejin.im/post/5c8fc3cee51d45074c6f9973

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值