说明

《你不知道的JavaScript》学习笔记。

为什么要用this

书中举了一个例子:

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, 我是 KYLE
speak.call( you ); // Hello, 我是 READER

如果不使用 this,那就需要给 identify() 和 speak() 显式的传入一个上下文对象。

function identify(context) {
return context.name.toUpperCase();
}
function speak(context) {
var greeting = "Hello, I'm " + identify( context );
console.log( greeting );
}
var me = {
name: "Kyle"
};
var you = {
name: "Reader"
};
identify( you ); // READER
speak( me ); //hello, 我是 KYLE

随着使用模式越来越复杂,显式传递上下文对象会让代码变得越来越混乱,使用 this 则不会这样。

this 提供了一种更优雅的方式来隐式“传递” 一个对象引用, 因此可以将 API 设计得更加简洁并且易于复用。

误解

两种常见的对于 this 的错误解释。

1、指向自身

1、先看一个例子:想要记录一下函数 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

console.log( count ); // NaN

显然 foo() 里的 this 不是指向那个函数对象,虽然属性名相同,跟对象却不相同。

2、再看下面两个函数

function foo() {
foo.count = 4; // foo 指向它自身
}

setTimeout( function(){
// 匿名(没有名字的) 函数无法指向自身
}, 10 );

代码分析:

  • 1、​​foo()​​ 函数是具名函数,在它的内部可以使用 foo 来引用自身
  • 2、传入​​setTimeout()​​ 的回调函数是匿名函数,无法从函数内部引用自身

3、解决方法

  • 1、使用 foo 标识符替代 this 来引用函数对象
  • 2、强制 this 指向 foo 函数对象

方法1:

function foo(num) {
console.log( "foo: " + num );
// 记录 foo 被调用的次数
foo.count++;
}

方法2:

var i;
for (i=0; i<10; i++) {
if (i > 5) {
// 使用 call(..) 可以确保 this 指向函数对象 foo 本身
foo.call( foo, i );
}
}

2、它的作用域

  • this 在任何情况下都不指向函数的词法作用域。
  • 在 JavaScript 内部, 作用域确实和对象类似, 可见的标识符都是它的属性。
  • 作用域“对象” 无法通过 JavaScript 代码访问, 它存在于 JavaScript 引擎内部。

this到底是什么

当一个函数被调用时, 会创建一个活动记录(有时候也称为​​执行上下文​​​)。 这个记录会包含函数在哪里被调用(​​调用栈​​)、 函数的调用方法、 传入的参数等信息。 this 就是记录的其中一个属性, 会在函数执行的过程中用到。

  • this 是在运行时进行绑定的, 并不是在编写时绑定,它的上下文取决于函数调用时的各种条件。
  • this 的绑定和函数声明的位置没有任何关系, 只取决于函数的调用方式。