變量作用域與解構(gòu)賦值

var聲明的變量是有作用域的:

1梭稚,如果是在函數(shù)體內(nèi)聲明颖低,作用域為函數(shù)內(nèi)部,函數(shù)外不可引用弧烤。

'use strict';

function foo(){
    var x = 1;
    x = x + 1;
}
x = x + 2; //ReferenceError! 無法在函數(shù)體外引用變量忱屑。

2,如果是在兩個不同的函數(shù)體內(nèi)聲明了同名變量:同名變量互相獨立暇昂,不受影響莺戒,作用域為自身所在的函數(shù)體。

'use strict';

function foo(){
    var x = 1;
    x = x + 1;
}
function bar(){
    var x = 'A';
    x = x + 'B';
}

3急波,嵌套的函數(shù)中从铲,內(nèi)部函數(shù)可以訪問外部函數(shù)定義的變量,反過來則不行澄暮。

'use strict';

function foo(){
    var x = 1;
    function bar(){
        y = x + 1; //函數(shù)bar可以訪問函數(shù)foo中的變量x!名段;
    }
    var z = y + x; //ReferenceError!foo不可以訪問bar的變量y!;
}

4泣懊,如果內(nèi)部函數(shù)和外部函數(shù)的變量名重名:互不影響伸辟。

'use strict';

function foo(){
    var x = 1;
    function bar(){
        var x = ‘A’;
        console.log('x in bar : ' + x); // 'A';
    }
    console.log('x in foo : ' + x); // 1;
    bar();
}
//x in foo : 1
//x in bar : ‘A’

這說明,函數(shù)在引用變量時是從內(nèi)向外查找的馍刮。


變量提升 : 只提升聲明自娩,不提升賦值

JavaScript的函數(shù)定義時,會先掃描整個函數(shù)體的語句渠退,把所有變量的聲明全部提升到函數(shù)頂部。

'use strict';

function foo(){
    var x = 'Hello, ' + y;;
    cosole.log(x);
    var y = 'Bob';
}
foo();//'Hello, undefined'

語句var x = 'Hello, ' + y;并沒有報錯脐彩,因為變量y在后邊聲明了碎乃,并被提升到了頂部。但是console,log(x)輸出的是Hello, undefined,說明變量y的值為undefined惠奸,真是因為JavaScript引擎自動提升了變量y的聲明梅誓,但不會提升變量y的賦值。

應(yīng)用場景:
由于JavaScript的這一怪異的“特性”,我們在函數(shù)內(nèi)部定義變量時梗掰,請嚴格遵守“在函數(shù)內(nèi)部首先申明所有變量”這一規(guī)則嵌言。最常見的做法是用一個var申明函數(shù)內(nèi)部用到的所有變量:

'use strict';

function foo(){
    var
        x = 1, //x初始化為1;
        y = x+1, //y的初始值為2及穗;
        z, i; //z和i的初始值為undefined
        
    //其他語句
    ...
}

全局作用域

不在任何函數(shù)內(nèi)定義的變量就具有全局作用域摧茴。實際上,JavaScript默認有一個全局對象window埂陆,全局作用域的變量實際上是被綁定到window的一個屬性

'use strict';

var course = 'Learn JavaScript';
alert(course);//'Learn JavaScript'
alert(windows.course);//'Learn JavaScript'

因此苛白,直接訪問全局變量course和訪問window.course是完全一樣的。


由于函數(shù)定義有兩種方式焚虱,以變量方式var foo = function () {}定義的函數(shù)實際上也是一個全局變量购裙,因此,頂層函數(shù)的定義也被視為一個全局變量鹃栽,并綁定到window對象:

'use strict';

function foo(){
    alert('foo!');
}
foo(); //'foo!'
windows.foo(); //'foo!'

JavaScript實際上只有一個全局作用域躏率。任何變量(函數(shù)也視為變量),如果沒有在當(dāng)前函數(shù)作用域中找到民鼓,就會繼續(xù)往上查找薇芝,最后如果在全局作用域中也沒有找到,則報ReferenceError錯誤摹察。


命名空間

