變量
之前看過(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)存中;
通過(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();
每個(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á)式和閉包放在下一篇吧