成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

this其實(shí)很簡單

TerryCai / 2440人閱讀

摘要:來點(diǎn)規(guī)則,有規(guī)可尋我們必須考察調(diào)用點(diǎn),來判斷下面即將要說的四中規(guī)則哪一種適用。所以,在回調(diào)函數(shù)中丟失綁定是一件很常見的事情,但是還有另一種情況,接受我們回調(diào)的函數(shù)故意改變的值。

此文主要總結(jié)于《你不知道的JavaScript 上卷》,雖然講解this的文章已經(jīng)爛大街了,但是依舊希望,這篇文章可以幫助到那些還是對(duì)this有些疑惑的哥們

前言

this關(guān)鍵字是JavaScript中最復(fù)雜的機(jī)制之一,它不是一個(gè)特殊的關(guān)鍵字,被自動(dòng)定義在所有的函數(shù)作用域中。

老規(guī)矩,我們直接看例子:

function identify(){
    console.log(this.name)
    return this.name.toUpperCase();
}

function speak() {
  var gretting = "Hello I am "+identify.call(this)
  console.log(gretting);
}

var me = {
    name:"Neal"
}

var you = {
    name:"Nealyang"
}
identify.call(me);
identify.call(you);

speak.call(me);
speak.call(you);

關(guān)于運(yùn)行結(jié)果大家可以自行運(yùn)行查看,如果對(duì)于this是如何工作的這里我們還是存在疑惑,那么別急,我們后面當(dāng)然會(huì)繼續(xù)深入探討下,這里,先說下關(guān)于this
的一些誤解的地方

關(guān)于this的誤解 this 值得是它自己

通常新手都會(huì)認(rèn)為this就是指向函數(shù)本身,至于為什么在函數(shù)中引用他自己呢,可能就是因?yàn)檫f歸這種情況的存在吧。但是這里,我想說,this并不是指向函數(shù)本身的

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

foo.count = 0;

for(var i = 0;i<10;i++){
    foo(i);
}

console.log(foo.count);

通過運(yùn)行上面的代碼我們可以看到,foo函數(shù)的確是被調(diào)用了十次,但是this.count似乎并沒有加到foo.count上。也就是說,函數(shù)中的this.count并不是foo.count。

所以,這里我們一定要記住一個(gè),就是函數(shù)中的this并不是指向函數(shù)本身的。上面的代碼修改如下:

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

foo.count = 0;

for(var i = 0;i<10;i++){
    foo.call(foo,i);
}

console.log(foo.count);

運(yùn)行如上代碼,此時(shí)我們就可以看到foo函數(shù)中的count的確已經(jīng)變成10了

this值得是他的作用域

另一種對(duì)this的誤解是它不知怎么的指向函數(shù)的作用域,其實(shí)從某種意義上來說他是正確的,但是從另一種意義上來說,這的確是一種誤解。

明確的說,this不會(huì)以任何方式指向函數(shù)的詞法作用域,作用域好像是一個(gè)將所有可用標(biāo)識(shí)符作為屬性的對(duì)象,這從內(nèi)部來說他是對(duì)的,但是JavaScript代碼不能訪問這個(gè)作用域“對(duì)象”,因?yàn)樗且鎯?nèi)部的實(shí)現(xiàn)。

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

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

foo(); //undefined

上面的代碼不止一處錯(cuò)誤,這里不做討論,僅僅用于看代碼,首先,視圖this.bar()來視圖訪問bar函數(shù),的確他做到了。雖然只是碰巧而已。然而,寫下這段代碼的開發(fā)者視圖使用this在foo和bar的詞法作用域中建立一座橋,是的bar可以訪問foo內(nèi)部變量作用域a。當(dāng)然,這是不可能的,不可能使用this引用在詞法作用域中查找東西。

什么是this

所以說了這么coder對(duì)this的誤解,那么究竟什么是this呢。記住,this不是在編寫時(shí)候綁定的,而是在運(yùn)行時(shí)候綁定的上下文執(zhí)行環(huán)境。this綁定和函數(shù)申明無關(guān),反而和函數(shù)被調(diào)用的方式有關(guān)系。

