讓天下沒有難學(xué)的js之JavaScript中變量的那些事

本篇文章面向群體:入門級</br>
難度等級:★☆☆☆☆</br>
內(nèi)容較多,建議點(diǎn)贊收藏后閱讀

什么是變量

變量作為js中最常見也是我們最早接觸的js知識(shí)點(diǎn)氧敢,相信大家都不陌生,變量幾乎存在于所有的編程語言中衬吆,百度百科中對于變量的解釋為 變數(shù)或變量秸滴,是指沒有固定的值,可以改變的數(shù)栈源。變量以非數(shù)字的符號(hào)來表達(dá)挡爵,一般用拉丁字母。 而JavaScript中的變量是松散類型(弱類型)的凉翻,所謂松散類型就是可以用來保存任何類型的數(shù)據(jù)了讨,在聲明變量時(shí)無需指定變量的類型捻激。所以,當(dāng)我們聲明一個(gè)變量之后前计,可以存儲(chǔ)任意類型的數(shù)據(jù)胞谭。

怎么去聲明一個(gè)變量

變量名

在js中聲明一個(gè)變量通常通過關(guān)鍵字加一個(gè)變量名的形式來聲明一個(gè)變量,那對于變量名在js中是如何要求的呢男杈?

  • 變量名必須以字母丈屹、下劃線(_)或者美元符($)開頭,后面可以跟字母伶棒、下劃線旺垒、美元符或者數(shù)字
  • 變量名的長度不能超過255個(gè)字符
  • 變量名必須區(qū)分大小寫
  • 變量名中間不可有空格換行符及其他標(biāo)點(diǎn)符號(hào)
  • 不能使用腳本語言保留的關(guān)鍵字作為變量名,如true肤无、false先蒋、function等(具體關(guān)鍵字列表可參考菜鳥教程JavaScript 保留關(guān)鍵字

除了這些js明文規(guī)定的變量命名規(guī)則之外,為了代碼可讀性更高宛渐,我們通常也對變量命名有行業(yè)通用的命名規(guī)范竞漾。

  • 盡量采用符合當(dāng)前語義的單詞進(jìn)行命名,如age窥翩、 time业岁,避免使用無意義的字符組合,如aaa寇蚊、bbb
  • 由于部分框架可能使用$作為關(guān)鍵字笔时,所以我們也應(yīng)盡量避免使用$作為變量名
  • 如果命名單詞超過兩個(gè)單詞,盡量采用駝峰法命名仗岸,如userAge允耿、myFirstName
  • 盡量避免使用中文作為變量名,盡管那樣不會(huì)報(bào)錯(cuò)

聲明變量

上面說了爹梁,js聲明變量的方法為關(guān)鍵字加一個(gè)變量名右犹,說完了變量名,我們就來說一下聲明變量的關(guān)鍵字姚垃,js中聲明變量的關(guān)鍵字有以下三種

var

var關(guān)鍵字是我們學(xué)習(xí)js最先接觸也是早期聲明變量最為常用的關(guān)鍵字念链,使用方法直接在var關(guān)鍵字后面直接跟變量名即可,如

var message = 'hello world'
console.log(message) // hello world

var 關(guān)鍵字特點(diǎn)

  • 可重復(fù)聲明积糯,但并沒有什么卵用
  • 聲明后的變量可修改
var message = 'hello'
message = 'world'
console.log(message) // world
  • 有初始值掂墓,每一個(gè)用var聲明的變量初始值均為undefined
var message
console.log(message) // undefined
  • 可同時(shí)聲明多個(gè)變量
var message, test, handleName
console.log(message) // undefined
console.log(test) // undefined
console.log(handleName) // undefined
  • var 關(guān)鍵字可以省略
message = 'hello'
console.log(message) // hello

// js 中如果省略var關(guān)鍵字,則會(huì)自動(dòng)創(chuàng)建全局變量
function foo() {
    message = 'hello'
    console.log(message) // hello
}
foo()
console.log(message) // hello

// 如果使用看成,則會(huì)創(chuàng)建當(dāng)前作用域的變量
function foo() {
    var message = 'hello'
    console.log(message) // hello
}
foo()
console.log(message) // message is not defined

<span style="color:red">*</span> 注意君编,在嚴(yán)格模式下不可省略關(guān)鍵字,否則會(huì)報(bào)錯(cuò)

let

let 關(guān)鍵字是ES2015(ES6) 新增加的重要的 JavaScript 關(guān)鍵字川慌,用法和 var 一樣

let message = 'hello world'
console.log(message) // hello world

let 關(guān)鍵字與var不同的地方

  • 不可重復(fù)聲明吃嘿,已經(jīng)聲明過的變量再次聲明會(huì)報(bào)錯(cuò)(在同一作用域內(nèi))
var message
let message // Identifier 'message' has already been declared

let test = 'test'
let test = 'hello' // Identifier 'test' has already been declared
  • let 所聲明的變量祠乃,只在 let 命令所在的代碼塊內(nèi)有效
{
  let a = 10;
  var b = 1;
}

a // ReferenceError: a is not defined.
b // 1

利用 let 的這一特性可以解決很經(jīng)典的一個(gè)問題

for (var index = 0; index < 6; index++) {
  setTimeout(function() {
    console.log(index)
  }, 100)
}

這段代碼的結(jié)果輸出的是6個(gè)6,這是因?yàn)檫@里的index是用 var 定義的兑燥,每一輪循環(huán)所用的index都是全局變量亮瓷,所以等到100毫秒后執(zhí)行的時(shí)候,index已經(jīng)循環(huán)完變成了6降瞳,那么利用 let 僅在當(dāng)前代碼塊有效的這個(gè)特性嘱支,就可以很好的解決這個(gè)問題

