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
來定義常量着帽,const
與let
都具有塊級作用域:
'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;