當(dāng)一個(gè)函數(shù)被調(diào)用的時(shí)候,會(huì)建立一個(gè)活動(dòng)記錄,也成為執(zhí)行環(huán)境。這個(gè)記錄包含函數(shù)是從何處(call-stack)被調(diào)用的,函數(shù)是 如何 被調(diào)用的,被傳遞了什么參數(shù)等信息。這個(gè)記錄的屬性之一,就是在函數(shù)執(zhí)行期間將被使用的this引用。

徹底明白this到底值得什么鬼 調(diào)用點(diǎn)

為了徹底弄明白this的指向問題,我們還必須明白什么是調(diào)用點(diǎn),即一個(gè)函數(shù)被調(diào)用的位置??紤]調(diào)用棧(即使我們到達(dá)當(dāng)前執(zhí)行位置而被d調(diào)用的所有方法堆棧)是非常重要的,我們關(guān)心的調(diào)用點(diǎn)就是當(dāng)前執(zhí)行函數(shù)的之前的調(diào)用

function baz() {
    // 調(diào)用棧是: `baz`
    // 我們的調(diào)用點(diǎn)是global scope(全局作用域)

    console.log( "baz" );
    bar(); // <-- `bar`的調(diào)用點(diǎn)
}

function bar() {
    // 調(diào)用棧是: `baz` -> `bar`
    // 我們的調(diào)用點(diǎn)位于`baz`

    console.log( "bar" );
    foo(); // <-- `foo`的call-site
}

function foo() {
    // 調(diào)用棧是: `baz` -> `bar` -> `foo`
    // 我們的調(diào)用點(diǎn)位于`bar`

    console.log( "foo" );
}

baz(); // <-- `baz`的調(diào)用點(diǎn)

上面代碼大家簡單感受下什么是調(diào)用棧和調(diào)用點(diǎn),比較簡單的東西。

來點(diǎn)規(guī)則,有規(guī)可尋

我們必須考察調(diào)用點(diǎn),來判斷下面即將要說的四中規(guī)則哪一種適用。先獨(dú)立解釋下四中規(guī)則的每一種,然后再來說明下如果多種規(guī)則適用調(diào)用點(diǎn)時(shí)他們的優(yōu)先級(jí)。

默認(rèn)綁定

所謂的默認(rèn)綁定,就是獨(dú)立函數(shù)的調(diào)用形式。

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

var a = 2;

foo(); // 2

為什么會(huì)是2呢,因?yàn)樵谡{(diào)用foo的時(shí)候,JavaScript對(duì)this實(shí)施了默認(rèn)綁定,所以this就指向了全局對(duì)象。

我們?cè)趺粗肋@里適用 默認(rèn)綁定 ?我們考察調(diào)用點(diǎn)來看看foo()是如何被調(diào)用的。在我們的代碼段中,foo()是被一個(gè)直白的,毫無修飾的函數(shù)引用調(diào)用的。沒有其他的我們將要展示的規(guī)則適用于這里,所以 默認(rèn)綁定 在這里適用。

需要注意的是,對(duì)于嚴(yán)格模式來說,默認(rèn)綁定全局對(duì)象是不合法的,this被置為undefined。但是一個(gè)很微妙的事情是,即便是所有的this綁定規(guī)則都是基于調(diào)用點(diǎn)的,如果foo的內(nèi)容沒有嚴(yán)格模式下,默認(rèn)綁定也是合法的。

隱含綁定

調(diào)用點(diǎn)是否有一個(gè)環(huán)境對(duì)象,也成為擁有者和容器對(duì)象。

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

var obj = {
    a: 2,
    foo: foo
};

obj.foo(); // 2

foo被申明,然后被obj添加到其屬性上,無論foo()是否一開始就在obj上被聲明,還是后來作為引用添加(如上面代碼所示),都是這個(gè) 函數(shù) 被obj所“擁有”或“包含”。

這里需要注意的是,只有對(duì)象屬性引用鏈的最后一層才影響調(diào)用點(diǎn)

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

var obj2 = {
    a: 42,
    foo: foo
};

var obj1 = {
    a: 2,
    obj2: obj2
};

obj1.obj2.foo(); // 42
隱含綁定丟死

this綁定最讓人頭疼的地方就是隱含綁定丟失了他的綁定,其實(shí)明確了調(diào)用位置,這個(gè)也不是難點(diǎn)。直接看代碼

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