全局變量會綁定到window上恩掷,不同的JavaScript文件如果使用了相同的全局變量,或者定義了相同名字的頂層函數(shù)供嚎,都會造成命名沖突黄娘,并且很難被發(fā)現(xiàn)。

減少沖突的一個方法是把自己的所有變量和函數(shù)全部綁定到一個全局變量中克滴。例如:

'use strict';

// 唯一的全局變量MYAPP:
var MYAPP = {};

// 其他變量:
MYAPP.name = 'myapp';
MYAPP.version = 1.0;

// 其他函數(shù):
MYAPP.foo = function () {
    return 'foo';
};

許多著名的JavaScript庫都是這么干的:jQuery逼争,YUI,underscore等等劝赔。


局部作用域

為了解決塊級作用域誓焦,ES6引入了新的關(guān)鍵字let,用let替代var可以申明一個塊級作用域的變量:

'use strict';

function foo() {
    var sum = 0;
    for (let i=0; i<100; i++) {
        sum += i;
    }
    // SyntaxError:
    i += 1;
}

常量

ES6標(biāo)準(zhǔn)引入了新的關(guān)鍵字const來定義常量着帽,constlet都具有塊級作用域

'use strict';

const PI = 3.14;
PI = 3; // 某些瀏覽器不報錯杂伟,但是無效果!
PI; // 3.14

解構(gòu)賦值

從ES6開始仍翰,JavaScript引入了解構(gòu)賦值赫粥,可以同時對一組變量進行賦值。

傳統(tǒng)的做法予借,如何把一個數(shù)組的元素分別賦值給幾個變量:

'use strict';

var array = ['hello', 'JavaScript', 'ES6'];
var x = array[0];
var y = array[1];
var z = array[2];

解構(gòu)賦值越平,直接對多個變量同時賦值:

'use strict';

注意频蛔,對數(shù)組元素進行解構(gòu)賦值時,多個變量要用[...]括起來秦叛。
var [x, y, z] = ['hello', 'JavaScript', 'ES6'];// x, y, z分別被賦值為數(shù)組對應(yīng)元素:

如果數(shù)組本身還有嵌套晦溪,也可以通過下面的形式進行解構(gòu)賦值,注意嵌套層次和位置要保持一致:
let [x, [y, z]] = ['hello', ['JavaScript', 'ES6']];
x; // 'hello'
y; // 'JavaScript'
z; // 'ES6'

解構(gòu)賦值還可以忽略某些元素:
let [, , z] = ['hello', 'JavaScript', 'ES6']; // 忽略前兩個元素挣跋,只對z賦值第三個元素
z; // 'ES6'

如果需要從一個對象中取出若干屬性三圆,也可以使用解構(gòu)賦值,便于快速獲取對象的指定屬性:
var person = {
    name: '小明',
    age: 20,
    gender: 'male',
    passport: 'G-12345678',
    school: 'No.4 middle school'
};
var {name, age, passport} = person; //name, age, passport分別被賦值為對應(yīng)屬性:

對一個對象進行解構(gòu)賦值時浆劲,同樣可以直接對嵌套的對象屬性進行賦值嫌术,只要保證對應(yīng)的層次是一致的:
var person = {
    name: '小明',
    age: 20,
    gender: 'male',
    passport: 'G-12345678',
    school: 'No.4 middle school',
    address: {
        city: 'Beijing',
        street: 'No.1 Road',
        zipcode: '100001'
    }
};
var {name, address: {city, zip}} = person;
name; // '小明'
city; // 'Beijing'
zip; // undefined, 因為屬性名是zipcode而不是zip
// 注意: address不是變量,而是為了讓city和zip獲得嵌套的address對象的屬性:
address; // Uncaught ReferenceError: address is not defined

使用解構(gòu)賦值對對象屬性進行賦值時牌借,如果對應(yīng)的屬性不存在度气,變量將被賦值為undefined,這和引用一個不存在的屬性獲得undefined是一致的膨报。如果要使用的變量名和屬性名不一致磷籍,可以用下面的語法獲取:
var person = {
    name: '小明',
    age: 20,
    gender: 'male',
    passport: 'G-12345678',
    school: 'No.4 middle school'
};

