JS學(xué)習(xí)筆記之變量及作用域(下)

相信大家在之前的學(xué)習(xí)中已經(jīng)掌握了基本類(lèi)型和引用類(lèi)型的相關(guān)概念;基本類(lèi)型 按值訪問(wèn),引用類(lèi)型按內(nèi)存地址訪問(wèn),以下是對(duì)變量及作用域的深入理解;

大家應(yīng)該知道語(yǔ)句 var n=1;這是一個(gè)創(chuàng)建變量并且為變量賦值的的語(yǔ)句;
咱們來(lái)詳細(xì)解剖下這個(gè)語(yǔ)句在解析器中會(huì)發(fā)生什么?

1. var n (創(chuàng)建變量名),

在解析器中創(chuàng)建了一個(gè)名字來(lái)保存他的變量同時(shí)設(shè)置了這個(gè)變量的作用域范圍(該變量所在的函數(shù)作用域范圍),并且無(wú)論在函數(shù)內(nèi)部哪里,在預(yù)處理的過(guò)程中都會(huì)將變量提升到頂部(既變量提升)

*變量提升

由于 JS是一門(mén)編譯語(yǔ)言,編譯時(shí)分為編譯過(guò)程和運(yùn)行過(guò)程,編譯過(guò)程是進(jìn)行詞法語(yǔ)法分析(既識(shí)別你所寫(xiě)的代碼,分辨出,語(yǔ)句,運(yùn)算符,數(shù)據(jù)類(lèi)型)如在這個(gè)過(guò)程中發(fā)現(xiàn)聲明語(yǔ)句var n ,解析器回去當(dāng)前作用域?qū)ふ沂欠裼衝 這個(gè)變量存在,如果有則忽略聲明,沒(méi)有則創(chuàng)建一個(gè)變量n ,并且為他分配一定內(nèi)存空間,當(dāng)編譯完成后會(huì)生成一段代碼(既后面要運(yùn)行的代碼)

var a=123;
function f(){
    alert(a);//undefined
    var a=1;
    alert(a);//1
}
f()

變量提升解析,解析器將函數(shù)作用域里面的var a=1;語(yǔ)句拆解,將變量聲明 var a提到函數(shù)作用域頂端;

var a=123;
function f(){
    var a;
    alert(a);//undefined
    a=1;
    alert(a);//1
}
f()

2. n = 1給變量賦值

賦的這個(gè)值分為引用類(lèi)型和基本類(lèi)型。解析器回先判斷這個(gè)值是引用類(lèi)型還是基本類(lèi)型。如果是引用類(lèi)型會(huì)把值保存到內(nèi)存中,而值的內(nèi)存地址保存在變量當(dāng)中。如果值是基本類(lèi)型,直接把值保存在變量中;

屬性的增刪及修改過(guò)程

當(dāng)n 為基本類(lèi)型時(shí),設(shè)置屬性不會(huì)報(bào)錯(cuò)但也沒(méi)有任何效果
當(dāng)n 為object,設(shè)置屬性a 當(dāng)中保存的就不是1了,而是定義對(duì)象的地址解析器去判斷a的時(shí)候通過(guò)地址到內(nèi)存去找對(duì)象,并且對(duì)對(duì)象的屬性/方法進(jìn)行增刪。

復(fù)制變量值

var a=123;
b=a;
b=3
alert(a)//123;
alert(b)//3         

在a中保存的值是123;將a 的值賦給b ,b就相當(dāng)于有了a 的副本,b的你死我活不關(guān)a的s事,a和b處于平行世界


var person1=new Object();
var person2=person1;
person1.name='alanshiyi';
alert(person2.name)

person2 復(fù)制了一份內(nèi)存地址, person2和person1保存的是一份相同的地址若person1.name是alanshiyi ,person2.name 也是 他倆是人和影子的關(guān)系;

var person1=new Object();
var person2=person1;
person1.name='alanshiyi';
person2.name='阿里巴巴'
alert(person2.name)
alert(person1.name)

當(dāng)修改person2時(shí)候,解析器會(huì)先找到person2發(fā)現(xiàn)他保存的是一個(gè)內(nèi)存地址就順著內(nèi)存地址找到內(nèi)存里的name 改成‘阿里巴巴’,而當(dāng)person1.name 要訪問(wèn)時(shí)解析器看person1里保存的地址,順著地址找到剛才修改的內(nèi)存‘阿里巴巴’;

傳遞參數(shù)

當(dāng)一個(gè)變量作為函數(shù)參數(shù)進(jìn)行傳遞時(shí)所有的參數(shù)都按值傳遞

接下來(lái)說(shuō)個(gè)大家都很迷茫的問(wèn)題
簡(jiǎn)單的說(shuō)就是,我們平常的參數(shù)傳值(參考書(shū)p70-p71中代碼) 如

function add(num){
 num+=10;
 return num;
}
var count=20;
result=add(conut);
alert(count)//20
alert(result)//30

num接受的參數(shù)是a這個(gè)基本類(lèi)型復(fù)制的副本10;相當(dāng)于前面說(shuō)的復(fù)制部分a=num;但是a和num是獨(dú)立的;


function setName(obj){
    obj.name='Nicholas';
}
var person=new Object();//全局變量
setName(person);
alert(person.name)//'Nicholas'