var obj = {
    a: 2,
    foo: foo
};

var bar = obj.foo; // 函數(shù)引用!

var a = "oops, global"; // `a`也是一個(gè)全局對(duì)象的屬性

bar(); // "oops, global"

所以如上的調(diào)用模式,我們又退回到了默認(rèn)綁定模式。

還能hold住,那么接著看代碼:

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

function doFoo(fn) {
    // `fn` 只不過`foo`的另一個(gè)引用

    fn(); // <-- 調(diào)用點(diǎn)!
}

var obj = {
    a: 2,
    foo: foo
};

var a = "oops, global"; // `a`也是一個(gè)全局對(duì)象的屬性

doFoo( obj.foo ); // "oops, global"

參數(shù)傳遞,僅僅是一種隱含的賦值,而且因?yàn)槲覀兪莻鬟f一個(gè)函數(shù),他是一個(gè)隱含的引用賦值,所以最終結(jié)果和我們前一段代碼一樣。

所以,在回調(diào)函數(shù)中丟失this綁定是一件很常見的事情,但是還有另一種情況,接受我們回調(diào)的函數(shù)故意改變this的值。那些很受歡迎的事件處理JavaScript包就十分喜歡強(qiáng)制你的回調(diào)的this指向觸發(fā)事件的DOM元素。

不管哪一種意外改變this的方式,你都不能真正地控制你的回調(diào)函數(shù)引用將如何被執(zhí)行,所以你(還)沒有辦法控制調(diào)用點(diǎn)給你一個(gè)故意的綁定。我們很快就會(huì)看到一個(gè)方法,通過 固定 this來解決這個(gè)問題。

如上,我們一定要清除的是引用和調(diào)用。記住,找this,我們只看調(diào)用,別被引用所迷惑

明確綁定

在JavaScript中,我們可以強(qiáng)制制定一個(gè)函數(shù)在運(yùn)行時(shí)候的this值。是的,call和apply,他們的作用就是擴(kuò)充函數(shù)賴以生存的作用域。

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

var obj = {
    a: 2
};

foo.call( obj ); // 2

上面代碼,我們使用foo,強(qiáng)制將foo的this指定為obj

如果你傳遞一個(gè)簡單原始類型值(string,boolean,或 number類型)作為this綁定,那么這個(gè)原始類型值會(huì)被包裝在它的對(duì)象類型中(分別是new String(..),new Boolean(..),或new Number(..))。這通常稱為“boxing(封箱)”。

但是,多帶帶的依靠明確綁定仍然不能為我們先前提到的問題,提供很好的解決方案,也就是函數(shù)丟失自己原本的this綁定。

硬性綁定
function foo() {
    console.log( this.a );
}

var obj = {
    a: 2
};

var bar = function() {
    foo.call( obj );
};

bar(); // 2
setTimeout( bar, 100 ); // 2

// `bar`將`foo`的`this`硬綁定到`obj`
// 所以它不可以被覆蓋
bar.call( window ); // 2

我們創(chuàng)建了一個(gè)函數(shù)bar(),在它的內(nèi)部手動(dòng)調(diào)用foo.call(obj),由此強(qiáng)制this綁定到obj并調(diào)用foo。無論你過后怎樣調(diào)用函數(shù)bar,它總是手動(dòng)使用obj調(diào)用foo。這種綁定即明確又堅(jiān)定,所以我們稱之為 硬綁定(hard binding)

new 綁定

這個(gè)比較簡單,當(dāng)函數(shù)前面加入new關(guān)鍵字調(diào)用的時(shí)候,其實(shí)就是當(dāng)做構(gòu)造函數(shù)調(diào)用的。其內(nèi)部其實(shí)完成了如下事情:

一個(gè)新的對(duì)象會(huì)被創(chuàng)建

這個(gè)新創(chuàng)建的對(duì)象會(huì)被接入原型鏈

這個(gè)新創(chuàng)建的對(duì)象會(huì)被設(shè)置為函數(shù)調(diào)用的this綁定

除非函數(shù)返回一個(gè)他自己的其他對(duì)象,這個(gè)被new調(diào)用的函數(shù)將自動(dòng)返回一個(gè)新創(chuàng)建的對(duì)象

