重拾ECMAScript基礎(chǔ)——變量、作用域

變量

之前看過(guò)一遍書,對(duì)ES中的參數(shù)傳遞,作用域鏈,無(wú)塊級(jí)作用域,閉包等有了一定的了解,但是似懂非懂,今天把整個(gè)過(guò)程用代碼演示出來(lái),加深理解。

  • 變量名
    學(xué)過(guò)匯編和C我們知道,像C/C++這種編譯成機(jī)器代碼的語(yǔ)言,變量名是不需要存儲(chǔ)在內(nèi)存中的,在代碼執(zhí)行時(shí)都編譯為(棧地址+偏移地址)的形式,變量名只是編程時(shí)提供給編譯器識(shí)別的一個(gè)名字,比如int n = 5;在機(jī)器代碼中是不存在n的,只對(duì)內(nèi)存中5的地址產(chǎn)生類似于mov [0x00410FC0],5的操作;
  • 指針
    指針,int *p = &n;,p這個(gè)指針變量保存著n對(duì)應(yīng)的內(nèi)存空間的地址,即它的值為0x00410FC0,而p這個(gè)變量名,也不存在于內(nèi)存和機(jī)器代碼中;
  • 引用
    C++中引入引用的概念,除了需要初始化,且只能初始化一次,其實(shí)它和變量名沒(méi)有區(qū)別,它就是一個(gè)別名;

提到前面這些概念,就是為了更好的理解javascript中的變量機(jī)制,JS是弱類型動(dòng)態(tài)語(yǔ)言(關(guān)于強(qiáng)、弱,靜態(tài)、動(dòng)態(tài)類型請(qǐng)參考 https://www.zhihu.com/question/19918532 ),是一種解釋性語(yǔ)言,同時(shí)是一種腳本語(yǔ)言(編程性語(yǔ)言、解釋性語(yǔ)言、腳本語(yǔ)言請(qǐng)參考 http://blog.csdn.net/mooncom/article/details/60955411 )。

在計(jì)算機(jī)科學(xué)中,指針(Pointer)是編程語(yǔ)言中的一個(gè)對(duì)象,利用地址,它的值直接指向(points to)存在電腦存儲(chǔ)器中另一個(gè)地方的值。由于通過(guò)地址能找到所需的變量單元,可以說(shuō),地址指向該變量單元。因此,將地址形象化的稱為“指針”。意思是通過(guò)它能找到以它為地址的內(nèi)存單元。——百度百科

JS與C/C++是不同的,JS中保存基本類型的值和標(biāo)識(shí)符在棧區(qū),而引用類型的標(biāo)識(shí)符和地址保存在棧區(qū),值保存在堆內(nèi)存中;

存儲(chǔ).png

通過(guò)變量名操作引用類型稱為引用,這里的引用和C/C++中的引用不是同一個(gè)概念,因此催生出“指針”的概念,操作在棧區(qū)的地址,也可以叫做操作指針,或是堆內(nèi)存地址。

//C++中指針存放地址
    int n = 5;
    int *p = &n;
    int *q = p;
    cout<<p<<"\t"<<q<<endl;//0x6ffe2c 0x6ffe2c 
    cout<<*p<<"\t"<<*q<<endl;//5 5 
//變量
        function sum1 () {
        }
        var sum2 = sum1;
        console.log(sum2.name);//'sum1'
        console.log(typeof sum1);//'function'
        sum1 = null;
        console.log(typeof sum1);//'null'
        console.log(sum2.name);//'sum1'

保存在棧區(qū)或堆內(nèi)存可以是基本類型的值,也可以是引用類型的值,基本類型的值與引用類型的值的區(qū)別:

  • 1.動(dòng)態(tài)屬性
    引用類型的值可以動(dòng)態(tài)的改變其屬性,因?yàn)樗菍?duì)象;而基本類型的值不可以;
//動(dòng)態(tài)屬性
        var person = new Object();
        person.name = 'Matthew';
        console.log(person.name);//'Matthew'

        var name = 'Matthew';
        name.age = 27;
        console.log(name.age);//undefined
  • 2.復(fù)制變量值
    基本類型的復(fù)制是把值復(fù)制到新變量分配的內(nèi)存空間中,兩個(gè)變量完全獨(dú)立;
//變量賦值
        var num1 = 5;
        var num2 = num1;
        num1 = 4;
        console.log(num1 + '\t' + num2);//4 5

