這一次我們首先將視角投向js基礎(chǔ)知識之變量,下面看下變量的基礎(chǔ)信息
姓名:變量
類型:松散型
定義方式:var,const,let或者直接變量名
常見問題:變量覆蓋考传,變量提升,變量作用域
我們從上面的基礎(chǔ)信息來一點一點的剖析變量這士兵甲
1:變量的類型
var num = 14;//這個時候變量num的類型為數(shù)字
console.log(typeof num);//查看num的類型為number
num = '改成字符串';//這個時候變量num的類型為字符串
console.log(typeof num);//查看num的類型string
js的變量的松散類型(弱類型)其實是相對于java等其他語言的強(qiáng)類型來說的戳葵,強(qiáng)語言類型在定義變量的時候需要指定變量的類型筋量,如果試圖去修改變量類型的話就會報錯植袍。
2:變量定義的方法
2.1 var
在ES5的時候我們定義變量的方法是通過var關(guān)鍵字+變量名的方式定義一個變量筋遭,例如:
var name;//定義了變量name
當(dāng)然也可以直接通過變量名的方法來定義一個全局變量搓萧,不過不是很推薦這種做法,例如:
name;//定義了一個全局變量
不過隨著時光的飛逝宛畦,規(guī)則也在悄悄的發(fā)生了變化,出現(xiàn)了ES6語法揍移,在ES6語法中出現(xiàn)了通過let,和const來定義變量的方法次和,首先我們來看const
2.2 const
const是定義常量的方法,定義的時候必須初始化那伐,且定義后不可以修改踏施。常量通常是大寫的方式,并且通過const定義的常量是不允許被修改的罕邀,如果嘗試修改會報錯畅形,例如:
const NAME;
NAME = '來瓶二鍋頭';
將上面代碼復(fù)制到瀏覽器控制臺運(yùn)行,會直接報錯
同樣的下面也會報錯
const PI = 3.14;
console.log(PI);
PI= 3.1415;
那么我們設(shè)想一下诉探,如果我們將常量定義為一個對象日熬,嘗試修改對象屬性的話會發(fā)生什么情況呢?心動不如行動肾胯。
const USER = {
name:'來瓶二鍋頭'
};
console.log(USER);
USER.name="再來瓶二鍋頭";
console.log(USER);
這個時候我覺得下面這個圖片挺適合的
上面我們已經(jīng)說過了const定義的是一個常量竖席,常量是不允許修改的,修改的話就會報錯敬肚,但是為什么修改對象屬性就可以的呢毕荐?這里就需要從原理來說了,每次聽到原理這個詞的時候都覺得好高大上艳馒,這就跟我們看到張無忌使用乾坤大羅移在光明頂大戰(zhàn)六大派的時候一樣憎亚,哇塞,乾坤大挪移好膩害啊弄慰。但是為什么厲害呢第美?不知道,反正就是厲害陆爽。我們今天就要去好好剖析一下乾坤大挪移為啥子那么膩害斋日,不對是const為什么可以修改對象的屬性。其實這里為什么能夠修改對象類型需要引入兩個概念(真煩墓陈,一個東西說不清楚非要再來一個東西恶守,嘿嘿):引用類型和基本類型第献。我們下面先來說明下這兩個概念,等著兩個概念弄通了兔港,你就會了乾坤大挪移了庸毫,咳咳,滾犢子衫樊,是理解了為什么可以修改對象屬性
2.2.1 基本類型
基本類型指的是簡單的數(shù)據(jù)段飒赃,按照數(shù)據(jù)類型(這玩意后面介紹)來說就是基本的數(shù)據(jù)類型都是基本類型,包括undefined,Null,Boolean,Number,String科侈。這些類型的值都是按值訪問的载佳,操作的話就是實際的值。什么叫操作的是實際的值呢臀栈?那么我們看下一下的例子:
var num1 = 5;
console.log('num1的值為:' + num1);
var num2 = num1;
console.log('num2的值為:' + num2);
num1 = 10;
console.log('修改后num1的值為:' + num1);
console.log('num2的值為:' + num2);
我們把代碼復(fù)制到控制臺蔫慧,查看運(yùn)行結(jié)果如下:
我們可以看到將num1的值賦值給num2,相當(dāng)于直接將5賦值過去,修改num1的值不會造成num2值得改變权薯。那么我們就可以得到以下一個結(jié)論:操作基本類型的數(shù)據(jù)的時候?qū)嶋H上操作的就是實際的值姑躲。
2.2.2 引用類型
引用類型指的是多個值構(gòu)成的對象。按照數(shù)據(jù)類型來劃分的那就是Object類型(數(shù)組也是object類型的數(shù)據(jù)哦)的數(shù)據(jù)盟蚣。那么我們操作引用類型的值的時候是實際的值嗎黍析?我們看下一下例子:
var obj1 = {
name:'二鍋頭'
};
console.log('obj1的數(shù)據(jù)為' + JSON.stringify(obj1));
var obj2 = obj1;
console.log('obj2的數(shù)據(jù)為' + JSON.stringify(obj2));
obj1.name='我愛二鍋頭';
console.log('修改后obj1的數(shù)據(jù)為' + JSON.stringify(obj1));
console.log('修改后obj2的數(shù)據(jù)為' + JSON.stringify(obj2));
復(fù)制到控制臺中看結(jié)果為
這個時候腦海里肯定會有以下的表情
這個就要說到引用類型變量值的問題了。按照js規(guī)定屎开,js不允許直接訪問內(nèi)存中的位置阐枣,而Object類型的數(shù)據(jù)是放在內(nèi)存中的,所以我們這個時候定義的引用類型的變量是一個指針奄抽,指向改對象所在內(nèi)存的位置侮繁。舉個例子吧,就是我們都有銀行卡如孝,銀行卡里有自己的錢宪哩,我們可以直接從銀行卡里把錢取出來嗎?肯定不能(違法行為不在考慮范圍內(nèi)哈)第晰,我們需要去銀行锁孟,然后取出我們的錢,我們能操作的只是我們的銀行卡茁瘦。而銀行卡就是一個中介品抽,一個指向我們錢的中介。我們可以用一下圖來表示上面寫的obj1和obj2的例子
因此當(dāng)我們復(fù)制一個引用類型數(shù)據(jù)賦值給另一個變量的時候其實只是創(chuàng)建了一個副本甜熔,這個副本就是一個指針圆恤。指向堆棧內(nèi)存中的一個對象。
到此為止我們搞懂了什么是引用類型什么是基本類型腔稀,那么我們回歸到我們的主體中盆昙,為什么const定義的對象類型的常量可以修改羽历。答案就是對象類型的常量是引用類型的,此時我們定義的常量其實就是一個指針淡喜,指向堆棧內(nèi)存中一個對象的指針秕磷,這個指針是不能修改的,但是堆棧內(nèi)存中的變量可以隨便變炼团。驚不驚喜意不意外澎嚣。哇咔咔。那么我們看下面的例子:
const OBJ1 = {
name:'二鍋頭'
};
console.log('OBJ1的數(shù)據(jù)為:' + JSON.stringify(OBJ1));
OBJ1 = {
name:'我愛二鍋頭'
};
復(fù)制到控制臺中看結(jié)果
哇咔咔瘟芝。收功易桃。
2.3 let
在ES5時代沒有塊級作用域這一說(又來了一作用域的概率,嘿嘿嘿锌俱,后面專門介紹這一老哥)晤郑,在ES5那個時代只有全局作用域和函數(shù)內(nèi)作用域,所以在ES6的時候引入了塊級作用域嚼鹉。通過let來定義變量,定義的就是一塊級作用域驱富,我們來看以下的例子:
if(true){
let num = 5;
console.log('num的值為:' + num);
}
console.log('num的值為:' + num);
復(fù)制到控制臺來看下結(jié)果
這個時候定義的變量num的作用域為塊級作用域锚赤,只在if代碼塊中起作用,在外部就不起作用了褐鸥,我們來對比下var定義變量线脚,同樣的方式,小二叫榕,上代碼
if(true){
var num = 5;
console.log('num的值為:' + num);
}
console.log('num的值為:' + num);
復(fù)制到控制臺看結(jié)果
看到不同了吧浑侥,那么通過let就能解決一個很老套的問題了,小二上問題,以下程序運(yùn)行出現(xiàn)的結(jié)果是什么晰绎?
for(var i = 0;i<5;i++){
setTimeout(function(){
console.log(i);
},100);
}
結(jié)果是什么寓落?輸出5個5,為什么呢荞下?具體原因涉及到宏任何和微任務(wù)伶选,后面單獨(dú)介紹(嘿嘿嘿,又埋坑了)那么怎么解決呢尖昏?答案很顯然仰税,用let來定義變量就可(這是最簡單的,當(dāng)然還有其他方法抽诉,后面別的章節(jié)會說到)陨簇,上代碼
for(let i = 0;i<5;i++){
setTimeout(function(){
console.log(i);
},100);
}
答案是什么呢?自己復(fù)制到控制臺就能看到結(jié)果了
這個時候關(guān)于變量定義的相關(guān)問題都已經(jīng)結(jié)束了迹淌。