国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

深入理解 JavaScript, 從作用域與作用域鏈開始

frontoldman / 1896人閱讀

摘要:所以上面那段代碼鏈中最初應(yīng)該是之后之后所以最后的輸出結(jié)果是作用域鏈概念看了前面一個(gè)完整的函數(shù)執(zhí)行過程,讓我們來說下作用域鏈的概念吧。而這一條形成的鏈就是中的作用域鏈。

1. 什么是作用域

作用域是你的代碼在運(yùn)行時(shí),某些特定部分中的變量,函數(shù)和對(duì)象的可訪問性。換句話說,作用域決定了變量與函數(shù)的可訪問范圍,即作用域控制著變量與函數(shù)的可見性和生命周期

2. JavaScript中的作用域

在 JavaScript 中有兩種作用域

全局作用域

局部作用域

如果一個(gè)變量在函數(shù)外面或者大括號(hào){}外聲明,那么就定義了一個(gè)全局作用域,在ES6之前局部作用域只包含了函數(shù)作用域,ES6為我們提供的塊級(jí)作用域,也屬于局部作用域

2.1 全局作用域

擁有全局作用域的對(duì)象可以在代碼的任何地方訪問到, 在js中一般有以下幾種情形擁有全局作用域:

最外層的函數(shù)以及最外層變量:

var globleVariable= "global";  // 最外層變量
function globalFunc(){         // 最外層函數(shù)
    var childVariable = "global_child";  //函數(shù)內(nèi)變量
    function childFunc(){        // 內(nèi)層函數(shù)
        console.log(childVariable);
    }
    console.log(globleVariable)
}
console.log(globleVariable);  // global
globalFunc();                 // global
console.log(childVariable)   // childVariable is not defined
console.log(childFunc)       // childFunc is not defined

從上面代碼中可以看到globleVariableglobalFunc在任何地方都可以訪問到, 反之不具有全局作用域特性的變量只能在其作用域內(nèi)使用。

未定義直接賦值的變量(由于變量提升使之成為全局變量)

function func1(){
    special = "special_variable";
    var normal = "normal_variable";
}
func1();
console.log(special);    //special_variable
console.log(normal)     // normal is not defined

雖然我們可以在全局作用域中聲明函數(shù)以及變量, 使之成為全局變量, 但是不建議這么做,因?yàn)檫@可能會(huì)和其他的變量名沖突,一方面如果我們?cè)偈褂?b>const或者let聲明變量, 當(dāng)命名發(fā)生沖突時(shí)會(huì)報(bào)錯(cuò)。

// 變量沖突
var globleVariable = "person";
let globleVariable = "animal"; // Error, thing has already been declared

另一方面如果你使用var申明變量,第二個(gè)申明的同樣的變量將覆蓋前面的,這樣會(huì)使你的代碼很難調(diào)試。

var name = "koala"
var name = "xiaoxiao"
console.log(name);  // xiaoxiao
2.2 局部作用域

和全局作用于相反,局部作用域一般只能在固定代碼片段內(nèi)可以訪問到。最常見的就是函數(shù)作用域

2.2.1 函數(shù)作用域
定義在函數(shù)中的變量就在函數(shù)作用域中。并且函數(shù)在每次調(diào)用時(shí)都有一個(gè)不同的作用域。這意味著同名變量可以用在不同的函數(shù)中。因?yàn)檫@些變量綁定在不同的函數(shù)中,擁有不同作用域,彼此之間不能訪問。
//全局作用域
function test(){
    var num = 9;
    // 內(nèi)部可以訪問
    console.log("test中:"+num);
}
//test外部不能訪問
console.log("test外部:"+num);

注意點(diǎn):

如果在函數(shù)中定義變量時(shí),如果不添加var關(guān)鍵字,造成變量提升,這個(gè)變量成為一個(gè)全局變量。

function doSomeThing(){
    // 在工作中一定避免這樣寫
    thing = "writting";
    console.log("內(nèi)部:"+thing);
}
console.log("外部:"+thing)

任何一對(duì)花括號(hào){...}中的語句集都屬于一個(gè)塊, 在es6之前,在塊語句中定義的變量將保留在它已經(jīng)存在的作用域中:

var name = "程序員成長(zhǎng)指北";
for(var i=0; i<5; i++){
    console.log(i)
}
console.log("{}外部:"+i);
// 0 1 2 3 4  {}外部:5

我們可以看到變量name和變量i是同級(jí)作用域。

2.2.2 在ES6塊級(jí)作用域未講解之前注意點(diǎn)
變量提升

變量提升英文名字hoisting,MDN中對(duì)它的解釋是變量申明是在任意代碼執(zhí)行前處理的,在代碼區(qū)中任意地方申明變量和在最開始(最上面)的地方申明是一樣的。也就是說,看起來一個(gè)變量可以在申明之前被使用!這種行為就是所謂的“hoisting”,也就是變量提升,看起來就像變量的申明被自動(dòng)移動(dòng)到了函數(shù)或全局代碼的最頂上。
看一段代碼:

var tmp = new Date();
function f() {
    console.log(tmp);
    if(false) {
        var tmp="hello";
    }
}

這道題應(yīng)該很多小伙伴在面試中遇到過,有人會(huì)認(rèn)為輸出的是當(dāng)前日期。但是正確的結(jié)果是undefined。這就是由于變量提升造成的,在這里申明提升了,定義的內(nèi)容并不會(huì)提升,提升后對(duì)應(yīng)的代碼如下:

var tmp = new Date();
function f() {
    var tmp;
    console.log(tmp);
    if(false) {
        tmp="hello";
    }
}
f();

console在輸出的時(shí)候,tmp變量?jī)H僅申明了但未定義。所以輸出undefined。雖然能夠輸出,但是并不推薦這種寫法推薦的做法是在申明變量的時(shí)候,將所用的變量都寫在作用域(全局作用域或函數(shù)作用域)的最頂上,這樣代碼看起來就會(huì)更清晰,更容易看出來哪個(gè)變量是來自函數(shù)作用域的,哪個(gè)又是來自作用域鏈

重復(fù)聲明

看一個(gè)例子:

// var
var name = "koloa";
console.log(name); // koala
if(true){
    var name = "程序員成長(zhǎng)指北";
    console.log(name); // 程序員成長(zhǎng)指北
}
console.log(name); // 程序員成長(zhǎng)指北

雖然看起來里面name申明了兩次,但上面說了,js的var變量只有全局作用域和函數(shù)作用域兩種,且申明會(huì)被提升,因此實(shí)際上name只會(huì)在最頂上開始的地方申明一次,var name="程序員成長(zhǎng)指北"的申明會(huì)被忽略,僅用于賦值。也就是說上面的代碼實(shí)際上跟下面是一致的。

// var
var name = "koloa";
    console.log(name); // koala
if(true){
    name = "程序員成長(zhǎng)指北";
    console.log(name); // 程序員成長(zhǎng)指北
}
console.log(name); // 程序員成長(zhǎng)指北
變量和函數(shù)同時(shí)出現(xiàn)的提升

如果有函數(shù)和變量同時(shí)聲明了,會(huì)出現(xiàn)什么情況呢?看下面但代碼

console.log(foo);
var foo ="i am koala";
function foo(){}

輸出結(jié)果是function foo(){},也就是函數(shù)內(nèi)容

如果是另外一種形式呢?

console.log(foo);
var foo ="i am koala";
var foo=function (){}

輸出結(jié)果是undefined

對(duì)兩種結(jié)果進(jìn)行分析說明:

第一種:函數(shù)申明。就是上面第一種,function foo(){}這種形式

另一種:函數(shù)表達(dá)式。就是上面第二種,var foo=function(){}這種形式

第二種形式其實(shí)就是var變量的聲明定義,因此上面的第二種輸出結(jié)果為undefined應(yīng)該就能理解了。

而第一種函數(shù)申明的形式,在提升的時(shí)候,會(huì)被整個(gè)提升上去,包括函數(shù)定義的部分!因此第一種形式跟下面的這種方式是等價(jià)的!

var foo=function (){}
console.log(foo);
var foo ="i am koala";

原因是:

