變量是每一門編程語言中非常重要的一個概念螟够,不同的編程語言中變量的作用也基本一致灾梦。變量可以通過變量名訪問,是計算機語言中能儲存計算結(jié)果或能表示值的抽象概念妓笙。
JavaScript變量
JavaScript屬于弱類型定義語言若河,某一個變量被定義類型,該變量可以根據(jù)環(huán)境變化自動進行轉(zhuǎn)換寞宫,不需要經(jīng)過顯性強制轉(zhuǎn)換萧福。
//比如
var a = 1;
a = {x: 1};
console.log(a); //{x:1}
但是像Java、.net辈赋、Python鲫忍、c++等這樣的強類型定義語言,上述的操作就會引起程序報錯钥屈。
JavaScript變量聲明(創(chuàng)建)
JavaScript變量命名需要遵循的規(guī)則
- 變量必須以字母開頭
- 變量也能以 $ 和 _ 符號開頭(不過我們不推薦這么做)
- 變量名稱對大小寫敏感(y 和 Y 是不同的變量)
JavaScript變量聲明的幾種方式
- es6之前使用var和function進行變量聲明
- es6中新增let和const
function也是變量的聲明方式嗎悟民?
我們來驗證一下function是否可以聲明變量。
let add = 1;
function add(num1, num2) {return num1 + num2};
//Uncaught SyntaxError: Identifier 'add' has already been declared
思考一下使用var篷就,function射亏,let,const聲明變量有什么區(qū)別竭业?
在這之前我們要知道在JavaScript中變量聲明和賦值(初始化)對于Js引擎來說是分為了兩個階段智润。一個Js的變量初始化語句也將被解釋成兩個語句來執(zhí)行,比如
var a = 1;
//被Js引擎解釋為
var a; //變量聲明語句
a = 1; //變量賦值語句
我們知道了這個過程后繼續(xù)探索JavaScript變量操作相關(guān)的特性永品。
-
變量聲明提升
- var 和 function有變量聲明提升的功能
- let 和 const 則沒有變量聲明提升的功能做鹰,必須要先聲明才能使用
//驗證變量提升
//1. 如果不進行聲明直接使用會報錯
console.log(a); //Uncaught ReferenceError: a is not defined
//2. 先使用后聲明
console.log(a); //undefined
var a = 1;
//對于js引擎來說和下面的情況是一樣的
console.log(a); //undefined
var a;
//或者
var b;
console.log(b); //undefined
//3. 先聲明并賦值后使用
var a = 1;
console.log(a); // 正常輸出 1
//4. 使用function聲明函數(shù)變量
add(1, 2); // 正常輸出 3
function add(num1, num2) {
console.log(num1 + num2);
}
//5. 使用let
console.log(a); //Uncaught ReferenceError: a is not defined
let a = 1;
//6. 使用const
console.log(a); //Uncaught ReferenceError: a is not defined
const a = 1;
從上面幾個例子中可以看出,如果在程序中未使用var和function對一個變量進行聲明直接使用則會報錯鼎姐;如果聲明了變量但未賦值或者初始化,則該變量的默認值為undefined更振;如果變量賦值或者初始化在使用之后炕桨,則在使用變量時其值也為undefined。使用var和function聲明的變量在當前執(zhí)行環(huán)境下肯腕,即使調(diào)用該變量的地方其值為undefined献宫,也能說明該變量是存在的(也就是說該變量被聲明了)。而使用let和const聲明的變量并不能達到這樣的效果实撒。
思考一個問題姊途,變量聲明提升到底是一個什么過程涉瘾?
我們知道JavaScript是解釋執(zhí)行語言,和Java那種編譯執(zhí)行語言不同捷兰。說到這里我們需要知道JavaScript代碼的執(zhí)行其實分為兩個階段—解釋階段(也可以理解為編譯階段立叛,但是和Java那種編譯成二進制的過程不一樣)和執(zhí)行階段。在解釋階段贡茅,Js引擎會對當前環(huán)境下所有的變量聲明(注意秘蛇,此處是聲明而不是賦值)放到了程序的最前端。下面看一看幾段JavaScript代碼經(jīng)過Js引擎解釋后執(zhí)行時的真正順序顶考。
console.log(a);
var a = 1;
console.log(a);
//被Js引擎解釋后的執(zhí)行順序
var a; //a的值為undefined
console.log(a); // undefined
a = 1; // a的值為1
console.log(a); // 1
add( 1, 2);
function add(num1, num2) {
console.log(num1 + num2);
}
//被Js引擎解釋后的執(zhí)行順序
function add(num1, num2) {
console.log(num1 + num2);
}
add(1, 2);
到這里我們應該對使用var聲明的變量提升和function聲明的函數(shù)變量提升有了一定的理解赁还。那么,思考一下下面幾段代碼的輸出結(jié)果是什么呢驹沿?
//第一段代碼
var a = 1;
function a {
return 2;
}
console.log(a);
//第二段代碼
var b;
console.log(b);
function b(){
return 3;
}
b = 4;
console.log(b);
//第三段代碼
var c;
console.log(c);
c = 5;
console.log(c);
function c() {
return 6;
};
c();
console.log(c);
//第四段代碼
var d;
console.log(d);
d = 7;
console.log(d);
var d = function() {
console.log(8);
}
d();
console.log(d);
感覺怎么樣艘策?大腦有沒有點懵?
上面的幾段代碼中普通變量和函數(shù)變量使用了相同的變量名渊季,在我們平時寫程序是不推薦這樣的柬焕,但是你作為一名JavaScript程序員你應該能分析其執(zhí)行順序,這也是我們前端開發(fā)面試過程中經(jīng)乘笥颍考察的問題斑举。下面我們看看上面三段代碼經(jīng)過Js引擎解釋之后真正的執(zhí)行順序。
//第一段代碼被Js引擎解釋之后的執(zhí)行順序
var a;
function a() {
return 2;
}
a = 1;
console.log(a); // 1
//第二段代碼被Js引擎解釋之后的執(zhí)行順序
var b;
function b() {
return 3;
}
console.log(b); // ? b(){ return 3;}
b = 4;
console.log(b); // 4
//第三段代碼被Js引擎解釋之后的執(zhí)行順序
var c;
function c() {
return 6;
}
console.log(c); // ? c(){ return 6;}
c = 5;
console.log(c); // 5
c(); // 報錯 Uncaught TypeError: c is not a function
console.log(c); // 不會被執(zhí)行
//第四段代碼被Js引擎解釋之后的執(zhí)行順序
var d; //此處是第一個變量聲明
var d; //此處是函數(shù)表達式的變量聲明提升
console.log(d); //此處d的值為undefined
d = 7;
console.log(d); // 7
d = function() {
console.log(8);
}
d(); // 8
console.log(d); // ? (){ console.log(8);}
- 重復聲明
- var 和 function能重復聲明病涨,后者覆蓋前者
- let 和 const 則不能重復聲明
var a = 1;
var a;
console.log(a); // 1 如果不明白為什么是1富玷,仔細看上一部分
let a = 1;
let a;
console.log(a); //報錯 Uncaught SyntaxError: Identifier 'a' has already been declared
- 作用域的范圍
- var 定義的變量的作用域是以函數(shù)為界限
- let 和 const 是塊作用域(常見為 for 和 if 語句的大括號為界限)
- var 可以定義全局變量和局部變量,let 和 const 只能定義局部變量
如何理解var定義的變量沒有塊級作用域既穆,let 和 const 聲明的變量有塊級作用域赎懦?
看下面的程序
function fn() {
var count1 = 0;
var count2 = 0;
for(var i=0;i<10;i++) {
count1++
}
for(let j=0;j<10;j++) {
count2++
}
/*if(true) {
let k = 'hello';
}*/
console.log(i); // 10
console.log(j); // 報錯 Uncaught ReferenceError: j is not defined at fn
//console.log(k); // 報錯 Uncaught ReferenceError: k is not defined at fn
}
fn();
- const 的特殊之處
- 使用const聲明的基本數(shù)據(jù)類型一旦聲明其值不可更改,引用類型比較特殊幻工,其內(nèi)部屬性的值可更改励两。
//使用const定義變量,如果其值為基本數(shù)據(jù)類型囊颅,則值不可更改
const a = 1;
a = 2; // 報錯 Uncaught TypeError: Assignment to constant variable.
//使用const定義的變量当悔,如果其值為引用數(shù)據(jù)類型,則該變量內(nèi)部屬性的值可更改
const obj = {x: 1};
obj.x = 2;
console.log(obj.x); // 2
const arr = [1];
arr.push(1);
console.log(arr); // [1, 1]
JavaScript變量使用(賦值)
聲明一個變量踢代,就是為了在這個變量中存儲一些值盲憎。
還記得JavaScript變量的值都有哪些數(shù)據(jù)類型嗎?
簡單數(shù)據(jù)類型:number胳挎,string饼疙,boolean,null(特殊類型)慕爬,undefined(特殊類型)
引用數(shù)據(jù)類型:object
es6新增數(shù)據(jù)類型:symbol
存儲簡單數(shù)據(jù)類型的變量和存儲引用類型的變量在使用過程中需要注意什么窑眯?
看一下下面的程序
var age= 22;
var job = 'programer';
var obj = {name: "zhang san"};
var arr = [1, 2];
var fn = function(age, obj, arr) {
var newAge = age;
var newObj = obj;
var newArr = arr;
newAge = 23;
newObj.name = "Jack";
newArr.push(3);
};
fn(age, obj, arr);
console.log(age); // 22
console.log(obj); // {name: "Jack"}
console.log(arr); // [1, 2, 3]
上面的程序相信大部分前端程序員都能知道是什么原因?qū)е碌摹?br>
簡單數(shù)據(jù)類型屏积,其在內(nèi)存中分別占有固定大小的空間,他們的值保存在棸跛Γ空間炊林,我們通過按值來訪問。
引用類型更胖,由于其值的大小不固定铛铁,因此不能把它們保存到棧內(nèi)存中。但內(nèi)存地址大小的固定的却妨,因此可以將內(nèi)存地址保存在棧內(nèi)存中饵逐。 這樣,當查詢引用類型的變量時彪标, 先從棧中讀取內(nèi)存地址倍权, 然后再通過地址找到堆中的值。我們按其引用訪問捞烟。
如果理解的不是很好薄声,可以詳細讀前端高質(zhì)量知識(一)-JS內(nèi)存空間詳細圖解這篇文章
思考!函數(shù)傳參中包括引用類型變量题画,在函數(shù)執(zhí)行后如何保證外部的引用類型變量不被修改默辨?
對!深拷貝苍息。后續(xù)文章中會詳細介紹缩幸。
JavaScript變量類型檢測
說起變量類型檢測,感覺和變量聲明提升一樣竞思,又有好多說不完的話了表谊。先思考兩個問題吧。
使用typeof variable 得到的值都有哪些盖喷?這些值和上文中提到的JavaScript數(shù)據(jù)類型有什么關(guān)系爆办?
來,看下面的程序课梳。
var a = 1;
var b = "hello Jack";
var c = true;
var d = null;
var e = undefined;
var obj = {name: "Jack"};
var arr = [1, 2];
var date = new Date();
var pattern1 = /hello/g;
var pattern2 = new RegExp("[bc]at", "i");
var fn = function(msg) { alert(msg);};
/*es6*/
var s = Symbol();
...
typeof a; // "number"
typeof NaN; // "number"
console.log(typeof b); // "string"
console.log(typeof c); // "boolean"
console.log(typeof d); // 注意 "object"
console.log(typeof e); // "undefined"
console.log(typeof obj); // "object"
console.log(typeof Object); // "function"
console.log(typeof arr); // "object"
console.log(typeof Array); // "function"
console.log(typeof date); // "object"
console.log(typeof pattern1 ); // "object"
console.log(typeof pattern2); // "object"
console.log(typeof RegExp); // "function"
console.log(typeof fn); // "function"
console.log(typeof Function); // "function"
console.log(typeof window); // "object"
console.log(typeof Math); // "object"
console.log(typeof s1); // "symbol"
...
使用typeof進行類型檢測的情景差不多了吧距辆。但是使用typeof對變量進行類型檢測時有些情況下得到的值并不是我們預想的,比如 typeof null惦界,可以說是JavaScript里面的一個bug挑格,但是這個bug也不會被糾正了,所以記住就可以了沾歪。
使用typeof Object 為什么得到"function" ?
在JavaScript中雾消,Object和Array等都是構(gòu)造函數(shù)灾搏,構(gòu)造函數(shù)也是函數(shù)挫望,所以使用typeof得到的值是"function"是可以理解。
使用typeof檢測得到的"function" 是變量的數(shù)據(jù)類型嗎狂窑?
注意媳板!typeof是JavaScript提供的一種檢測變量類型的方法,此處的變量類型是為了給Js引擎分析的泉哈,所以它把object和function給區(qū)分開了蛉幸,和上文提到的JavaScript變量值的數(shù)據(jù)類型不完全同。上文描述的變量值的數(shù)據(jù)類型是相對于內(nèi)存方面有更重要的信息(比如存放于棧內(nèi)存或者堆內(nèi)存)丛晦。
為什么使用typeof檢測存放于堆內(nèi)存中的“對象”得到的值有的是“object”奕纫,有的是“function”?
JavaScript中的對象分為普通對象和函數(shù)對象烫沙。這兩種對象都屬于引用類型匹层,并存放于堆內(nèi)存中。這兩種對象會在后續(xù)文章中深入討論锌蓄。
怎么判斷一個對象是另外一個對象(在Java中稱為類)的實例升筏?
是的,你應該很快的就能想到instanceof瘸爽。
JavaScript變量銷毀
變量中一旦存了值您访,它就會占用相應的內(nèi)存空間。變量中存的是簡單數(shù)據(jù)類型則存儲在棧內(nèi)存中剪决,如果是引用類型則存儲在堆內(nèi)存中灵汪。
銷毀,是指從內(nèi)存中回收該變量的存儲空間
那么變量什么時候會被銷毀(回收)昼捍?
如果是全局變量即在window環(huán)境下识虚,當瀏覽器窗口關(guān)閉后該變量被銷毀(回收)。
如果是局部變量(函數(shù)內(nèi)部變量)妒茬,當函數(shù)執(zhí)行完之后即被銷毀(回收)担锤。
如果是使用let 和 const定義的應用在塊級作用域中的變量,則在當前塊執(zhí)行完之后被銷毀(回收)乍钻。
思考肛循!什么樣的情況下函數(shù)內(nèi)部的變量會一直保存在內(nèi)存當中?
是的银择,你沒有猜錯多糠。使用閉包的情況下,函數(shù)內(nèi)部的變量不會在函數(shù)執(zhí)行完之后立即回收浩考,它們會一直留在內(nèi)存中夹孔。關(guān)于JavaScript閉包會在后續(xù)文章中詳細討論。
小結(jié)
變量是編程語言的根基,我們前端程序員應該熟練掌握JavaScript變量的各個特性搭伤,尤其是變量聲明提升和類型檢測只怎。寫到這里,關(guān)于JavaScript變量相關(guān)的知識點聊的差不多了怜俐。其實每個知識點如果深入挖的越深則涉及的范圍越來越廣身堡。在這篇文章中盡量做到點到為止。
這是我在簡書上寫的第一篇總結(jié)性文章拍鲤,文章結(jié)構(gòu)以及文字表達方面經(jīng)驗不足贴谎。如有遺漏或描述錯誤的地方請各位同學留言指正。