你不懂js系列学习笔记-this与对象原型- 01

第一章: this

原文:You-Dont-Know-JSjavascript

JavaScript 中最使人困惑的机制之一就是 this 关键字。它是一个在每一个函数做用域中自动定义的特殊标识符关键字,但即使是一些老练的 JavaScript 开发者也对它到底指向什么感到困扰。java

1 为何要用 this

让咱们试着展现一下 this 的动机和用途:git

function identify() {
  return this.name.toUpperCase();
}

function speak() {
  var greeting = "Hello, I'm " + identify.call(this);
  console.log(greeting);
}

var me = {
  name: "Kyle"
};

var you = {
  name: "Reader"
};

identify.call(me); // KYLE
identify.call(you); // READER

speak.call(me); // Hello, I'm KYLE
speak.call(you); // Hello, I'm READER
复制代码

这个代码片断容许 identify()speak() 函数对多个 环境 对象(meyou)进行复用,而不是针对每一个对象定义函数的分离版本。github

与使用 this 相反地,你能够明确地将环境对象传递给 identify()speak()ide

function identify(context) {
  return context.name.toUpperCase();
}

function speak(context) {
  var greeting = "Hello, I'm " + identify(context);
  console.log(greeting);
}

identify(you); // READER
speak(me); // Hello, I'm KYLE
复制代码

然而,this 机制提供了更优雅的方式来隐含地“传递”一个对象引用,致使更加干净的API设计和更容易的复用。函数

你的使用模式越复杂,你就会越清晰地看到:将执行环境做为一个明确参数传递,一般比传递 this 执行环境要乱。当咱们探索对象和原型时,你将会看到一组能够自动引用恰当执行环境对象的函数是多么有用。ui

2 困惑

在开发者们用太过于字面的方式考虑 this 这个名字时就会产生困惑。这一般会产生两种臆测,但都是不对的。this

2.1 它本身

第一种常见的倾向是认为 this 指向函数本身。至少,这是一种语法上的合理推测。spa

为何你想要在函数内部引用它本身?最多见的理由是递归(在函数内部调用它本身)这样的情形,或者是一个在第一次被调用时会解除本身绑定的事件处理器。设计

考虑下面的代码,咱们试图追踪函数(foo)被调用了多少次:

function foo(num) {
  console.log("foo: " + num);

  // 追踪 `foo` 被调用了多少次
  this.count++;
}

foo.count = 0;

var i;

for (i = 0; i < 10; i++) {
  if (i > 5) {
    foo(i);
  }
}
// foo: 6
// foo: 7
// foo: 8
// foo: 9

// `foo` 被调用了多少次?
console.log(foo.count); // 0 

复制代码

当代码执行 foo.count = 0 时,它确实向函数对象 foo 添加了一个 count 属性。可是对于函数内部的 this.count 引用,this 其实 根本就不 指向那个函数对象,即使属性名称同样,但根对象也不一样,于是产生了混淆。实际上,若是他再挖的深一些,他会发现本身不当心建立了一个全局变量 count(第二章解释了这是 如何 发生的!),并且它当前的值是 NaN。(全局变量 count 初始值为undefined,++以后为 NaNNaN++以后仍是 NaN )

对于当前咱们的例子来讲,另外一个 好用的 解决方案是在每个地方都使用 foo 标识符做为函数对象的引用,而根本不用this

function foo(num) {
  console.log("foo: " + num);

  // 追踪 `foo` 被调用了多少次
  foo.count++;
}

foo.count = 0;

var i;

for (i = 0; i < 10; i++) {
  if (i > 5) {
    foo(i);
  }
}
// foo: 6
// foo: 7
// foo: 8
// foo: 9

// `foo` 被调用了多少次?
console.log(foo.count); // 4
复制代码

然而,这种方法也相似地回避了对 this真正 理解,并且彻底依靠变量 foo 的词法做用域。

2.2 它的做用域

this 的含义第二常见的误解,是它不知怎的指向了函数的做用域。这是一个刁钻的问题,由于在某一种意义上它有正确的部分,而在另一种意义上,它是严重的误导。

明确地说,this 不会以任何方式指向函数的 词法做用域。做用域好像是一个将全部可用标识符做为属性的对象,这从内部来讲是对的。可是 JavasScript 代码不能访问做用域“对象”。它是 引擎的内部实现。

考虑下面代码,它(失败的)企图跨越这个边界,用 this 来隐含地引用函数的词法做用域:

function foo() {
  var a = 2;
  this.bar();
}

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

foo(); //undefined
//两个this都指向 window,试图经过 this.bar() 来引用 bar() 函数,可以执行仅仅是巧合。
复制代码

不能使用 this 引用在词法做用域中查找东西。这是不可能的。

咱们早先说过,this 不是编写时绑定,而是运行时绑定。它依赖于函数调用的上下文条件。this 绑定与函数声明的位置没有任何关系,而与函数被调用的方式紧密相连。

当一个函数被调用时,会创建一个称为执行环境的活动记录。这个记录包含函数是从何处(调用栈 —— call-stack)被调用的,函数是如何被调用的,被传递了什么参数等信息。这个记录的属性之一,就是在函数执行期间将被使用的 this 引用。

相关文章
相关标签/搜索