for (let index = 0; index < 6; index++) {
  setTimeout(function() {
    console.log(index)
  }, 100)
}
// 0 1 2 3 4 5

因?yàn)?let 僅在當(dāng)前代碼塊有效,所以這里每一輪循環(huán)都相當(dāng)于定義了一個(gè)新的index挣饥,所以最后輸出的時(shí)循環(huán)時(shí)候的值除师。你可能會(huì)問,如果每一輪循環(huán)的變量i都是重新聲明的扔枫,那它怎么知道上一輪循環(huán)的值汛聚,從而計(jì)算出本輪循環(huán)的值?這是因?yàn)?JavaScript 引擎內(nèi)部會(huì)記住上一輪循環(huán)的值茧吊,初始化本輪的變量i時(shí)贞岭,就在上一輪循環(huán)的基礎(chǔ)上進(jìn)行計(jì)算。

const

const 關(guān)鍵字是ES2015(ES6) 新增加的重要的 JavaScript 關(guān)鍵字搓侄,用法和 var 一樣,只不過 const 生成的是一個(gè)或多個(gè)常量话速,在有些時(shí)候讶踪,我們定義的值不希望被覆蓋或者修改,我們就可以用 const

const message = 'hello world'
console.log(message) // hello world

const 關(guān)鍵字 和 var 關(guān)鍵字不同的地方

  • 不可重復(fù)聲明
  • 聲明時(shí)必須初始化泊交,否則會(huì)報(bào)錯(cuò)
const a // Missing initializer in const declaration
const b = 'test' // 正確
  • 聲明的常量不可進(jìn)行賦值修改
const a = 'hello'
a = 'world' // Uncaught TypeError: Assignment to constant variable.
其實(shí)const聲明的常量并非嚴(yán)格意義上的常量乳讥,因?yàn)楫?dāng)我們用const定義一個(gè)常量的值為引用類型(下面會(huì)講基本類型和引用類型)時(shí)候,雖然我們不能進(jìn)行重新賦值廓俭,但我們可以修改引用類型的值云石。
const a = {
    name: '小明'
}
a.name = '小豪'
console.log(a.name) // 小豪

變量的值:基本類型與引用類型

我們知道,js中的值可以保存所有數(shù)據(jù)類型研乒,那么js中的數(shù)據(jù)類型又都有什么呢汹忠?
JavaScript中的數(shù)據(jù)類型有六種,其中有五種簡單數(shù)據(jù)類型(也叫基本數(shù)據(jù)類型)雹熬,還有一種復(fù)雜的數(shù)據(jù)類型——對象(Object)宽菜,由于本文主要講的是變量相關(guān)知識(shí),所以對數(shù)據(jù)類型不深入講解竿报,知識(shí)大概說一下铅乡。

基本類型

