说明
《你不知道的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 的绑定和函数声明的位置没有任何关系, 只取决于函数的调用方式。