javascript成神之路(4):深入理解this关键字,是的就是this

  1. 云栖社区>
  2. 前端那些事儿>
  3. 博客>
  4. 正文

javascript成神之路(4):深入理解this关键字,是的就是this

cacao111 2018-07-11 14:39:01 浏览1207
展开阅读全文

摘要:如果你真的理解了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日

作者:技术金三胖 

本文来源:开源中国  如需转载请联系原作者

网友评论

登录后评论
0/500
评论
cacao111
+ 关注
所属团队号: 前端那些事儿