JS中并不神秘的this

295 阅读2分钟

说在前面的话:this的作用机制和lexical scope完全没有关系。

lexical scope(词法作用域)指的是查找变量的规则。 lexical scope与函数声明在代码中的位置有关,与函数如何被调用无关。

复习一下lexical scope:举起第一颗小栗子🌰

function foo(){
    let a=1;
    return function (){
        console.log(a);
    }
}
var a=0;
let bar=foo();
bar();// 1

barrefer的函数是在foo中定义的。可以看到变量访问与lexical scope有关,与函数的call-site(被调用位置)没有关系。

this与call-site有关

与call-site有关的意识是:我们判断函数内部的this取值,要找到调用这个函数的语句,函数如何被调用决定了函数内部的this。

下面4条rule在大多数情况下决定了this的取值:

  • default binding,这也是出现频率最高的情况;
  • implict binding;
  • explict binding;
  • new(嘻嘻终于讲到我了!)

1. default binding

function foo(){
    console.log(this);
}
function bar(){
    'use strict'
    console.log(this);
}
foo(); // window or global object
bar(); // undefined

tip: strict mode是function-scope based; 一个函数是否在strict mode下编译运行由它自身定义的时候有关;与call-site是否是strict mode没有关系。

2. implicit binding

function foo(){
    console.log(this.a);
}
var obj={
    a: 1,
    foo: foo
}
obj.foo(); //1

这个规则应用的场景往往是某个对象obj内部引用某个函数,然后用obj.fn()的方式调用这个函数,此时函数内部的this指向obj

3. explict binding

这个规则的应用场景与Function.prototype.call()Function.prototype.apply()Function.prototype.bind()有关。

4. new

在js中,并没有构造函数的概念。我们以new的方式去调用一个函数,都可以返回一个新创建的对象。这一点需要与java等语言区分开来。

lexical this in arrow function

function foo(){
    setTimeout(()=>{
        console.log(this.a);
    },1000);
}
var obj={
    a: 2019,
    foo: foo
}
obj.foo(); //2019

实际上,这等价于:

function foo(){
    var self=this;
    setTimeout(()=>{
        console.log(self.a);
    },1000);
}
var obj={
    a: 2019,
    foo: foo
}
obj.foo(); //2019

箭头函数内部的this继承自父函数的this,实际上这就很像是lexical查找机制。