this全面解析

290 阅读3分钟

1、this为何而生?

函数可复用、代码简洁易维护

例:function speak(){
         console.log("hello "+this.name)
    }
    let p1 = {
         name:"Mike"
    }
    let p2 ={
         name:"David"
     }
    speak.call(p1) // hello Mike
    speak.call(p2) // hello David

 解释:正如例子所示,speak函数内部的name变量并不会写死,而是利用this来绑定,根据不同的对象,
 调用对应的name值。如果不采用this,而是采用给speak函数传递name参数,当参数多且复杂的情况下,
 代码会变得乱糟糟。
 

2、对于this的理解误区:

2-1、this指向函数本身 (x)

2-2、this指向函数作用域 (x)

例:functon doSomething(){
    this.count ++
}
解释:doSomething.count和this.count并不是相等,this指向函数被调用的上下文对象。

例:function father(){
        var age = 50;
        this.son();
    }
    function son(){
        console.log(this.age)
    }
    father() // 结果报错not defined,this并没有指向father的作用域

解释:就是son函数的this并不指向father函数作用域对象,因此不能获取father内的age属性。

3、正确概念

正确的解释是:this是在运行时绑定,和函数的申明位置没有关系,而取决于函数的调用方式。

4、this绑定规则:

既然,this的指向不明,那么就有一套规则去判断,当函数被调用时this应该指向哪个对象。this绑定规则按照优先级的高低,从低到高依次为:

默认绑定 < 隐式绑定 < 显示绑定 < new绑定

4-1、默认绑定:函数独立调用

例:上述的doSomething函数,直接调用doSomething(),由于没有为该函数指定明确的调用对象,默认
会寻找全局对象(非严格模式下),或者结果为undefined(严格模式下)

4-2、隐式绑定:为函数找个宿主(即找个对象),将函数包裹在内。 此时,this指向宿主对象。

例:var obj = {
    age:18,
    son:son
}
调用方法:obj.son()  //18

不足之处:隐式绑定并不能严格保证this指向宿主对象。

例:let son = obj.son
    son() //找到全局对象的age属性,或者undefined
    因为let son = obj.son将函数从obj宿主对象中取出,然后在全局环境中调用,由于son函数调用时
    并没有宿主对象包裹,隐式绑定规则失效,采用了默认绑定规则。真好印证了this是在运行时绑定的,
    没记住,请再次默念三遍。

4-3:显示绑定:javascript函数自带的call和apply方法,可以将this绑定到指定的对象上。

例:let obj = {age:18}
    son.apply(obj,arguments)或者son.call(obj,argument)
    由显示绑定衍生出更安全的this绑定(硬绑定)
    利用函数实现上述显示绑定
    例:
    function bind(){
       son.apply(obj,arguments)
    }
    bind() //18
    
解释:es5内置的Function.proptotype.bind方法正是利用了硬绑定的原理。(后续有空可以写一篇
关于bind的实现细节)

4-4:new绑定:通过new关键字被调用的函数。

例:let son = new Son(20);
    son.age //20
    
    通过new创建Son构造函数的一个新对象son, 这个new过程中,实现了this指向新对象son。
    下面我们看一下new的过程,到底this是怎么被绑定的。
    
    1、创建新的对象
    2、新对象执行[[prototype]]链接(继承构造函数的属性和方法)
    3、this绑定到新对象上面
    4、如果构造函数没有返回其他对象,则直接返回新对象
    

5、特殊情况:

es6中的箭头函数并不遵循以上四条规则。箭头函数的this是根据当前的词法作用域来决定。具体说,箭头函数绑定为申明时的作用域对象上。

续: this的知识体系非常强大,后续将带来更多的分享,谢谢你的支持哦~