轉行前端自學也有5個多月了,感覺JS學的不明不白像吻,斷斷續(xù)續(xù)峻黍,沒有標準复隆,還是輸出點內(nèi)容比較靠譜。
以下是對變量及作用域章節(jié)的理解姆涩。
錯漏之處還請批評指正昏名。
與諸君共勉!
一阵面、基本類型和引用類型
javascript的變量與其他語言的變量有很大的區(qū)別轻局。javascript變量是松散型的(不強制類型),決定了她只是在特定的時間用于保存何種數(shù)據(jù)類型值的規(guī)則样刷,變量的值及其數(shù)據(jù)類型可以在腳本的生命周期內(nèi)改變仑扑。
在此之前先了解棧,堆置鼻,隊列的存儲方式
基本類型值:保存在棧內(nèi)存中的簡單數(shù)據(jù)段镇饮,這種值完全保存在內(nèi)存中的一個位置。
引用類型值:保存在堆內(nèi)存中的對象箕母,意思是變量中保存的是一個指針储藐,這個指針指向內(nèi)存中的另一個位置,該位置保存對象嘶是。
先上圖
將一個值賦給變量時钙勃,解析器必須確定這個值是基本類型值,還是引用類型值聂喇。
基本類 型值有以下幾種:Undefined辖源、Null、Boolean希太、Number 和 String克饶。這些類型在內(nèi)存中分別占 有固定大小的空間,他們的值保存在椞芑裕空間矾湃,我們通過按值來訪問的。
PS:在某些語言中堕澄,字符串以對象的形式來表示邀跃,因此被認為是引用類型。
ECMAScript 放棄這一傳統(tǒng)奈偏。 如果賦值的是引用類型的值坞嘀,則必須在堆內(nèi)存中為這個值分配空間。由于這種值的大小 不固定(比如Array )惊来,因此不能把它們保存到棧內(nèi)存中丽涩。但內(nèi)存地址大小的固定的,因此可以將內(nèi)存地址 保存在棧內(nèi)存中。這樣矢渊,當查詢引用類型的變量時继准,先從棧中讀取內(nèi)存地址,然后再通過地 址找到堆中的值矮男。對于這種移必,我們把它叫做按引用訪問。
二毡鉴、動態(tài)屬性
定義基本類型值和引用類型值的方式是相似的:創(chuàng)建一個變量并為該變量賦值崔泵。但是, 當這個值保存到變量中以后猪瞬,對不同類型值可以執(zhí)行的操作則大相徑庭憎瘸。
PS:動態(tài)屬性對于引用類型可以添加屬性和方法,也可以刪除和改變其屬性陈瘦,但基本類型不可以幌甘。
var person=new Object(); //創(chuàng)建引用類型
box.name='Nicholas'; //新增一個屬性
alert(person.name); //輸出'Nicholas
如果是基本類型的值添加屬性的話,就會出現(xiàn)問題了痊项。
var name='Nicholas'; //創(chuàng)建一個基本類型
name.age=27 ; //給基本類型添加屬性
alert(name.age); //undefined (不是引用類型無法輸出)
三锅风、復制變量值
除了保存方式不同外,在變量的復制方面鞍泉,基本類型和引用類型也有所不同吓揪。基本類型復制的是值本身廓啊,而引用類型復制的是地址控漠。
如果從一個變量向另一個變量復制基本類型的值 會在變量對象中創(chuàng)建一個新的值梧田,然后把該值復制到為新變量分配的位置上乔宿。
例如
var num1=5; //在棧內(nèi)生成一個num1 5
var num2=num1;//在棧內(nèi)生成num2 5
num2='a' ;
alert(num2); //a
alert(num1) ; //5
num2 只是num1的副本贺嫂,在沒有給num2賦值時num1=num2;給num2賦值后num2 不會影響num1的值(就像復印東西一樣乡小,打印出來的兩份東西都是獨立翅溺,雖然開始全都一樣肮砾,但之后他們互不關聯(lián)诀黍,若其中的一張被丟了還是被亂畫都和另一張沒關系);
var obj1=new Object(); //創(chuàng)建一個對象
var obj2=obj1;//把引用地址賦值給obj2
obj1.name='Nicholas';//新增一個屬性
alert(obj2.name)//Nicholas
alert(obj.name)//Nicholas
在引用類型中仗处,obj2其實就是 obj1眯勾,因為他們指向的是同一個對象。如果這個對象中的 name 屬性被修改了婆誓,obj2.name 和 obj1.name 輸出的值都會被相應修改掉了吃环。
var obj1=new Object(); //創(chuàng)建一個對象
var obj2=obj1;//把引用地址賦值給obj2
obj1.name='Nicholas';//新增一個屬性
obj2.name='aaa';//新增另一個屬性
alert(obj2.name)//aaa
alert(obj1.name)//aaa
在引用類型中,obj2其實就是 obj1洋幻,因為他們指向的是同一個對象郁轻。如果這個對象中的 name 屬性被修改了,obj2.name 和 obj1.name 輸出的值都會被相應修改掉了。
四好唯、傳遞參數(shù)
ECMAScript 中所有函數(shù)的參數(shù)都是按值傳遞的竭沫,言下之意就是說,參數(shù)不會按引用傳 遞骑篙,雖然變量有基本類型和引用類型之分
function q(num) { //按值傳遞蜕提,傳遞的參數(shù)是基本類型
num+= 10; //這里的 num 是局部變量,全局無效
return num; }
var count=20;
var result=box(num);
alert(result); //30
alert(num); //20
PS:以上的代碼中靶端,傳遞的參數(shù)是一個基本類型的值谎势。而函數(shù)里的 num 是一個局部變 量,和外面的 num 沒有任何聯(lián)系杨名。
下面給出一個參數(shù)作為引用類型的例子它浅。
function box(obj) { //按值傳遞,傳遞的參數(shù)是引用類型 镣煮,但不是按引用傳遞,是傳遞引用類型
obj.name='aaa';
}
var p=new Object();
box(p);
alert(p.name);//aaa
PS:這個有點難理解姐霍,按值傳遞,傳遞的參數(shù)是引用類型 典唇,但不是按引用傳遞镊折,是傳遞引用類型。如果存在按引用傳遞的話介衔,那么函數(shù)里的那個變量將會是全局變量恨胚,在外部也可 以訪問。(比如 PHP 中炎咖,必須在參數(shù)前面加上&符號表示按引用傳遞赃泡。而 ECMAScript 沒有這 些,只能是局部變量乘盼∩埽可以在 PHP 中了解一下。)
PS:所以按引用傳遞和傳遞引用類型是兩個不同的概念绸栅。
function box(obj) {
obj.name='aaa';
var obj=new Object();//函數(shù)內(nèi)部又創(chuàng)建了一個對象
obj.name='Mr.'; //并沒有替換掉原來的 obj
}
最后得出結論级野,ECMAScript 函數(shù)的參數(shù)都將是局部變量,也就是說粹胯,沒有按引用傳遞蓖柔。
五、檢測類型
要檢測一個變量的類型风纠,我們可以通過 typeOf 運算符來判別况鸣。
例如:
var box='aaa';
alert(typeof box); //string
雖然 typeof 運算符在檢查基本數(shù)據(jù)類型的時候非常好用,但檢測引用類型的時候竹观,它就 不是那么好用了镐捧。通常,我們并不想知道它是不是對象,而是想知道它到底是什么類型的對 象愤估。
因為數(shù)組也是 object帮辟,null 也是 Object 等等。 這時我們應該采用 instanceof 運算符來查看玩焰。
var box=[1,2,3];
alert(box instanceof Array); //是否是數(shù)組
var box2= {};
alert(box2 instanceof Object); //是否是對象
var box3= /g/;
alert(box3 instanceof RegExp); //是否是正則表達式
var box4= new String('aaa');
alert(box4 instanceof String); //是否是字符串對象
PS:當使用 instanceof 檢查基本類型的值時由驹,它會返回 false。
詳細了解請參考《JavaScript高級程序設計》4.1 p68