在javascript中存在著(zhù)this和scope兩個(gè)概念,如果不細心了解,還真搞不清楚這兩個(gè)概念,今天我們就來(lái)詳細了解一下this和scope的區別以及它們的作用,最后會(huì )附上code以加深理解。
一、作用域(scope)
所謂作用域就是:變量在聲明它們的函數體以及這個(gè)函數體嵌套的任意函數體內都是有定義的。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | function scope(){ var foo = "global"; if(window.getComputedStyle){ var a = "I'm if"; console.log("if:"+foo); //if:global } while(1){ var b = "I'm while"; console.log("while:"+foo);//while:global break; } !function (){ var c = "I'm function"; console.log("function:"+foo);//function:global }(); console.log( foo,//global a, // I'm if b, // I'm while c // c is not defined ); } scope(); |
(1)scope函數中定義的foo變量,除過(guò)自身可以訪(fǎng)問(wèn)以外,還可以在if語(yǔ)句、while語(yǔ)句和內嵌的匿名函數中訪(fǎng)問(wèn)。 因此,foo的作用域就是scope函數體。
(2)在javascript中,if、while、for 等代碼塊不能形成獨立的作用域。因此,javascript中沒(méi)有塊級作用域,只有函數作用域。
但是,在JS中有一種特殊情況:
如果一個(gè)變量沒(méi)有使用var聲明,window便擁有了該屬性,因此這個(gè)變量的作用域不屬于某一個(gè)函數體,而是window對象。
| 1 2 3 4 5 6 | function varscope(){ foo = "I'm in function"; console.log(foo);//I'm in function } varscope(); console.log(window.foo); //I'm in function |
二、作用域鏈(scope chain)
所謂作用域鏈就是:一個(gè)函數體中嵌套了多層函數體,并在不同的函數體中定義了同一變量, 當其中一個(gè)函數訪(fǎng)問(wèn)這個(gè)變量時(shí),便會(huì )形成一條作用域鏈(scope chain)。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | foo = "window"; function first(){ var foo = "first"; function second(){ var foo = "second"; console.log(foo); } function third(){ console.log(foo); } second(); //second third(); //first } first(); |
當執行second時(shí),JS引擎會(huì )將second的作用域放置鏈表的頭部,其次是first的作用域,最后是window對象,于是會(huì )形成如下作用域鏈:
second->first->window, 此時(shí),JS引擎沿著(zhù)該作用域鏈查找變量foo, 查到的是”second”
當執行third時(shí),third形成的作用域鏈:third->first->window, 因此查到的是:”frist”
特殊情況:with語(yǔ)句
JS中的with語(yǔ)句主要用來(lái)臨時(shí)擴展作用域鏈,將語(yǔ)句中的對象添加到作用域的頭部。with語(yǔ)句結束后,作用域鏈恢復正常。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | foo = "window"; function first(){ var foo = "first"; function second(){ var foo = "second"; console.log(foo); } function third(obj){ console.log(foo); //first with (obj){ console.log(foo); //obj } console.log(foo); //first } var obj = {foo:'obj'}; third(obj); } first(); |
在執行third()時(shí),傳遞了一個(gè)obj對象,obj中有屬性foo, 在執行with語(yǔ)句時(shí),JS引擎將obj放置在了原鏈表的頭部,于是形成的作用域鏈如下:
obj->third->first->window, 此時(shí)查找到的foo就是obj中的foo,因此輸出的是:”obj”, 而在with之前和之后,都是沿著(zhù)原來(lái)的鏈表進(jìn)行查找,從而說(shuō)明,在with語(yǔ)句結束后,作用域鏈已恢復正常。
三、this 關(guān)鍵字
在一個(gè)函數中,this總是指向當前函數的所有者對象,this總是在運行時(shí)才能確定其具體的指向, 也才能知道它的調用對象。
這句話(huà)總結了關(guān)于this的一切,切記,切記,切記!(ps:重要的事情說(shuō)三遍!)
| 1 2 3 4 5 6 7 8 | window.name = "window"; function f(){ console.log(this.name); } f();//window var obj = {name:'obj'}; f.call(obj); //obj |
在執行f()時(shí),此時(shí)f()的調用者是window對象,因此輸出”window”
f.call(obj) 是把f()放在obj對象上執行,相當于obj.f(),此時(shí)f中的this就是obj,所以輸出的是”obj”
四、實(shí)戰應用
code1:
| 1 2 3 4 5 6 7 8 9 10 11 | var foo = "window"; var obj = { foo : "obj", getFoo : function(){ return function(){ return this.foo; }; } }; var f = obj.getFoo(); f(); //window |
code2:
| 1 2 3 4 5 6 7 8 9 10 11 12 | var foo = "window"; var obj = { foo : "obj", getFoo : function(){ var that = this; return function(){ return that.foo; }; } }; var f = obj.getFoo(); f(); //obj |
code1和code2是對this和scope最好的總結,如果對于運行結果有疑惑,歡迎討論!
代碼解析:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 | code1: 執行var f = obj.getFoo()返回的是一個(gè)匿名函數,相當于: var f = function(){ return this.foo; } f() 相當于window.f(), 因此f中的this指向的是window對象,this.foo相當于window.foo, 所以f()返回"window" code2: 執行var f = obj.getFoo() 同樣返回匿名函數,即: var f = function(){ return that.foo; } 唯一不同的是f中的this變成了that, 要知道that是哪個(gè)對象之前,先確定f的作用域鏈:f->getFoo->window 并在該鏈條上查找that,此時(shí)可以發(fā)現that指代的是getFoo中的this, getFoo中的this指向其運行時(shí)的調用者,從var f = obj.getFoo() 可知此時(shí)this指向的是obj對象,因此that.foo 就相當于obj.foo,所以f()返回"obj" |
聯(lián)系客服