五種基本的數(shù)據(jù)類型:Undefined、Null烈菌、Boolean阵幸、Number和String花履。這5種基本數(shù)據(jù)類型是按值訪問的,因?yàn)榭梢圆僮鞅4嬖谧兞恐械膶?shí)際的值挚赊。我們就直接按正常思路來就行了诡壁。

var a = '小明'
var b = a
a = '小豪'
console.log(b) // '小明'

引用類型

由于對象的知識(shí)相對較多,后續(xù)會(huì)有專門的文章進(jìn)行介紹總結(jié)咬腕。

如果有點(diǎn)基礎(chǔ)同學(xué)都知道欢峰,原始類型存儲(chǔ)的是值,對象類型存儲(chǔ)的是地址(指針)涨共,
那么當(dāng)我們定義一個(gè)變量的值為對象的時(shí)候纽帖,由于存儲(chǔ)的實(shí)際是這個(gè)對象在內(nèi)存中的地址,相當(dāng)于我們在這里只是引用了這個(gè)對象举反,所以在對一些變量進(jìn)行復(fù)制賦值和修改時(shí)候就會(huì)出現(xiàn)一些意想不到的事情懊直,比如下面的代碼

var a = {
  name: '小明'
}
var b = a
b.name = '小豪'
console.log(a.name) // 小豪

在上面的例子中,我們把a(bǔ)賦值給b火鼻,實(shí)際上只是把a(bǔ)所引用的對象地址賦值給了b室囊,這時(shí)候a和b其實(shí)指向的是一個(gè)對象,所以當(dāng)我們修改其中任何一個(gè)的時(shí)候魁索,都是在對同一對象進(jìn)行修改

這樣很好的解釋了我們上面所說的const定義的常量可修改的問題融撞,當(dāng)我們用const定義的常量為對象時(shí),其實(shí)我們在這個(gè)常量里保存的只是一個(gè)對象的地址粗蔚,無論我們怎么修改這個(gè)對象尝偎,const定義的常量里保存的地址是沒有變化的,所以上面例子中對const定義的對象進(jìn)行修改其實(shí)并沒有違背const定義的變量不可修改的原則鹏控,只有我們給這個(gè)常量重新賦值一個(gè)新對象(也就是新地址)的時(shí)候致扯,才會(huì)觸發(fā)const定義的常量不可修改的規(guī)則。

變量作用域

作用域当辐,我們這里為了理解抖僵,可以簡單的理解為作用域就是變量可以生效及訪問的地方。JavaScript中分為全局作用域和局部作用域缘揪,全局作用域里的變量在所有的地方都可以訪問耍群,局部作用域只能在當(dāng)前作用域被訪問。

在一些類似于c語言的編程語言中寺晌,每一對花括號(hào)包裹的區(qū)域都有自己的作用域世吨,我們稱之為塊狀作用域,而在JavaScript中沒有塊級作用域(es6之前)呻征,取而代之的是函數(shù)作用域耘婚,所以我們通常所說的局部作用域也就是函數(shù)作用域。

var a = "hello" // 全局作用域
function foo() {
    // 在此作用域可以訪問到a
    console.log(a) // 'hello'
    var b = 'world'
}
foo()
console.log(a) // 'hello'
// 由于變量b定義在函數(shù)foo內(nèi)陆赋,所以在函數(shù)foo的作用域外訪問不到
console.log(b) // b is not defined

在js中可以存在函數(shù)嵌套函數(shù)的情況沐祷,所以我們非常容易見到函數(shù)作用域嵌套的情況嚷闭,這時(shí)候就會(huì)形成一條作用域鏈,在js中赖临,當(dāng)你使用一個(gè)變量時(shí)胞锰,JavaScript引擎會(huì)首先在當(dāng)前作用域內(nèi)尋找,如果找不到兢榨,就會(huì)到上一層作用域?qū)ふ倚衢牛绻恢闭业巾攲幼饔糜颍ㄒ簿褪侨肿饔糜颍┻€找不到時(shí),就會(huì)報(bào)錯(cuò)吵聪。

ES2015(ES6) 新增加了 let 關(guān)鍵字凌那,從而可以讓我們在塊級作用域(大括號(hào))中聲明變量。

變量提升

在JavaScript 中吟逝,函數(shù)及變量的聲明都將被提升到當(dāng)前作用域的最頂部帽蝶。

//var a   <------------------
console.log(a) // undefined  ↑
var a   //  ---------------->

