[翻译]You Don't Know JS: this & Object Prototypes:Chapter1

[翻译]Chapter1 this or that

第一次翻译,翻译的不好,已再尽全力s去翻译了,假如那里看不明点,请出门左转下边原文地点

英文原文点击这里

javascript中最令人疑心的东西就是this关键字,它在每一个函数作用域中都邑自动定义的一个特别的标识符,然则,它熬煎着每一个javascript开发者,哪怕是有履历的。

任何科技的充足提高,都和魔法没什么区分

javascript的this机制现实上并非先进的,然则开发者常常根据他们本身的看法把this诠释的庞杂和杂沓。毫无疑问这是由于缺乏深切的邃晓。

why this

既然this机制让开发者以至是履历丰富的程序员觉得疑心。那为什么会是有用的,thia弊大于利吗?在此之前,我们先跳过how的题目,去检察一下why的题目。

让我们来申明运用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, I'm KYLE
speak.call( you ); // Hello, I'm READER

假如看不邃晓这个代码块,不要焦急!我们很快会诠释,把这些题目放到一边,我们先来看关于why的题目。

这个代码块许可identify()和speak()两个函数分别被两个对象me和you重用,而不是为每一个对象零丁写一个版本的函数。

经由过程依靠this。你能够用一种更明白的体式格局在环境对象中运用两个要领。

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上下文越发贫苦。当我们检察对象和原型的时刻,你将能看到一个能够自动援用准确的函数鸠合上下文对象的实用性。

疑心点

我们很快会最先诠释this现实上是怎样事情的,然则起首要改正一下毛病的看法。

itself

第一个常常被诠释成疑心的是this指代函数本身,最少这却是一个合理的诠释。

为什么你会须要在函数内部援用函数本身,最能够的缘由是递归(在函数内部挪用函数本身)或许事宜处置惩罚的时刻当第一次挪用时解绑事宜。

开发者最新的js机制是把函数当做一个对象一样来援用(js里一切的函数都是对象),如许会致使须要在函数相互挪用之间存储状况(属性值)。固然这类机制可行的,也有一些有限的用途。这本书剩下的部份将会论述很多其他的情势,以便更好的存储函数对象以外的状况。

然则等一会,我们将会探究一种情势,来申明this是怎样不让函数取得本身的援用,就像我们之前假定的一样。

斟酌下边的代码,我们尝试去跟踪一下foo函数被挪用了多少次。

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

    // keep track of how many times `foo` is called
    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

// how many times was `foo` called?
console.log( foo.count ); // 0 -- WTF?

foo.count依旧是0,纵然经过了四次console以后我们能清楚的看到foo现实上挪用了四次,这个让人扫兴的泉源在于对this是什么的诠释太字面。

当代码实行到foo.count = 0的时刻,确切,它给foo函数对象添加了一个属性count,然则this.count只是在函数内部援用,this并非指向一切其他的函数对象,纵然属性称号雷同,由于this地点的对象不一样,所以疑心就产生了。

一个有义务的开发者应该问到这一点:假如我增添一个count属性然则这个属性并非我想要的,那我是不是增添了。现实上,假如开发者更深的发掘,她会发明她建立了一个变量count,这个变量当前的值是NAN,一旦她注意到这个新鲜的效果,她将有一系列的疑问,全局对象是什么,为什么这个值是NAN而不是数值。(在foo中打印count,会显现出NAN)。

有义务的开发者不该当在这一点住手而是应该深切发掘为什么这个援用的表现没有想料想的那样,去回复这个辣手然则很主要的题目。很多开发者简朴的防止这个题目,而且采用一些其他的处理办法,比方建立另一个对象去存储count这个属性:

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

    // keep track of how many times `foo` is called
    data.count++;
}

var data = {
    count: 0
};

var i;