函數(shù)聲明被提升到最頂上;

申明只進(jìn)行一次,因此后面var foo="i am koala"的申明會(huì)被忽略。

函數(shù)申明的優(yōu)先級(jí)優(yōu)于變量申明,且函數(shù)聲明會(huì)連帶定義一起被提升(這里與變量不同)

接下來講,在ES6中引入的塊級(jí)作用域之后的事!

2.2.2 塊級(jí)作用域
ES6新增了letconst命令,可以用來創(chuàng)建塊級(jí)作用域變量,使用let命令聲明的變量只在let命令所在代碼塊內(nèi)有效。

let 聲明的語法與 var 的語法一致。你基本上可以用 let 來代替 var 進(jìn)行變量聲明,但會(huì)將變量的作用域限制在當(dāng)前代碼塊中。塊級(jí)作用域有以下幾個(gè)特點(diǎn):

變量不會(huì)提升到代碼塊頂部且不允許從外部訪問塊級(jí)作用域內(nèi)部變量

console.log(bar);//拋出`ReferenceErro`異常: 某變量 `is not defined`
let bar=2;
for (let i =0; i<10;i++){
    console.log(i)
}
console.log(i);//拋出`ReferenceErro`異常: 某變量 `is not defined`

其實(shí)這個(gè)特點(diǎn)帶來了許多好處,開發(fā)者需要檢查代碼時(shí)候,可以避免在作用域外意外但使用某些變量,而且保證了變量不會(huì)被混亂但復(fù)用,提升代碼的可維護(hù)性。就像代碼中的例子,一個(gè)只在for循環(huán)內(nèi)部使用的變量i不會(huì)再去污染整個(gè)作用域。

不允許反復(fù)聲明

ES6的letconst不允許反復(fù)聲明,與var不同

// var
function test(){
    var name = "koloa";
    var name = "程序員成長(zhǎng)指北";
    console.log(name); // 程序員成長(zhǎng)指北
}

// let || const
function test2(){
    var name ="koloa";
    let name= "程序員成長(zhǎng)指北"; 
    // Uncaught SyntaxError: Identifier "count" has already been declared
}

看到這里是不是感覺到了塊級(jí)作用域的出現(xiàn)還是很有必要的。

3. 作用域鏈

在講解作用域鏈之前先說一下,先了解一下 JavaScript是如何執(zhí)行的?

3.1 JavaScript是如何執(zhí)行的?


JavaScript代碼執(zhí)行分為兩個(gè)階段:

3.1.1 分析階段

javascript編譯器編譯完成,生成代碼后進(jìn)行分析

分析函數(shù)參數(shù)

分析變量聲明

分析函數(shù)聲明

分析階段的核心,在分析完成后(也就是接下來函數(shù)執(zhí)行階段的瞬間)會(huì)創(chuàng)建一個(gè)AO(Active Object 活動(dòng)對(duì)象)

3.1.2 執(zhí)行階段

分析階段分析成功后,會(huì)把給AO(Active Object 活動(dòng)對(duì)象)給執(zhí)行階段

引擎詢問作用域,作用域中是否有這個(gè)叫X的變量

如果作用域有X變量,引擎會(huì)使用這個(gè)變量

如果作用域中沒有,引擎會(huì)繼續(xù)尋找(向上層作用域),如果到了最后都沒有找到這個(gè)變量,引擎會(huì)拋出錯(cuò)誤。

執(zhí)行階段的核心就是,具體怎么,后面會(huì)講解LHS查詢RHS查詢

3.1.3 JavaScript執(zhí)行舉例說明

看一段代碼:

function a(age) {
    console.log(age);
    var age = 20
    console.log(age);
    function age() {
    }
    console.log(age);
}
a(18);
首先進(jìn)入分析階段

前面已經(jīng)提到了,函數(shù)運(yùn)行的瞬間,創(chuàng)建一個(gè)AO (Active Object 活動(dòng)對(duì)象)

AO = {}

第一步:分析函數(shù)參數(shù):

形式參數(shù):AO.age = undefined
實(shí)參:AO.age = 18

第二步,分析變量聲明:

// 第3行代碼有var age
// 但此前第一步中已有AO.age = 18, 有同名屬性,不做任何事
即AO.age = 18

第三步,分析函數(shù)聲明:

// 第5行代碼有函數(shù)age
// 則將function age(){}付給AO.age
AO.age = function age() {}

函數(shù)聲明注意點(diǎn):AO上如果有與函數(shù)名同名的屬性,則會(huì)被此函數(shù)覆蓋。但是一下面這種情況

var age = function () {
            console.log("25");
        }

聲明的函數(shù)并不會(huì)覆蓋AO鏈中同名的屬性

進(jìn)入執(zhí)行階段

分析階段分析成功后,會(huì)把給AO(Active Object 活動(dòng)對(duì)象)給執(zhí)行階段,引擎會(huì)詢問作用域,的過程。所以上面那段代碼AO鏈中最初應(yīng)該是

AO.age = function age() {}
//之后
AO.age=20
//之后
AO.age=20

所以最后的輸出結(jié)果是:

function age(){
    
}
20
20
3.2 作用域鏈概念

看了前面一個(gè)完整的javascript函數(shù)執(zhí)行過程,讓我們來說下作用域鏈的概念吧。JavaScript上每一個(gè)函數(shù)執(zhí)行時(shí),會(huì)先在自己創(chuàng)建的AO上找對(duì)應(yīng)屬性值。若找不到則往父函數(shù)的AO上找,再找不到則再上一層的AO,直到找到大boss:window(全局作用域)。 而這一條形成的“AO鏈” 就是JavaScript中的作用域鏈。

3.3 過程LHS和RHS查詢特殊說明

LHS,RHS 這兩個(gè)術(shù)語就是出現(xiàn)在引擎對(duì)變量進(jìn)行查詢的時(shí)候。在《你不知道的Javascript(上)》也有很清楚的描述。在這里,我想引用freecodecamp 上面的回答來解釋:

LHS = 變量賦值或?qū)懭雰?nèi)存。想象為將文本文件保存到硬盤中。 RHS = 變量查找或從內(nèi)存中讀取。想象為從硬盤打開文本文件。 Learning Javascript, LHS RHS
3.3.1 LHS和RHS特性

都會(huì)在所有作用域中查詢

嚴(yán)格模式下,找不到所需的變量時(shí),引擎都會(huì)拋出ReferenceError異常。

非嚴(yán)格模式下,LHR稍微比較特殊: 會(huì)自動(dòng)創(chuàng)建一個(gè)全局變量

查詢成功時(shí),如果對(duì)變量的值進(jìn)行不合理的操作,比如:對(duì)一個(gè)非函數(shù)類型的值進(jìn)行函數(shù)調(diào)用,引擎會(huì)拋出TypeError異常

3.3.2 LHS和RHS舉例說明

例子來自于《你不知道的Javascript(上)》

function foo(a) {
    var b = a;
    return a + b;
}
var c = foo( 2 );

直接看引擎在作用域這個(gè)過程:
LSH(寫入內(nèi)存):

c=, a=2(隱式變量分配), b=

RHS(讀取內(nèi)存)

讀foo(2), = a, a ,b
(return a + b 時(shí)需要查找a和b)
3.4 作用域鏈總結(jié)

最后對(duì)作用域鏈做一個(gè)總結(jié),引用《你不知道的Javascript(上)》中的一張圖解釋

今天就分享這么多,如果對(duì)分享的內(nèi)容感興趣,可以關(guān)注公眾號(hào)「程序員成長(zhǎng)指北」,或者加入技術(shù)交流群,大家一起討論。

文章同步到
程序員成長(zhǎng)指北(ID:coder_growth)
作者:koala 一個(gè)有趣的人

github博客地址:
https://github.com/koala-codi...

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/105684.html

