乐趣区

Javascript中this基础应用

前言

以下内容只针对非严格模式, 严格模式区不说了
如果看过 《JavaScript 高级程序设计(第 3 版)》 的人都应该有印象,里面关于 this 对象是这么形容的

this 对象是在运行时基于函数的执行环境绑定的:在全局函数中,this 等于 window,而当函数被作为某个对象的方法调用时,this 等于那个对象。不过,匿名函数的执行环境具有全局性,因此其 this 对象通常指向 window。

demo1

示例如下

var a = 10,
  obj = {a: 20};

function log() {
  var a = 30;
  console.log(this.a); // 依然指向全局
};

console.log(this.a); // 全局
log(); // 方法
log.call(obj); // 指向 obj

;
(function () {console.log(this.a) // 匿名函数
})()

// 10
// 10
// 20
// 10

目前为止,一切都符合预期表现。然后我们再看些复杂点的写法。

demo2

var a = 10,
  obj = {
    a: 20,
    log: function () {console.log(this.a); // 指向 obj
    }
  };

function log() {function _log() {console.log(this.a); // 指向全局
  };
  _log();};

obj.log(); // 调用对象方法
log(); // 调用方法内部方法

// 20
// 10

虽然有点绕,但是还是可以明白的,this 指向调用方法该对象,匿名函数 this 指向全局
看到这里我们好像已经大概明白 this 是怎么一回事了,然后我们再写复杂点

demo3

var a = 10,
  obj = {
    a: 20,
    b: this.a + 30
  };
console.log(obj.b); // 40

是不是又猛的怀疑人生了?明明应该 this 是指向 obj 得出 50,为什么会指向全局得出 40 的!?
想想上面说的当函数被作为某个对象的方法调用时,this 等于那个对象。难道属性值又是另一回事?
带着疑问我们直接打印出 this 看看

var a = 10,
  obj = {
    a: 20,
    b: this
  };
console.log(obj.b === window); //true

所以属性值里的 this 真的指向全局,于是我们可以假设得出一个结论:当函数被作为某个对象的方法调用时,this 等于那个对象,而某个对象的属性值的 this 是直接指向全局的
听起来好拗口,是不是意思对象的方法 this 指向对象,属性值指向全局!?

demo4

带着疑问我们直接看例子

var a = 10,
  obj = {
    a: 20,
    log: function () {console.log(this.a);
    }
  };
var fn = obj.log;
fn(); // 调用赋值方法 10

正如前面所说,当函数被作为某个对象的方法调用时,this 等于那个对象。如果不是该对象直接调用时,this 指向调用对象。
基于这个结论, 即使中间经过几层函数调用也一样的

var a = 10,
  obj = {
    a: 20,
    log: function () {console.log(this.a); // 指向 obj
    }
  };

function all_log(fn) {fn();
}

all_log(obj.log); // 10

看到了吧, 即使经过一个中间函数调用, 实际相当于 window 调用对象方法, 所以 this 也是指向全局的.

demo5

然后看看经常会用到的函数 return 出的 this 指向

var a = 10,
  obj = {
    a: 20,
    log: function () {return function () {console.log(this.a); // 指向??
      }
    }
  };
obj.log()(); //10

衹要基础稍好也能理解,因为 obj.log()返回一个匿名函数,然后相当于在全局下再调用匿名函数,因此 this 直接指向全局了。书里官方点的解释就是

每个函数在被调用时都会自动取得两个特殊变量:thisarguments。内部函数在搜索这两个变量时,只会搜索到其活动对象为止,因此永远不可能直接访问外部函数中的这两个变量(例子是指 obj)

需要注意的是 如果没有明确指向全局的话 this 实际上都是指向 undefined 的,衹是非严格模式下,它会被自动指向全局对象。

function log() {
  'use strict';
  console.log(this === window);
}

log(); // 独立调用 false
window.log(); // window 下调用 true

demo6

书里还提到一个挺有意思的写法

var a = 10,
  obj = {
    a: 20,
    log: function () {console.log(this.a); // 指向??
    }
  };
(obj.log = obj.log)(); // 先赋值再调用 10

你会发现居然 this 也指向全局了。
因为代码先执行了一条赋值语句,然后再调用赋值后的结果。因为这个赋值表达式的值是函数本身,所以 this 的值不能得到维持,结果就返回了 10。

demo7

如果是作为 构造函数 使用的情况下

function Person() {
  this.name = 'mike';
  this.log = function () {console.log(this);
  }
};

var man = new Person();
man.log();//Person {name: "mike", log: function}

里面的 this 是指向 new 出来的对象, 详情可以看看我之前写的博文关于 Javascript 中的 new 运算符, 构造函数与原型链一些理解

demo8

还有种情况是通过 callapply修改 this 指向, 它会强制指向方法的第一个参数

var a = 10,
  obj = {a: 20};

function log() {console.log(this.a);
};

log.call(obj); // 指向 obj
log.apply(obj); // 指向 obj

基于上面的情况, 我们可以得出几个结论

1, this 对象是在运行时基于函数的执行环境绑定的
2, 如果没有明确指向全局的话 this 实际上都是指向 undefined 的,衹是非严格模式下,它会被自动指向全局对象
3, 当函数被作为某个对象的方法调用时,this 等于那个对象
4, 当某个对象的属性值里的 this 是直接指向全局对象
5, 匿名函数的执行环境具有全局性,因此其 this 对象通常指向全局对象
6, 构造函数中的 this 会指向它实例化对象
7, call 或 apply 方法能强制修改 this 指向方法指定对象

退出移动版