说 this 是 js 编程中最重要的变量也不为过了,而在遇到 this 时,也曾经傻傻分不清楚 this 指代的到底是什么, 所以现在对曾踩过 this 的坑做一下总结。
虽然 this 很令人迷惑,但其实总会发现,在浏览器环境中, this 的指代不外乎两种,要么是 window,要么是某个特定的对象(window 之外的对象), 而涉及到具体的场景中 this 的取值,可以归纳为五种。
- var global = 4;
- console.log(this.global);
如上例中,在全局作用域下定义了变量 global, 同样的,当我们调用 this.global 时,会把之前定义的变量打印出来。这是因为,全局作用域中定义的变量相当于为 window 对象添加了属性,而全局作用域下直接调用 this,同样指代了 window 对象,因此能够取到之前定义的 this 值。
对象方法, 也即对象的成员函数,对象方法中的 this 指代的是对象本身。
- var obj = {
- value: 2,
- func: function() {
- console.log(this.value); //2
- }
- }
如上例中 obj 是一个对象, value 和 func 分别为它的成员变量和成员方法, 此时在方法中取到的 this.value 值即为 obj.value 值,this 指代的是 obj 本身。
new 操作符通常和构造函数共同出现,往往用于创建一个新对象,而此时构造函数中的 this 指代的正是所创建的新对象。
- function Car(){
- this.color="red";
- this.size="large";
- this.getColor=function(){
- console.log(this.color)
- }
- }
- var car=new Car();
- console.log(car.getColor())//red
如上例所示,这里可以正确的把 red 打印出来,其中函数 Car 是一个最基本的构造函数,当用 new 操作符来创建一个新的 Color 对象时,构造函数执行过程中首先会新建一个对象,然后将 this 赋值给这个新对象,之后所有对 this 的赋值操作都等同于对新对象赋值了。
在调用普通函数表达式时, 调用 this, 往往指代的是 window 对象。
- var value = 3;
- var func = function() {
- console.log(this.value);
- }
- func();
如上所示,func 是一个普通的指代函数的变量,在该函数中调用 this,指代的是 window 对象。
绑定 this 也是很常见的一种场景,call,apply,bind 方法均用于绑定 this 到指定的对象。
以上我们把 this 的相关场景分成了五种,但即使这样,也常常有令人迷惑的地方,下面来看下常见的两个题目:
- var obj = {
- a: 3;
- getValue: function() {
- return this.a;
- }
- }
- var func = obj.getValue;
- console.log(func());
看到上例,可能会毫不犹豫的说答案当然是 3 了,很简单嘛,obj.getValue, 所属对象是 obj, 里面的 this 指代的当然是 obj 了,返回的 this.a 当然就是 obj.a,是 3 没问题,这时候,看了控制台的打印就会大吃一惊了,答案是 undefined, 这是因为 var func=obj.getValue 这一句赋值语句被忽略了,但这是至关重要的,经过这个赋值语句之后,this 指代的不再是 obj 本身了,而是全局对象 window, 因为 wiondow 中不含有 a 变量,this.a 当然是 undefined 了。
- var obj = {
- a: 4,
- getValue: function() {
- var innerfunc = funciton() {
- return this.a;
- }
- return innerfunc();
- }
- }
- console.log(obj.func());
看到这里,可能又会脱口而出,打印出的值是 4 没错,这次明显就是直接调用的 obj 对象的 func 函数,this 总该指向 obj 了吧,但是当然了,答案依然是 undefined, 不要忽略,getValue 函数中 this 是经过 innerfunc 调用的,而 innerfunc 的调用对象并不是 obj, 所以在执行 innerfunc 函数时,其中的 this 是指代 window 的,a 值是 undefined。
以上的题目二之所以迷惑我们,关键的一点是,有时候不自觉得将 this 和作用域关联起来,因此,不要将 this 和作用域等同,不要自然而然的找 innerfunc 中的 this 时,会去找其包含作用域 getValue 的 this,因为每个函数天生就有 this 变量,所以某个函数本身的 this 与其包含作用域中的 this 无直接关系。
有了以上的五种场景和常见的两个 this 题目坑,或许可以作如下总结,this 的指代值只有两种:
- var a=function(){
- return this.value;
- }
- a();//调用函数表达式的值
- (function(){
- return this.value;
- })();//调用函数表达式
- ~~function(){
- return this.value;
- }();//调用函数表达式;
第一节中提到的全局作用域中的 this 也可以等同于在一个全局函数中调用,this, 是 window. 有了以上两种判断方法就会更简单了,甚至只要区分出一种场景,就能快速判断了,如只要确定 this 所在的函数指定(绑定)了调用对象,this 就相当于该对象,否则就是 window.
上述的两个坑只是稍微列举了一下,实际开发中 this 的坑真的很多,尤其对我这种小白来说,总结发现 this 的这么多坑的原因往往与一个特性有关,就是 this 的后绑定,在看到涉及 this 的函数定义时,往往不自觉的在这里就给 this 赋值了,如上节中的坑一,正因为我看到的 this 所在的函数是属于一个对象的,所以未理会 var func=obj.getValue,变不由得将 this 和 obj 绑定了,但这种明显是错误的,由于 this 的后绑定,我们不应随意在看到 this 时就将其赋值,而应好好分析在调用时 this 的所指才是正解。 那么为什么 this 要后绑定呢,给我们带来了这么多坑,这应该设计到语言的设计问题了,这里无法深入,但毋庸置疑的是,js 的极其重要的原型继承机制就是依赖于 this 后绑定的,想象一下 this 不是后绑定,后果很可怕。。。。
来源: https://juejin.im/post/5a4ec61251882573473d8dbf