在上面的例子中,我們雖然聲明語句在打印語句的后面块攒,但是我們打印a卻并沒有報(bào)錯(cuò)励稳,就是因?yàn)檫@里的變量聲明被提到了當(dāng)前作用域的最上面,我們稱之為變量提升囱井。

在JavaScript中 var a = 'test' 其實(shí)是分為兩步進(jìn)行的驹尼,分別是變量聲明 var a 和 變量賦值 a = 'test', 變量聲明會(huì)被提前,但是變量賦值不會(huì)被提前庞呕,我們來看下面的例子

console.log(a) // undefined
var a = 'test'

// 上述代碼的執(zhí)行步驟可以用下面的代碼理解

var a
console.log(a)
a = 'test'

結(jié)合上面的函數(shù)作用域扶欣,我們來看一下下面的例子

var a = 'test'
function foo() {
  console.log(a)
  var a = 'hello'
}
foo()

大家猜一下打印的結(jié)果是什么,沒錯(cuò)千扶,就是 undefined ,我們這里定義了全局變量a骆捧,又定義了函數(shù)局部變量a澎羞,所以我們在執(zhí)行函數(shù) foo() 時(shí),其實(shí)是找的函數(shù) foo() 里的局部變量a敛苇,局部變量a通過變量提升到了函數(shù) foo() 的頂部妆绞,但是變量賦值卻沒有提升,所以最后打印結(jié)果為 undefined枫攀,上面的代碼可以理解為下面這樣

var a = 'test'
function foo() {
  var a
  console.log(a)
  a = 'hello'
}
foo()

需要注意的是 letconst 不存在變量提升括饶,在你定義他們之前使用會(huì)報(bào)錯(cuò)。比較有意思的是来涨,letconst的報(bào)錯(cuò)還不一樣

console.log(test) // Cannot access 'test' before initialization
const test = 'hello'

console.log(message) // message is not defined
let message = 'hello'

// 如果我們在局部作用域里寫图焰,報(bào)錯(cuò)就一樣了
function foo() {
  console.log(message) // Cannot access 'message' before initialization
  let message = 'world'
}
foo()

作者確實(shí)基礎(chǔ)較差,寫文章更多的目的是為了提高自己的能力蹦掐,但是總結(jié)寫下來難免有啰嗦和失誤的地方技羔,如果可以幫到別人當(dāng)然十分開心僵闯,如果大家發(fā)現(xiàn)什么問題也歡迎隨時(shí)提出,我也會(huì)持續(xù)的學(xué)習(xí)藤滥,不斷的對文章新型修改鳖粟,希望大家一起進(jìn)步,加油

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末拙绊,一起剝皮案震驚了整個(gè)濱河市向图,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌标沪,老刑警劉巖榄攀,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異谨娜,居然都是意外死亡航攒,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進(jìn)店門趴梢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來漠畜,“玉大人,你說我怎么就攤上這事坞靶°灸” “怎么了?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵彰阴,是天一觀的道長瘾敢。 經(jīng)常有香客問我,道長尿这,這世上最難降的妖魔是什么簇抵? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮射众,結(jié)果婚禮上碟摆,老公的妹妹穿的比我還像新娘。我一直安慰自己叨橱,他們只是感情好典蜕,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著罗洗,像睡著了一般愉舔。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上伙菜,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天轩缤,我揣著相機(jī)與錄音,去河邊找鬼。 笑死典奉,一個(gè)胖子當(dāng)著我的面吹牛躺翻,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播卫玖,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼公你,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了假瞬?” 一聲冷哼從身側(cè)響起陕靠,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎脱茉,沒想到半個(gè)月后剪芥,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡琴许,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年税肪,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片榜田。...
    茶點(diǎn)故事閱讀 38,569評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡益兄,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出箭券,到底是詐尸還是另有隱情净捅,我是刑警寧澤,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布辩块,位于F島的核電站蛔六,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏废亭。R本人自食惡果不足惜国章,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望豆村。 院中可真熱鬧捉腥,春花似錦、人聲如沸你画。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽坏匪。三九已至,卻和暖如春撬统,著一層夾襖步出監(jiān)牢的瞬間适滓,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工恋追, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留凭迹,地道東北人罚屋。 一個(gè)月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像嗅绸,于是被迫代替她去往敵國和親脾猛。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評論 2 348