總結(jié)性來一波

函數(shù)是否在new中調(diào)用,如果是的話this綁定的是新創(chuàng)建的對(duì)象

var bar = new Foo();

函數(shù)是否通過call、apply或者其他硬性調(diào)用,如果是的話,this綁定的是指定的對(duì)象

var bar = foo.call(obj);

函數(shù)是否在某一個(gè)上下文對(duì)象中調(diào)用,如果是的話,this綁定的是那個(gè)上下文對(duì)象

var bar = obj.foo();

如果都不是的話,使用默認(rèn)綁定,如果在嚴(yán)格模式下,就綁定到undefined,注意這里是方法里面的嚴(yán)格聲明。否則綁定到全局對(duì)象

var bar = foo();
綁定例外

第一種情況就是將null和undefined傳給call、apply、bind等函數(shù),然后此時(shí)this采用的綁定規(guī)則是默認(rèn)綁定

第二種情況這里舉個(gè)例子,也是面試中常常會(huì)出現(xiàn)的例子

function foo() {
  console.log(this.a);
}
var a = 2;
var o = {
    a:3,
    foo:foo
}
var p = {a:4};
(p.foo = o.foo)();

如上調(diào)用,其實(shí)foo采用的也是默認(rèn)綁定,這里我們需要知道的是,p.foo = o.foo的返回值是目標(biāo)函數(shù)的引用,所以最后一句其實(shí)就是foo()

es6中的箭頭函數(shù)

es6中的箭頭函數(shù)比較簡單,由于箭頭函數(shù)并不是function關(guān)鍵字定義的,所以箭頭函數(shù)不適用this的這四中規(guī)則,而是根據(jù)外層函數(shù)或者全局作用域來決定this

function foo() {
  // 返回一個(gè)arrow function
    return (a) => {
    // 這里的`this`是詞法上從`foo()`采用
        console.log( this.a );
    };
}

var obj1 = {
    a: 2
};

var obj2 = {
    a: 3
};

var bar = foo.call( obj1 );
bar.call( obj2 ); // 2, 不是3!

這里foo內(nèi)部創(chuàng)建的箭頭函數(shù)會(huì)自動(dòng)獲取foo的this。

來一道經(jīng)典面試題吧

第一題

var a=10;
var foo={
  a:20,
  bar:function(){
      var a=30;
      console.log(this)
      return this.a;
    }
};
foo.bar()
(foo.bar)()
(foo.bar=foo.bar)()
(foo.bar,foo.bar)()

第二題

function t(){
this.x=2;
}
t();
console.log(window.x);

第三題

var obj = {
x: 1,
y: 2,
t: function() {
console.log(this.x)
}
}
obj.t();

var dog={x:11};
dog.t=obj.t;
dog.t();


show=function(){
console.log("show"+this.x);

}

dog.t=show;
dog.t();

第四題

name = "this is window";
var obj1 = {
name: "php",
t: function() {
console.log(this.name)
}
};
var dog1 = {
name: "huzi"
};

obj1.t();

dog1.t = obj1.t;

var tmp = dog1.t;
tmp(); //this本來指向window

(dog1.t = obj1.t)();
dog1.t.call(obj1);

第五題

var number=2;
var obj={
number:4,
/*匿名函數(shù)自調(diào)*/
fn1:(function(){
var number;
this.number*=2;//4

number=number*2;//NaN
number=3;
return function(){
var num=this.number;
this.number*=2;//6
console.log(num);
number*=3;//9
alert(number);
}
})(),

db2:function(){
this.number*=2;
}
}

var fn1=obj.fn1;

alert(number);

fn1();

obj.fn1();

alert(window.number);

alert(obj.number);
交流

掃碼關(guān)注我的個(gè)人微信公眾號(hào),分享更多原創(chuàng)文章。點(diǎn)擊交流學(xué)習(xí)加我微信、qq群。一起學(xué)習(xí),一起進(jìn)步。共同交流上面的題目吧

歡迎兄弟們加入:

Node.js技術(shù)交流群:209530601

React技術(shù)棧:398240621

前端技術(shù)雜談:604953717 (新建)

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/89780.html