作為函數(shù)傳遞一個(gè)引用類(lèi)型的變量;函數(shù)外的是全局變量;它的作用域范圍是至少是函外層;而復(fù)制出來(lái)的這個(gè)參數(shù)他是以一個(gè)局部變量且作用范圍在函數(shù)內(nèi)部,和引用類(lèi)型復(fù)制一樣,雖然相互獨(dú)立但是他們當(dāng)中都保存同一個(gè)位置的引用,如果對(duì)參數(shù)進(jìn)行修改,內(nèi)存中的對(duì)象也會(huì)被修改。而再次通過(guò)函數(shù)外的訪問(wèn)時(shí),訪問(wèn)的還是內(nèi)存中的修改的變量;

雖然和引用類(lèi)型復(fù)制時(shí)的引用訪問(wèn)一樣,但這不能說(shuō)明引用類(lèi)型是按引用傳遞的;

課本上又給了一個(gè)例子


function setName(obj){
   obj.name='Nicholas';
   obj=new Object();//局部對(duì)象
   obj.name='Greg';
}
var person=new Object();//全局對(duì)象
setName(person);
alert(person.name)//'Nicholas'

這段代碼重新定義了一個(gè)對(duì)象obj , 并且給obj添加了不同的屬性,(按照引用傳遞的說(shuō)法obj當(dāng)修改obj.name;時(shí)候,解析器會(huì)先找到obj發(fā)現(xiàn)他保存的是一個(gè)內(nèi)存地址就順著內(nèi)存地址找到內(nèi)存里的name 改成‘Greg’,而當(dāng)person.name 要訪問(wèn)時(shí)解析器看obj里保存的地址,順著地址找到剛才修改的內(nèi)存‘Greg’ 才對(duì),但是person.name 返回是‘Nicholas’。
返回‘Nicholas’的原因:在函數(shù)內(nèi)部重新定義一個(gè)對(duì)象obj,就相當(dāng)于創(chuàng)建一個(gè)新的變量,而且還是一個(gè)局部變量,這個(gè)變量和外部的person是沒(méi)有任何關(guān)系的,全局對(duì)象無(wú)法訪問(wèn)局部對(duì)象,且局部對(duì)象在函數(shù)執(zhí)行完畢后即可銷(xiāo)毀。所以引用類(lèi)型的傳遞是按值來(lái)傳遞的。

變量的類(lèi)型檢測(cè)

typeof
區(qū)分變量類(lèi)型是:基本類(lèi)型還是對(duì)象(不能區(qū)分具體是什么對(duì)象)
instanceof 返回布爾值
語(yǔ)法:object instanceof 對(duì)象類(lèi)型

作用域和內(nèi)存

作用域:計(jì)算機(jī)對(duì)值進(jìn)行保存和讀取的規(guī)則;
當(dāng)對(duì)值進(jìn)行保存時(shí),肯定需要一個(gè)范圍,如果是這個(gè)范圍是以函數(shù)為單位的話(huà)就叫函數(shù)做用域,以塊為單位的話(huà)叫塊級(jí)作用域,單位越小,重復(fù)概率越小,作用域可以相互嵌套。

執(zhí)行環(huán)境和作用域

在本章開(kāi)頭提過(guò)變量提升的問(wèn)題,我再來(lái)重新捋一遍;
主要講的就是解析器在編譯時(shí)的情況;

1.打開(kāi)頁(yè)面加載JS時(shí),解析器會(huì)創(chuàng)建一個(gè)全局作用域(相當(dāng)于建房子);

2.變量的聲明,解析器發(fā)現(xiàn)有變量聲明,就會(huì)去當(dāng)前作用域找沒(méi)有相同的變量存在,沒(méi)有的話(huà)就創(chuàng)建這個(gè)變量,并為他分配一定的內(nèi)存空間且將聲明提到當(dāng)前作用域頂上(分配房間)。

3.如果編譯的過(guò)程中發(fā)現(xiàn)聲明的是一個(gè)函數(shù)(和變量的聲明差不多)但是它會(huì)建立一個(gè)新的作用域(相當(dāng)于分配一個(gè)房間再給它空間范圍)。這會(huì)出現(xiàn)一個(gè)新的問(wèn)題,如果變量名和函數(shù)名重名的而且在同一個(gè)作用于下,解析器就會(huì)在當(dāng)前作用域下覆蓋變量。

當(dāng)都編譯完成后會(huì)生成一段代碼。
接下來(lái)就是代碼運(yùn)行的部分

作用域鏈

4.代碼執(zhí)行
接上回 解析器在編譯完成時(shí)函數(shù)運(yùn)行過(guò)程中創(chuàng)建了作用域,也生成了所需執(zhí)行的代碼,這個(gè)代碼在運(yùn)行階段會(huì)創(chuàng)建作用域鏈,作用域鏈把開(kāi)始創(chuàng)建的作用域鏈接起來(lái)形成一個(gè)鏈條,對(duì)函數(shù)或比那輛進(jìn)行訪問(wèn)時(shí)要按照作用域鏈進(jìn)行依次的訪問(wèn)
訪問(wèn)方式 :先在當(dāng)前作用域找,找不到再?gòu)母讣?jí)一層一層往上找 ,直到全局作用域;

var color='blue';
function changeColor(){
    var anotherColor='red';
    function swapColor(){
        var tempColor=anotherColor;
        anotherColor=color;
        color=tempColor;
        //這里可以訪問(wèn)color,anothercolor和tempColor
    }
    //這里可以訪問(wèn)color,anotherColor,但不能訪問(wèn)tempColor和swapColor()
}
//這里只能訪問(wèn)color和changeColor()
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容