var
声明的变量是有作用域的:
1,如果是在函数体内声明,作用域为函数内部,函数外不可引用。
'use strict';
function foo(){
var x = 1;
x = x + 1;
}
x = x + 2; //ReferenceError! 无法在函数体外引用变量。
2,如果是在两个不同的函数体内声明了同名变量:同名变量互相独立,不受影响,作用域为自身所在的函数体。
'use strict';
function foo(){
var x = 1;
x = x + 1;
}
function bar(){
var x = 'A';
x = x + 'B';
}
3,嵌套的函数中,内部函数可以访问外部函数定义的变量,反过来则不行。
'use strict';
function foo(){
var x = 1;
function bar(){
y = x + 1; //函数bar可以访问函数foo中的变量x!;
}
var z = y + x; //ReferenceError!foo不可以访问bar的变量y!;
}
4,如果内部函数和外部函数的变量名重名:互不影响。
'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’
这说明,函数在引用变量时是从内向外查找的。
变量提升 : 只提升声明,不提升赋值
JavaScript的函数定义时,会先扫描整个函数体的语句,把所有变量的声明全部提升到函数顶部。
'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
的赋值。
应用场景:
由于JavaScript的这一怪异的“特性”,我们在函数内部定义变量时,请严格遵守“在函数内部首先申明所有变量”这一规则。最常见的做法是用一个var申明函数内部用到的所有变量:
'use strict';
function foo(){
var
x = 1, //x初始化为1;
y = x+1, //y的初始值为2;
z, i; //z和i的初始值为undefined
//其他语句
...
}
全局作用域
不在任何函数内定义的变量就具有全局作用域。实际上,JavaScript默认有一个全局对象window,全局作用域的变量实际上是被绑定到window的一个属性:
'use strict';
var course = 'Learn JavaScript';
alert(course);//'Learn JavaScript'
alert(windows.course);//'Learn JavaScript'
因此,直接访问全局变量course和访问window.course是完全一样的。
由于函数定义有两种方式,以变量方式var foo = function () {}定义的函数实际上也是一个全局变量,因此,顶层函数的定义也被视为一个全局变量,并绑定到window对象:
'use strict';
function foo(){
alert('foo!');
}
foo(); //'foo!'
windows.foo(); //'foo!'
JavaScript实际上只有一个全局作用域。任何变量(函数也视为变量),如果没有在当前函数作用域中找到,就会继续往上查找,最后如果在全局作用域中也没有找到,则报ReferenceError错误。
命名空间
全局变量会绑定到window上,不同的JavaScript文件如果使用了相同的全局变量,或者定义了相同名字的顶层函数,都会造成命名冲突,并且很难被发现。
减少冲突的一个方法是把自己的所有变量和函数全部绑定到一个全局变量中。例如:
'use strict';
// 唯一的全局变量MYAPP:
var MYAPP = {};
// 其他变量:
MYAPP.name = 'myapp';
MYAPP.version = 1.0;
// 其他函数:
MYAPP.foo = function () {
return 'foo';
};
许多著名的JavaScript库都是这么干的:jQuery,YUI,underscore等等。
局部作用域
为了解决块级作用域,ES6
引入了新的关键字let
,用let
替代var
可以申明一个块级作用域的变量:
'use strict';
function foo() {
var sum = 0;
for (let i=0; i<100; i++) {
sum += i;
}
// SyntaxError:
i += 1;
}
常量
ES6
标准引入了新的关键字const
来定义常量,const
与let
都具有块级作用域:
'use strict';
const PI = 3.14;
PI = 3; // 某些浏览器不报错,但是无效果!
PI; // 3.14
解构赋值
从ES6开始,JavaScript引入了解构赋值,可以同时对一组变量进行赋值。
传统的做法,如何把一个数组的元素分别赋值给几个变量:
'use strict';
var array = ['hello', 'JavaScript', 'ES6'];
var x = array[0];
var y = array[1];
var z = array[2];
解构赋值,直接对多个变量同时赋值:
'use strict';
注意,对数组元素进行解构赋值时,多个变量要用[...]括起来。
var [x, y, z] = ['hello', 'JavaScript', 'ES6'];// x, y, z分别被赋值为数组对应元素:
如果数组本身还有嵌套,也可以通过下面的形式进行解构赋值,注意嵌套层次和位置要保持一致:
let [x, [y, z]] = ['hello', ['JavaScript', 'ES6']];
x; // 'hello'
y; // 'JavaScript'
z; // 'ES6'
解构赋值还可以忽略某些元素:
let [, , z] = ['hello', 'JavaScript', 'ES6']; // 忽略前两个元素,只对z赋值第三个元素
z; // 'ES6'
如果需要从一个对象中取出若干属性,也可以使用解构赋值,便于快速获取对象的指定属性:
var person = {
name: '小明',
age: 20,
gender: 'male',
passport: 'G-12345678',
school: 'No.4 middle school'
};
var {name, age, passport} = person; //name, age, passport分别被赋值为对应属性:
对一个对象进行解构赋值时,同样可以直接对嵌套的对象属性进行赋值,只要保证对应的层次是一致的:
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
使用解构赋值对对象属性进行赋值时,如果对应的属性不存在,变量将被赋值为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
解构赋值还可以使用默认值,这样就避免了不存在的属性返回undefined的问题:
var person = {
name: '小明',
age: 20,
gender: 'male',
passport: 'G-12345678'
};
// 如果person对象没有single属性,默认赋值为true:
var {name, single=true} = person;
name; // '小明'
single; // true
有些时候,如果变量已经被声明了,再次赋值的时候,正确的写法也会报语法错误:
// 声明变量:
var x, y;
// 解构赋值:
{x, y} = { name: '小明', x: 100, y: 200};
// 语法错误: Uncaught SyntaxError: Unexpected token =
这是因为JavaScript引擎把{开头的语句当作了块处理,于是=不再合法。解决方法是用小括号括起来:
({x, y} = { name: '小明', x: 100, y: 200});
应用场景:
解构赋值在很多时候可以大大简化代码。例如,交换两个变量x和y的值,可以这么写,不再需要临时变量:
var x=1, y=2;
[x, y] = [y, x]
快速获取当前页面的域名和路径:
var {hostname:domain, pathname:path} = location;