再也不用担心this指向的问题了

239 阅读4分钟

某日,跟公司大牛交流学习进步的方法,大牛说:要善于总结,技术总结很重要。于是自己想动手写点东西,总结一下自己近期学到的一些技术点,踩到的坑,也算是对自己一个技术复盘与总结。(作者表达能力一般,可能有表达错误或不准确的地方,欢迎纠正与交流。第一次写,有点小激动0.0)。

在工作中或面试过程中,经常会碰到有关this指向的问题,似曾相识的场景,却总是被混淆分不清,中了面试官的套路。这段时间作者研究了下,简单总结一波

1,默认规则。举个栗子,代码如下:

var name = '小明';
function print() {
    console.log(this.name);  // '小明'
    console.log(this);   //window对象
}
print();  

这里我们看到打印出来的是小明,this.name被解析成了全局对象,this指向的是window对象。why?为什么this会指向window呢,因为在本例中,函数调用时应用了this的默认绑定原则,因此this指向的是全局对象。那我们怎么判断当前应用了默认绑定呢,我们可以通过函数的调用位置来判断,我们发现print函数是直接调用的,不经过任何修饰。因此只能使用默认绑定,无法使用其他原则

2,隐式绑定

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

var obj = {
    a:2,
    foo:foo
}
obj.foo()  // 2

这里我们看到foo函数的调用者是obj对象,此时this绑定到了obj上,所以this.a输出的是obj.a。这种情况称之为隐式绑定:当函数引用有上下文对象时,隐式绑定规则会把函数调用中的this绑定到这个上下文对象。好,这个时候我们应该对隐式绑定有个大概的了解,那当函数有多个引用的时候会怎么样呢?让我们来看一道题:

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

var obj1 = {
    a:2,
    obj:obj
}
obj1.obj.foo()  // 1

我们看到这种情况下this指向的是最后一层的对象上,即obj上。隐式绑定一定都是这样的吗,答案式否定的,这里面有一个坑:有些时候会发生隐式绑定丢失的情况。让我们来举例说明:

var a = 'hello world';

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

var obj = {
    a:1,
    foo:foo
}

var print = obj.foo;
print();    // hello world

这里我们发现隐式绑定规则失效了,this.a输出的不是1,而是全局对象上的a。这又是怎么回事呢?其实print,obj.foo都是函数的引用。直接调用print函数式,相当于直接调用foo函数,是不是很熟悉呢?没错,是默认绑定,所以此时this指向的是window。所以说我们在判断是否是隐式绑定的时候需要仔细些,分清楚情况。

3,显示绑定

何为显示绑定?利用call(),apply(),bind()强制绑定this指向的我们称之为显示绑定。举个栗子:

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

var obj = {
    a:1
}

foo.call(obj);  // 1

这里我们看到通过call方法将this强制绑定到了对象obj上。利用bind()函数绑定this的做法我们又称之为硬绑定,硬绑定之后当前函数的this指向无法再修改,举个栗子:

var a = 'hello world';

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

var obj = {
    a:1
}

var print = foo.bind(obj);
print();    // 1

// 利用call方法强制将this绑定在window上,但是失败了,this还是指向obj
print.call(window);   // 1

call(),apply(),bind()这三个函数有什么区别,这里就不一一阐述了,有兴趣的同学可以去了解下。

4,new绑定

这个相信大家平时编程时会遇到过很多次,这里简单说明下,如下:

function Person(name) {
    this.name = name;
}
var p = new Person('小明');
console.log(p.name);    // 小明

这种情况我们称之为new绑定。

this绑定的四种基本规则我们基本弄清楚了,那么它们的优先级是什么样的呢?这里我们直接给出结论,不在一一举例对比说明了,有兴趣的同学可以自己举例对比下。优先级:new绑定 > 显示绑定 > 隐式绑定 > 默认绑定。我们在判断this指向的时候可以按照这个优先级顺序来判断,绝大部分情况都可以适用。

参考文献:《你不知道的JavaScript》,很nice的一本书!