摘要:如果你真的理解了this是什么,那么你的web几乎所向披靡(文章后面有惊喜,不要错过)
很多程序员会这么认为,this关键字与面向对象程序开发紧密相关,其完全指向由构造器新创建的对象。在ECMAScript规范中也是这样实现的,但正如我们将看到那样,在ECMAScript中,this并不限于只用来指向新创建的对象。还有很多新功能特性
一、定义
this代表当前对象,说明this是在某种特定的情况下才是成立的,它是执行上下文的一个属性。
activeExecutionContext = { AEC: {...}, this: thisVal };
这里AEC是我们讨论的变量对象。this与上下文中可执行代码的类型有直接关系,this值在进入上下文时确定,并且在上下文运行期间永久不变。
二、全局代码中的this
在全局代码中,this始终是全局对象本身,这样就有可能间接的引用到它了。
// 显示定义全局对象的属性 this.ab = 10; // global.ab = 10 alert(ab); // 10 // 通过赋值给一个无标示符隐式 bb = 20; alert(this.bb); // 20 // 也是通过变量声明隐式声明的 // 因为全局上下文的变量对象是全局对象自身 var cc = 30; alert(this.cc); // 30
三、函数代码中的this
在这时候this值的首要特点是它不是静态的绑定到一个函数。this是进入上下文时确定,在一个函数代码中,这个值在每一次完全不同,但是在代码运行时的this值是不变的,也就是说,因为它不是一个变量,就不可能为其分配一个新值。
var F = {y: 10}; var Bar = { y: 20, test: function () { alert(this === Bar); // true alert(this.y); // 20 this = F; // 错误,任何时候不能改变this的值 alert(this.y); // 如果不出错的话,应该是10,而不是20 } }; // 在进入上下文的时候 // this被当成Bar对象 B.test(); // true, 20 F.test = Bar.test; // 不过,这里this依然不会是foo // 尽管调用的是相同的function F.test(); // false, 10
在我们通常的函数调用中,this是由激活上下文代码的调用者来提供的,即调用函数的父上下文(parent context )。this取决于调用函数的方式,正是调用函数的方式影响了调用的上下文中的this值,没有别的什么可以看到,即使是正常的全局函数也会被调用方式的不同形式激活,这些不同的调用方式导致了不同的this值。例如:
function F() { alert(this); } F(); // global alert(F === F.prototype.constructor); // true // 但是同一个function的不同的调用表达式,this是不同的 F.prototype.constructor(); // F.prototype
那么,调用函数的方式如何影响this值?请看下面
四、引用类型
引用类型的值只有两种情况: 1、当我们处理一个标示符时,2、或者一个属性访问器
在一个函数上下文中,this由调用者提供,由调用函数的方式来决定。如果调用括号()的左边是引用类型的值,this将设为引用类型值的base对象(base object),在其他情况下(与引用类型不同的任何其它属性),这个值为null。不过,实际不存在this的值为null的情况,因为当this的值为null的时候,其值会被隐式转换为全局对象,例如:
function F() { return this; } F(); // global
我们看到在调用括号的左边是一个引用类型值(因为F是一个标示符)
var fooReference = { base: global, propertyName: 'foo' };
相应地,this也设置为引用类型的base对象。即全局对象
var F = { B: function () { return this; } }; F.B(); // F
我们再次拥有一个引用类型,其base是F对象,在函数B激活时用作this。
var FB = { base: F, propertyName: 'B' };
五、作为构造器调用的函数中的this
还有一个与this值相关的情况是在函数的上下文中,这是一个构造函数的调用。
unction A() { alert(this); // "a"对象下创建一个新属性 this.x = 10; } var a = new A(); alert(a.x); // 10
new运算符调用“A”函数的内部的[[Construct]] 方法,接着,在对象创建后,调用内部的[[Call]] 方法。 所有相同的函数“A”都将this的值设置为新创建的对象。
六、函数调用中手动设置this
在函数原型中定义的两个方法允许去手动设置函数调用的this值。它们是.apply和.call方法。他们用接受的第一个参数作为this值,this 在调用的作用域中使用。这两个方法的区别很小,对于.apply,第二个参数必须是数组(或者是类似数组的对象,如arguments,反过来,.call能接受任何参数。两个方法必须的参数是第一个——this。
var dog = { say() { console.log(this) } } var cat = {} dog.say.call(cat)
其中this通过call被绑定到了cat上
七、箭头函数
它的this指向与普通函数有很大的不同。箭头函数内部的 this 是词法作用域,由上下文确定。简单说就是箭头函数中的 this 只和定义它时候的作用域的 this 有关,而与在哪里以及如何调用它无关,同时它的 this 指向是不可改变的。
var obj = { x: 10, foo: function() { var fn = () => { return () => { return () => { console.log(this); //Object {x: 10} console.log(this.x); //10 } } } fn()()(); } } obj.foo();
对于箭头函数还需要注意一点,就是它的this确定后不会改变。使用call、apply、bind也无法改变它的this,因此使用它们传入的第一个参数无效。但是后面添加的参数值还是有效的。
原文发布时间:2018年01月10日
作者:技术金三胖
本文来源:开源中国 如需转载请联系原作者