[JS] You Don't Know JavaScript [this & Object Proto

前言

this是JavaScript中最令人困惑的关键字之一,他会自动在每个function作用域中生成,但是this实际上是指向什么对很多资深的JS开发人员来说也是困惑的,对于this这个机制而言并没有这么先进,但是开发者常常会将它複杂化或令人困惑的方式引用他,如果没有对于this的充分了解那么this这个关键字将会变得跟魔术一样神奇。


Why 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 ); // KYLEidentify.call( you ); // READERspeak.call( me ); // Hello, I'm KYLEspeak.call( you ); // Hello, I'm READER

上面的程式中允许identify(...)speak(...)重複使用meyouobject,而不是每个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 ); // READERspeak( me ); // Hello, I'm KYLE

以上面的例子来说,可以看到虽然this不是必要的,但是他可以更优雅的传递object,从而使API个简洁并易于重新使用;而随着模式更加複杂,将上下文使用显性参数传递的方式会比隐式使用this的传递更加混乱。


Confusions

开发者对于this如果是使用字面上的意思去解释他的时候往往都会造成混乱,最常见的有两种假设,但他们都是错的。

itself

第一种假设是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`) itselffoo.call( foo, i );}}// foo: 6// foo: 7// foo: 8// foo: 9console.log( foo.count ); // 4

把原本的程式更改为上面就可以顺利的访问到foo中的count了,你可能会感到困惑,但是我们会在后面详细地做解释。

Its Scope

还有一个主要的误解,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。


What's this?

对于this来说,他不是取决于开发者时间所绑定而是运行时绑定,他是基于上下文取决于函数调用的条件,这个绑定与函式声明的位置无关而是与函式调用的方式有关。

当呼叫一个function时会创建一个启动纪录(执行上下文),这个纪录包含有关从何处调用function的信息、呼叫此function的方式、传递的参数等等;this会在function执行的期间使用纪录的属性之一。

参考文献:
You Don't Know JavaScript


关于作者: 网站小编

码农网专注IT技术教程资源分享平台,学习资源下载网站,58码农网包含计算机技术、网站程序源码下载、编程技术论坛、互联网资源下载等产品服务,提供原创、优质、完整内容的专业码农交流分享平台。

热门文章