相關(guān)文章

  • 函數(shù)作用域與閉包

    摘要:函數(shù)作用域要理解閉包,必須從理解函數(shù)被調(diào)用時(shí)都會(huì)發(fā)生什么入手。可以說,閉包是函數(shù)作用域的副產(chǎn)品。無論通過何種手段將內(nèi)部函數(shù)傳遞到所在的詞法作用域以外,它都會(huì)持有對(duì)原始定義作用域的引用,無論在何處執(zhí)行這個(gè)函數(shù)都會(huì)使用閉包。 函數(shù)作用域 要理解閉包,必須從理解函數(shù)被調(diào)用時(shí)都會(huì)發(fā)生什么入手。 我們知道,每個(gè)javascript函數(shù)都是一個(gè)對(duì)象,其中有一些屬性我們可以訪問到,有一些不可以訪問,...

    Code4App 評(píng)論0 收藏0
  • Javascript深入理解this作用域問題以及new/let/var/const對(duì)this作

    摘要:理解作用域高級(jí)程序設(shè)計(jì)中有說到對(duì)象是在運(yùn)行時(shí)基于函數(shù)的執(zhí)行環(huán)境綁定的在全局函數(shù)中,等于,而當(dāng)函數(shù)被作為某個(gè)對(duì)象調(diào)用時(shí),等于那個(gè)對(duì)象。指向與匿名函數(shù)沒有關(guān)系如果函數(shù)獨(dú)立調(diào)用,那么該函數(shù)內(nèi)部的,則指向。 理解this作用域 《javascript高級(jí)程序設(shè)計(jì)》中有說到: this對(duì)象是在運(yùn)行時(shí)基于函數(shù)的執(zhí)行環(huán)境綁定的:在全局函數(shù)中,this等于window,而當(dāng)函數(shù)被作為某個(gè)對(duì)象調(diào)用時(shí),t...

    snowLu 評(píng)論0 收藏0
  • JavaScript 作用域和作用域鏈學(xué)習(xí)

    摘要:作用域與作用域鏈每個(gè)函數(shù)都有自己的執(zhí)行環(huán)境。這是初步了解作用域,如想更深入了解作用域,請(qǐng)看下面鏈接作用域原理作用域鏈由一道題圖解的作用域或者看權(quán)威指南和高級(jí)程序設(shè)計(jì) 本文是我學(xué)習(xí)JavaScript作用域整理的筆記,如有不對(duì),請(qǐng)多指出。 作用域 一個(gè)變量的作用域是程序源代碼中定義這個(gè)變量的區(qū)域。 而在ES5中只分為全局作用域和函數(shù)作用域,也就是說for,if,while等語句是不會(huì)創(chuàng)建...

    史占廣 評(píng)論0 收藏0
  • 【7】JavaScript 函數(shù)高級(jí)——作用域與作用域鏈

    摘要:函數(shù)高級(jí)作用域與作用域鏈一作用域作用域個(gè)數(shù)定義的函數(shù)個(gè)數(shù)全局作用域理解就是一塊地盤一個(gè)代碼段所在的區(qū)域。函數(shù)執(zhí)行上下文環(huán)境是在調(diào)用函數(shù)時(shí)函數(shù)體代碼執(zhí)行之前創(chuàng)建。 JavaScript函數(shù)高級(jí)——作用域與作用域鏈 一、作用域 作用域個(gè)數(shù) = n(定義的函數(shù)個(gè)數(shù)) + 1(全局作用域)(1)理解 就是一塊地盤, 一個(gè)代碼段所在的區(qū)域。 它是靜態(tài)的(相對(duì)于上下文對(duì)象), 在編寫代碼時(shí)就確定...

    lentrue 評(píng)論0 收藏0
  • 前端基礎(chǔ)進(jìn)階(四):詳細(xì)圖解作用域鏈與閉包

    摘要:之前一篇文章我們?cè)敿?xì)說明了變量對(duì)象,而這里,我們將詳細(xì)說明作用域鏈。而的作用域鏈,則同時(shí)包含了這三個(gè)變量對(duì)象,所以的執(zhí)行上下文可如下表示。下圖展示了閉包的作用域鏈。其中為當(dāng)前的函數(shù)調(diào)用棧,為當(dāng)前正在被執(zhí)行的函數(shù)的作用域鏈,為當(dāng)前的局部變量。 showImg(https://segmentfault.com/img/remote/1460000008329355);初學(xué)JavaScrip...

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

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

0條評(píng)論

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