this
是JavaScript中最令人困惑的关键字之一,他会自动在每个function作用域中生成,但是this
实际上是指向什麽对很多资深的JS开发人员来说也是困惑的,对於this这个机制而言并没有这麽先进,但是开发者常常会将它复杂化或令人困惑的方式引用他,如果没有对於this的充分了解那麽this这个关键字将会变得跟魔术一样神奇。
如果this
是个令人困惑的机制,那麽为什麽JavaScript会需要用到它?在了解他之前我们应该知道他的价值。
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(...)
重复使用me
与you
object,而不是每个object都需要自己的function。
如果不使用this
,你也可以将object显性的传递给function。
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, I'm KYLE
以上面的例子来说,可以看到虽然this
不是必要的,但是他可以更优雅的传递object,从而使API个简洁并易於重新使用;而随着模式更加复杂,将上下文使用显性参数传递的方式会比隐式使用this的传递更加混乱。
开发者对於this
如果是使用字面上的意思去解释他的时候往往都会造成混乱,最常见的有两种假设,但他们都是错的。
第一种假设是this
代表着函数本身,当你使用递归(在函式内部呼叫function
)或一个在首次调用可以解除绑定的事件处理
时会在自身函式中呼叫function。
对於刚接触JS的开发者,他们认为由於function是object(所有function在JS中都是object)所以可以在函数调用之间储存状态(function属性中的值),虽然这是可行的但是他的功能有限。
function foo(num) {
console.log( "foo: " + num );
this.count++; // keep track of how many times `foo` is called
}
foo.count = 0;
for (var i=0; i<10; i++) {
if (i > 5) {
foo( i );
}
}
// foo: 6
// foo: 7
// foo: 8
// foo: 9
// how many times was `foo` called?
console.log( foo.count ); // 0 -- WTF?
上面的程序中,即使我们对於foo(...)调用了四次但是foo.count
依然是0,虽然我们确实有在foo的属性中加入了count,但是因为this
并不是只向该function的object,就算属性名称相同但是所指向的object不同所以依然是0。
function foo(num) {
console.log( "foo: " + num );
// keep track of how many times `foo` is called
// Note: `this` IS actually `foo` now, based on
// how `foo` is called (see below)
this.count++;
}
foo.count = 0;
var i;
for (i=0; i<10; i++) {
if (i > 5) {
// using `call(..)`, we ensure the `this`
// points at the function object (`foo`) itself
foo.call( foo, i );
}
}
// foo: 6
// foo: 7
// foo: 8
// foo: 9
console.log( foo.count ); // 4
把原本的程序更改为上面就可以顺利的访问到foo中的count了,你可能会感到困惑,但是我们会在後面详细地做解释。
还有一个主要的误解,this
是function的作用域,从某种意义上来说他是对的,但另一种意义上来说他是完全错误的。
对於scope来说他确实有点像object,在他的范围中具有每个可用的标示符的属性,但是JS引擎无法访问到scope object。
function foo() {
var a = 2;
this.bar();
}
function bar() {
console.log( this.a );
}
foo(); //undefined
在上面的程序中使用this.bar()
来调用bar(...)虽然在这里行得通(之後的章节会解释)但是其实是错误的,对於呼叫bar(...)
最自然的使用方法就是直接引用他的标示符,但是写上面这个程序的开发者希望将bar(...)
与foo(...)
的lexcial scope连结再一起以便bar(...)可以访问到a,这样的连结是不可能的
,不能够通过使用this来连结两个lexcial scope。
对於this
来说,他不是取决於开发者时间所绑定而是运行时绑定,他是基於上下文取决於函数调用的条件
,这个绑定与函式声明的位置无关而是与函式调用的方式有关。
当呼叫一个function时会创建一个启动纪录(执行上下文)
,这个纪录包含有关从何处调用function的信息、呼叫此function的方式、传递的参数等等;this会在function执行的期间使用纪录的属性之一。
参考文献:
You Don't Know JavaScript
<<: ProxmoxVE PVE VM 安装 ChromeOS
>>: Mobile Number Tracker Online
前言 GPIO为最基础应用也最广泛之功能,本篇主要纪录GPIO中所学习到的知识。 以STM32G43...
参赛心得: 今天是铁人赛最後一天,会参加铁人赛是因为学校做也得要求,虽然学习的内容不算难,主要的文章...
在 Objective-C 与 Swift 的命名有明显的区别,虽然 Swift interface...
今天原本想要介绍一下 Virtual DOM 结果忙一下 , 就没时间研究 diff 演算法了 di...
这是我第一次参加铁人赛,如果内容有不清楚或有误欢迎大家指正,影片皆是我自学Unity并且规划的教学内...