// 把passport屬性賦值給變量id:
let {name, passport:id} = person;
name; // '小明'
id; // 'G-12345678'
// 注意: passport不是變量现柠,而是為了讓變量id獲得passport屬性:
passport; // Uncaught ReferenceError: passport is not defined

解構(gòu)賦值還可以使用默認值院领,這樣就避免了不存在的屬性返回undefined的問題:
var person = {
    name: '小明',
    age: 20,
    gender: 'male',
    passport: 'G-12345678'
};
// 如果person對象沒有single屬性,默認賦值為true:
var {name, single=true} = person;
name; // '小明'
single; // true

有些時候够吩,如果變量已經(jīng)被聲明了比然,再次賦值的時候,正確的寫法也會報語法錯誤:
// 聲明變量:
var x, y;
// 解構(gòu)賦值:
{x, y} = { name: '小明', x: 100, y: 200};
// 語法錯誤: Uncaught SyntaxError: Unexpected token =
這是因為JavaScript引擎把{開頭的語句當(dāng)作了塊處理周循,于是=不再合法强法。解決方法是用小括號括起來:
({x, y} = { name: '小明', x: 100, y: 200});

應(yīng)用場景
解構(gòu)賦值在很多時候可以大大簡化代碼。例如湾笛,交換兩個變量x和y的值饮怯,可以這么寫,不再需要臨時變量:

var x=1, y=2;
[x, y] = [y, x]

快速獲取當(dāng)前頁面的域名和路徑

var {hostname:domain, pathname:path} = location;
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末嚎研,一起剝皮案震驚了整個濱河市蓖墅,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌临扮,老刑警劉巖论矾,帶你破解...
    沈念sama閱讀 211,561評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異杆勇,居然都是意外死亡拇囊,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,218評論 3 385
  • 文/潘曉璐 我一進店門靶橱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事关霸〈疲” “怎么了?”我有些...
    開封第一講書人閱讀 157,162評論 0 348
  • 文/不壞的土叔 我叫張陵队寇,是天一觀的道長膘掰。 經(jīng)常有香客問我,道長佳遣,這世上最難降的妖魔是什么识埋? 我笑而不...
    開封第一講書人閱讀 56,470評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮零渐,結(jié)果婚禮上窒舟,老公的妹妹穿的比我還像新娘。我一直安慰自己诵盼,他們只是感情好惠豺,可當(dāng)我...
    茶點故事閱讀 65,550評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著风宁,像睡著了一般洁墙。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上戒财,一...
    開封第一講書人閱讀 49,806評論 1 290
  • 那天热监,我揣著相機與錄音,去河邊找鬼饮寞。 笑死孝扛,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的骂际。 我是一名探鬼主播疗琉,決...
    沈念sama閱讀 38,951評論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼歉铝!你這毒婦竟也來了盈简?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,712評論 0 266
  • 序言:老撾萬榮一對情侶失蹤太示,失蹤者是張志新(化名)和其女友劉穎柠贤,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體类缤,經(jīng)...
    沈念sama閱讀 44,166評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡臼勉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,510評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了餐弱。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片宴霸。...
    茶點故事閱讀 38,643評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡囱晴,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出瓢谢,到底是詐尸還是另有隱情畸写,我是刑警寧澤,帶...
    沈念sama閱讀 34,306評論 4 330
  • 正文 年R本政府宣布氓扛,位于F島的核電站枯芬,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏采郎。R本人自食惡果不足惜千所,卻給世界環(huán)境...
    茶點故事閱讀 39,930評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蒜埋。 院中可真熱鬧淫痰,春花似錦、人聲如沸理茎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,745評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽皂林。三九已至朗鸠,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間础倍,已是汗流浹背烛占。 一陣腳步聲響...
    開封第一講書人閱讀 31,983評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留沟启,地道東北人忆家。 一個月前我還...
    沈念sama閱讀 46,351評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像德迹,于是被迫代替她去往敵國和親芽卿。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,509評論 2 348

推薦閱讀更多精彩內(nèi)容