for (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( data.count ); // 4

虽然这个确切处理了题目,然则不幸的是它疏忽了真正的题目,不能邃晓this究竟是什么以及她是怎样事情的,而是回到了一个越发熟习的温馨环境,词法作用域(静态作用域)。

词法作用域是一个圆满而有用的机制,我不会以任何体式格局轻蔑它,然则,不停的猜想怎样运用this,然则一般会带来毛病,退回到词法作用域去处理题目并非一个好缘由。

为了在一个函数对象援用本身,this有着不言而喻的不足,你一般须要经由过程指向词法标识符(变量)去援用函数对象。

function foo() {
    foo.count = 4; // `foo` refers to itself
}

setTimeout( function(){
    // anonymous function (no name), cannot
    // refer to itself
}, 10 );

在第一个要领中,称为定名函数,foo是一个援用,能够被用来在函数内部援用函数本身。

然则第二个例子,这个没有称号标识的函数是经由过程setTimeout实行回调(所以叫匿名函数),所以,这个时刻没有适宜的要领经由过程函数名援用函数对象本身。

上边是旧的用法,如今已弃用,一个函数中的arguments.callee援用指向当前实行的函数的函数对象。this援用是从匿名函数内部援用本身的唯一要领,不过,最好的要领是防止运用匿名函数,最少在那些须要援用本身的时刻,运用定名函数或许表达式。而arguments.callee已被弃用,不发起运用。

所以,别的一个处理要领处理我们运转的例子是我们在每一个处所用foo这个标识符作为函数对象的援用,而不是this。

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

    // keep track of how many times `foo` is called
    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

// how many times was `foo` called?
console.log( foo.count ); // 4

但是这个要领依旧侧重于this的现实邃晓,而且依靠于foo的词法作用域中的变量。

另有别的一种要领更关注this在foo函数对象中现实指向的题目。

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

// how many times was `foo` called?
console.log( foo.count ); // 4

防止用this,我们拥抱这类要领,我们将会轻微诠释一下这个手艺是怎样更完全的事情的,所以,假如你依然有一点疑心,别焦急。

its scope

第二个罕见的毛病看法是以为this的意义是以某种情势指代函数作用域,这是一个让人疑心的题目,由于在肯定意义上这是有原理的,然则另一方面,这是很让人误导的。

要明白的是,在任何情况下,this并非指代函数的词法作用域,在内部,作用域是一个相似能够接见每一个标识符属性的对象,然则这个作用域对象在js代码中是不能接见的,他是引擎内部实行的一部份。

下边代码尝试去逾越代码的边境去运用this隐式的指向函数作用域。

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

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

foo(); //undefined

这个代码块里有不止一个毛病,好像它能够得出想要的效果,这个你看到的代码。。。(原文这里讽刺了这块代码)。

起首,你想经由过程this.bar()来援用bar要领,当运转起来无疑会出变乱,我们要尽快诠释为什么报错,挪用bar()最天然的体式格局是省略this,只对标识符举行词法援用。

但是,开发者尝试运用this的目标是在foo和bar两个函数之间制造一个桥梁,是的bar能够接见到foo内部作用域中的变量,然则并没有如许的桥梁,你不能用this援用去查找词法作用域下的一些东西,这是不能够的。

每当你觉得你想尝试把慈父作用域的查找和this夹杂的时刻,记得,如许的桥是不存在的。

what this

抛开那些不准确的诠释,如今我们把注意力,转移到this机制是怎样事情的。

我们之前说过,this是运转的时刻绑定而不是声明的时刻绑定,它是基于函数挪用情况下的上下文,this绑定和函数性命的位置没有关联,而是与函数的挪用体式格局有关联。

当一个函数被挪用,将会建立一个激活纪录,也就是所谓的实行上下文。该纪录包含了函数挪用的位置信息(客栈),以及函数是怎样被挪用的,另有参数是怎样通报的等等,这个纪录的个中一个属性是this援用,将会在函数实行的时期被运用。

鄙人一章,我们要进修去找一个函数的挪用点去确定在函数实行的时刻是怎样绑定this的。

回忆

this绑定关于没有花时间去搞懂这个机制详细怎样事情的js开发者来说是一个恒定不停的难题。来自stackoverfolw的回复者的猜想,试错和自觉标复制粘贴并非应用这类机制的有用要领。

进修this,起首要去相识this不是什么,只管任何的假定或许误会都能够致使你。。。this既不是函数本身的援用,也不是函数词法作用域的援用。

this现实上是函数挪用时举行的绑定,它的援用绑定完全由函数的挪用位置所决议。

    原文作者:张小草1018
    原文地址: https://segmentfault.com/a/1190000009064229
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