相關(guān)文章

  • 40 行代碼內(nèi)實(shí)現(xiàn)一個(gè) React.js

    摘要:代碼托管這個(gè)倉庫。假設(shè)現(xiàn)在我們需要實(shí)現(xiàn)一個(gè)點(diǎn)贊取消點(diǎn)贊的功能。如果你對(duì)前端稍微有一點(diǎn)了解,你就順手拈來點(diǎn)贊為了現(xiàn)實(shí)當(dāng)中的實(shí)際情況,所以這里特易把這個(gè)的結(jié)構(gòu)搞得稍微復(fù)雜一些。這里非常暴力地使用了,把兩個(gè)按鈕粗魯?shù)夭迦肓水?dāng)中。 作者:胡子大哈原文鏈接:http://huziketang.com/blog/posts/detail?postId=58aea515204d50674934c3a...

    twohappy 評(píng)論0 收藏0
  • React 導(dǎo)讀(一)

    摘要:需要有一定的基礎(chǔ)和的使用經(jīng)驗(yàn)。這就是屬性的作用。方法接收一個(gè)新對(duì)象來重新賦值。也接收一個(gè)函數(shù),這個(gè)回調(diào)函數(shù)這里我默認(rèn)有一個(gè)參數(shù),表示之前的的值,這個(gè)函數(shù)的返回值就是最新的。但是不同的是在組件內(nèi)部是只讀的。 前言 寫這篇文章的主要目標(biāo)是讓初學(xué)者更快的上手 React 的項(xiàng)目開發(fā),能有一個(gè)循循漸進(jìn)的理解過程。需要有一定的 JavaScript 基礎(chǔ)和 NPM 的使用經(jīng)驗(yàn)。不多說了,下面會(huì)按...

    kumfo 評(píng)論0 收藏0
  • 十分鐘快速了解《你不知道的 JavaScript》(上卷)

    摘要:最近剛剛看完了你不知道的上卷,對(duì)有了更進(jìn)一步的了解。你不知道的上卷由兩部分組成,第一部分是作用域和閉包,第二部分是和對(duì)象原型。附錄詞法這一章并沒有說明機(jī)制,只是介紹了中的箭頭函數(shù)引入的行為詞法。第章混合對(duì)象類類理論類的機(jī)制類的繼承混入。 最近剛剛看完了《你不知道的 JavaScript》上卷,對(duì) JavaScript 有了更進(jìn)一步的了解。 《你不知道的 JavaScript》上卷由兩部...

    趙春朋 評(píng)論0 收藏0
  • vuex其實(shí)簡單,只需3步

    摘要:每一條被記錄,都需要捕捉到前一狀態(tài)和后一狀態(tài)的快照。然而,在上面的例子中中的異步函數(shù)中的回調(diào)讓這不可能完成因?yàn)楫?dāng)觸發(fā)的時(shí)候,回調(diào)函數(shù)還沒有被調(diào)用,不知道什么時(shí)候回調(diào)函數(shù)實(shí)際上被調(diào)用實(shí)質(zhì)上任何在回調(diào)函數(shù)中進(jìn)行的狀態(tài)的改變都是不可追蹤的。 前言 之前幾個(gè)項(xiàng)目中,都多多少少碰到一些組件之間需要通信的地方,而因?yàn)榉N種原因,event bus 的成本反而比vuex還高, 所以技術(shù)選型上選用了 v...

    binta 評(píng)論0 收藏0
  • vuex其實(shí)簡單,只需3步

    摘要:每一條被記錄,都需要捕捉到前一狀態(tài)和后一狀態(tài)的快照。然而,在上面的例子中中的異步函數(shù)中的回調(diào)讓這不可能完成因?yàn)楫?dāng)觸發(fā)的時(shí)候,回調(diào)函數(shù)還沒有被調(diào)用,不知道什么時(shí)候回調(diào)函數(shù)實(shí)際上被調(diào)用實(shí)質(zhì)上任何在回調(diào)函數(shù)中進(jìn)行的狀態(tài)的改變都是不可追蹤的。 前言 之前幾個(gè)項(xiàng)目中,都多多少少碰到一些組件之間需要通信的地方,而因?yàn)榉N種原因,event bus 的成本反而比vuex還高, 所以技術(shù)選型上選用了 v...

    summerpxy 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<