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在执行时会做四件事件
- 创立一个新的子对象;
- 让子对象继承构造函数的原型对象(prototype);
- 调用构造函数,将this指向子对象,并给子对象增加对应的属性(例如在run中增加this.sum=18);
- 将子对象返回到函数里面。
第一个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的指向不看定义,看调用。
- this默认指向window;
- 函数调用时,this通常指向调用函数的对象,例如 obj.fun()时,this指向obj;
- 构造函数中,this指向新创建的对象,例如new Fun()时,this指向新创建的对象;
- 应用call,apply,bind能够自定义this的指向对象
- dom事件中的this,指向该文档的window对象
- 箭头函数没有独立的this,this指向上一级的this对象;
- 原型对象共有办法中的this,这个this指向未来调用此办法的’.‘前子对象
本文参加了SegmentFault 思否写作挑战赛,欢送正在浏览的你也退出。