我們都知道變量是類似于引用的方式來(lái)映射的,《JS高程》中把它解釋為指針的復(fù)制,實(shí)際上指的是傳遞一個(gè)引用,即傳遞棧區(qū)標(biāo)識(shí)符對(duì)應(yīng)的那個(gè)地址,和參數(shù)中傳遞基本類型和引用類型是一樣的,都是按值傳遞,只不過(guò)前者是傳遞真值,后者是傳遞引用(地址)。

        var obj1 = new Object();
        var obj2 = obj1;
        obj1.name = 'Matthew';
        console.log(obj2.name);//'Matthew'
  • 3.傳遞參數(shù)
    上面也講了,JS中參數(shù)傳遞都是按值傳遞,基本類型很好理解,參數(shù)是真值;
//參數(shù)傳遞
        function add(num) {
            num++;
            return num;
        }

        var count = 1;
        var result = add(count);
        console.log(count);//1 沒(méi)有變化
        console.log(result);//2

而參數(shù)為引用類型時(shí),傳遞的是一個(gè)引用,上面講到,引用類型在棧區(qū)的標(biāo)識(shí)符對(duì)應(yīng)的地址,可以理解為一個(gè)指針,那么這里可以解釋為傳遞的是這個(gè)指針的副本,即這個(gè)地址,所以也是值傳遞,我們看代碼:

        function setName(obj) {
            obj.name = 'Matthew';
            obj = new Object();
            obj.name = 'Alex';
        }

        var person = new Object();
        setName(person);
        console.log(person.name);//'Matthew'

如果是按引用傳遞,結(jié)果應(yīng)該顯示為'Alex';
什么是引用傳遞?,在JS中是不存在引用傳遞的,在C++中,通過(guò)取地址符,取到變量地址,使得形參的地址與實(shí)參的相同,改變形參也就改變了實(shí)參,所以通常用來(lái)通過(guò)函數(shù)內(nèi)部修改改變外部環(huán)境,看代碼:

void swap(int &a,int &b)
{
     int temp;
     temp=a;
     a=b;
     b=temp;
     cout<<a<<""<<b<<"\n";
}
int main() 
{
    int x=1;
    int y=2;
    swap(x,y);
    cout<<x<<""<<y<<"\n";//21 21
    return 0;
} 

形參a,b作為局部變量在棧區(qū)開(kāi)辟了內(nèi)存空間,存放的是實(shí)參變量的地址;如此,才是引用傳遞;

作用域

  • 當(dāng)程序開(kāi)始執(zhí)行時(shí),解析器(編譯器)會(huì)創(chuàng)建一個(gè)全局執(zhí)行環(huán)境,又稱為執(zhí)行上下文,在web瀏覽器中,這個(gè)環(huán)境就是window對(duì)象,以后執(zhí)行流每進(jìn)入一個(gè)函數(shù),函數(shù)的環(huán)境的都會(huì)被推入這個(gè)環(huán)境棧中,在函數(shù)執(zhí)行完后,棧將其環(huán)境推彈出,把控制權(quán)交給之前的執(zhí)行環(huán)境,ECMAScript的執(zhí)行流正是由這個(gè)方便的機(jī)制控制的;
    每個(gè)執(zhí)行環(huán)境都會(huì)保存一個(gè)變量對(duì)象和一個(gè)作用域鏈,變量對(duì)象就是上文提到的標(biāo)識(shí)符和值或標(biāo)識(shí)符和地址(基本類型或引用類型);作用域鏈的前端就是當(dāng)前環(huán)境的變量對(duì)象,下一個(gè)對(duì)象就是下一個(gè)包含(外部)環(huán)境的變量對(duì)象,以此類推,直到全局環(huán)境的變量對(duì)象;
//作用域
        var color = 'blue';

        function changeColor() {
            var anotherColor = 'red';

            function swapColors() {
                var tempColor = anotherColor;
                anotherColor = color;
                color = tempColor;
                //這里可以訪問(wèn)color,anotherColor和tempColor
            }
            //這里可以訪問(wèn)color和anotherColor,但不能訪問(wèn)tempColor
            swapColors();
        }
        //這里只能訪問(wèn)color
        changeColor();
作用域鏈.png

每個(gè)矩形代表一個(gè)執(zhí)行環(huán)境,作用域鏈?zhǔn)且枣湵淼男问竭B接的,只能向上訪問(wèn),不能向下訪問(wèn);

  • 沒(méi)有塊級(jí)作用域
    1.Javascript中沒(méi)有塊級(jí)作用域:
//沒(méi)有塊級(jí)作用域
        for (var i = 0;i < 10;i++) {

        }

        console.log(i);//10

        if(true) {
            var color = 'blue';
        }

        console.log(color);//'blue'

2.聲明變量
未使用var 關(guān)鍵字聲明的是全局變量,及時(shí)在函數(shù)內(nèi)部;
3.查詢標(biāo)識(shí)符
從作用域鏈前端向上逐級(jí)查詢,直到全局環(huán)境;

函數(shù)表達(dá)式和閉包放在下一篇吧

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容