首发个人博客

JavaScript 中的 this ,大家都用过。但是它到底指向哪里呢?今天在阅读 《你不知道的JavaScript (上卷)》再结合自己平时看的博客,对它又有了新的认识,在此来做个小结,再碰到 this ,就再也不用担心不知道它指向哪里了。

与调用位置有关,而与定义位置无关

以下示例(在浏览器端运行)

function sayHi(){
    var hi = 1;
    console.log(this.hi);
}

var hi = 2;

var obj = {
    hi : 3,
    sayHi : sayHi
}

// output
sayHi(); // 2
obj.sayHi(); // 3

从上述代码的执行结果我们可以看出,直接调用 sayHi() 函数,它输出的 this.hi 不指向函数体内自己定义的 hi 变量,也就是说,this 的指向与词法作用域无关,这也是刚接触 JavaScript的同学常犯的一个错误(包括我自己),认为 this.xx 就指向函数体里面定义的变量 xxsayHi函数的只定义了一次,但obj.sayHi()sayHi() 的输出结果不一样,也恰恰证明了 函数体内this的指向与函数定义的位置无关,而与函数被调用的位置有关,至于为什么输出结果不一样,在下文会讲到。

this 的绑定规则

默认绑定

默认绑定比较常见,表现形式就是在全局作用域中独立调用,如上文中的 sayHi(),这种直接调用方式函数体内的 this 就应用了默认绑定规则,默认绑定有以下两种情况。

  1. 在严格模式下(指在函数声明的过程中,处在严格模式,而不是函数调用处于严格模式),函数体内 this 绑定到 undefined
  2. 在非严格模式下,函数体内 this 指向全局对象 window
/***** 以下例子为处在严格模式下 *****/
function fn1(){
    'use strict'
    console.log(this);
}

fn1(); // undefined

/***** 以下例子处于非严格模式下被调用 *****/
function fn2(){
    console.log(this);
}

fn2(); // window

function fn3(){
    console.log(this);
}

/*
 * 这也是在非严格模式下被调用
 * 因为在函数定义时没有用严格模式
 */
'use strict'
fn3(); // window

隐式绑定

隐式绑定的常见形式为 obj.fn(),若obj对象中有 fn 这个方法(fn可以在别处定义,但必须被添加到obj中作为obj的一个属性方法),那么 fn 中的 this 就指向 obj 对象,如最开始代码中 obj.sayHi() 输出3,就是因为sayHi中的this隐式绑定到obj对象。(个人觉得默认绑定中绑定到window对象时也可以归类为隐式绑定,因为在全局对象中,非严格模式且不考虑ES6的话,所有的全局变量都自动成为window的属性)。来看个具有迷惑性的例子(出自于原书)

function foo() { 
 console.log( this.a );
}
var obj = { 
 a: 2,
 foo: foo 
};
var bar = obj.foo; // 函数别名!
var a = "oops, global"; // a 是全局对象的属性
bar(); // "oops, global"

这是个具有迷惑性的例子,obj 对象有函数 foo 这个方法,后面又在全局作用域中,bar 引用了这个方法,最后再调用 bar。我们只需要关注函数最后被调用的位置,它是在全局作用域中被单独调用的,所以还是为默认绑定,指向 window

显式绑定

显示绑定就比较简单了,用 call,apply,bind方法,都会绑定函数中的this到传入的参数对象中

new 绑定

所谓new绑定就是在用构造函数new一个对象的时候,其中的this指向生成的对象。这就完了嘛?还没有哦。

箭头函数,词法作用域中的this

简单的说,箭头函数中的this,会绑定到函数外(也就是上一层作用域中的this),函数外的this指向哪,箭头函数中的this就指向哪。(代码出自于原书)

function foo() {
    // 返回一个箭头函数
    return (a) => {
        //this 继承自 foo()
        console.log( this.a ); 
    };
}

var obj1 = { 
    a:2
};

var obj2 = { 
    a:3
};

var bar = foo.call( obj1 );
/*
 * foo先绑定this到obj1对象上,所以foo内的this指向obj1,
 * 返回的箭头函数根据词法作用域规则,继承了外部foo的this,
 * 所以箭头函数中的this指向obj1
 */


bar.call( obj2 ); // 2, 不是 3 !
/*
 * 箭头函数中this一但绑定,不可更改
 * 此时还是指向obj1
 * 所以输出的是 obj1.a => 2
 */

绑定的优先级

new > 显示 > 隐式 > 默认

上述知识来自《你不知道的JavaScript(上卷)》


晴天
31 声望1 粉丝