this是javascript前端编程中十分罕用的一个关键字,也是面试常考题目,然而因为this不是其余面向对象语言的固定指向,而是状况不同的调用代码,会产生不同指向的this。上面咱们就零碎的剖析下咱们在编程时,如何了解每种状况下的this指向。

this的定义

首先,咱们面对一个编程语言问题的时候,特地是关键字或者原生API的时候,第一反馈应该是去查官网文档,因为官网文档才是最正确,最权威,最艰深的解释,这也是我编程这么多年的经验总结。上面是this在MDN Web Docs中的解释

在与其余语言相比,函数的 this 关键字在 JavaScript 中的体现略有不同,此外,在严格模式和非严格模式之间也会有一些差异。

绝大多数状况下,函数的调用形式决定了 this 的值(运行时绑定)。this 不能在执行期间被赋值,并且在每次函数被调用时 this 的值也可能会不同。ES5 引入了 bind 办法来设置函数的 this 值,而不必思考函数如何被调用的。ES2015 引入了箭头函数,箭头函数不提供本身的 this 绑定(this 的值将放弃为闭合词法上下文的值)。

其中最重要的一句是,”函数的调用形式决定了 this 的值(运行时绑定)“,咱们能够了解为在例如person.run()调用时,run办法中的this指向的是person这个对象。

下一章节中依据具体案例具体分析一下。(下列状况均指在非严格模式下的状况剖析)

this的案例剖析

咱们将应用6个案例来剖析this的每种场景下的含意

案例1 函数执行

function run(){    console.log('this->' + this)}run()//this->[object Window]console.log('this->' + this)//this->[object Window]var title = 'window'function doJob(){    let title = 'doJob'    function test(){        console.log('this->' + this.title)//this->window    }    test()}doJob()

第一个是打印执行run时的this对象,因为未指定run的执行对象,默认this指向window。

第二个是在全局环境下执行打印this,此时this也是默认的window对象。

第三个的重点是看test的执行,test是独自执行,没有任何绑定对象,所以test中的this是window对象

案例2 对象创立

function run(){      this.sum = 18    console.log('this->' + this)}new run()//this->[object Object]let person = {name:'123',prun:run};person.prun()//this->[object Object]let arr = [1,2,run]arr[2]() //this->1,2,function run(){...}

留神: new在执行时会做四件事件

  1. 创立一个新的子对象;
  2. 让子对象继承构造函数的原型对象(prototype);
  3. 调用构造函数,将this指向子对象,并给子对象增加对应的属性(例如在run中增加this.sum=18);
  4. 将子对象返回到函数里面。

第一个new run()执行时,能够了解为将new将run变成了一个构造函数,此时this会指向新创建的子对象,打印进去//this[object Object]的后果

第二个person.run()执行时,this指向的是’.‘后面的person对象,所以this指向person。

第三个arr[2]()执行时,this指向arr数组。

案例3 call,apply,bind的利用

function run(){    console.log('this->' + this)}let obj = {name:'456'}run.call(obj)//this->[object Object]run.apply(obj)//this->[object Object]let bindRun = run.bind(obj)bindRun()//this->[object Object]

函数call,apply,bind的成果都是指定this的对象,影响函数执行的作用域。所以上述都是打印的指定的obj对象。

案例4 dom绑定

<button onclick="clickFun()">button</button>...function clickFun(){    console.log('this->' + this)//this->[object Window]}

dom元素绑定事件中,this指向window对象。

案例5 箭头函数

let arrow = ()=>{    console.log('this->' + this) //this->[object Window]}arrow()var title = 'window'function test(){    let title = 'test'    console.log('test this->' + this.title) //test this->p    let arrow1 = ()=>{        console.log('arrow1 this->' + this.title) //arrow1 this->p    }    arrow1()    setTimeout(() => {        console.log('setTimeout this->' + this.title)//setTimeout this->p       });}let p = {title:'p',test}p.test()var title = 'window'let obj = {    title:'obj',    fun:()=>{        console.log('this->' + this.title)//this->window    },      getTitle(){          setTimeout(() => {                console.log('this->' + this.title)   //this->obj            });    }}obj.fun()obj.getTitle()

首先,箭头函数没有this指向,它的this指向上一级的作用域中的this。

第一个执行arrow()函数时,因为箭头函数没有this指向,所以this指向上一级的window;

第二个arrow1与setTimeout中的箭头函数执行时,this追随test的函数的this指向,test执行后方有p对象,所以这外面三个的this都是指向p对象,所以都是打印p

第三个中咱们先剖析obj.fun()执行,因为fun函数是一个箭头函数,this指向上一级的this,上一级指的是全局window,不是obj,因为对象没有作用域。这里是容易混同的一个点。而后上面的setTimeout中this指向getTitle函数的this,getTitle因为有'.'前的obj,所以这里的this指向的是obj

案例6 原型对象函数

function Student(name){    this.name = name}Student.prototype.logName = function(){    console.log(this.name);}let xiaoqiang = new Student('xiaoqiang')let dawang = new Student('dawang')xiaoqiang.logName()//xiaoqiangdawang.logName()//dawang

原型对象办法中,在执行时,this指向调用该办法的子对象,所以xiaoqiang和dawang调用时,会别离打印出各自的名称,也遵循了一个this指向看调用的准则

总结

this的指向不看定义,看调用。

  1. this默认指向window;
  2. 函数调用时,this通常指向调用函数的对象,例如 obj.fun()时,this指向obj;
  3. 构造函数中,this指向新创建的对象,例如new Fun()时,this指向新创建的对象;
  4. 应用call,apply,bind能够自定义this的指向对象
  5. dom事件中的this,指向该文档的window对象
  6. 箭头函数没有独立的this,this指向上一级的this对象;
  7. 原型对象共有办法中的this,这个this指向未来调用此办法的’.‘前子对象
本文参加了SegmentFault 思否写作挑战赛,欢送正在浏览的你也退出。