var 关键字
在
JavaScript
中,常用var
声明一个变量。
语法
var varname1 [= value1] [, varname2 [=value2]] ...[,varnameN[= valueN]];
varnameN
: 变量名。遵循命名规范valueN
: 变量的初始化值,可以是任何合法的表达式,默认值:undefined
描述
-
变量声明,无法发生在何处,都在执行任何代码之前进行处理。
-
var
声明变量的作用域是其当前的执行上下文。 -
在任何函数外声明的变量为
全局变量
-
在函数内声明的变量为
局部变量
-
当赋值给未声明的变量时,执行赋值后,该变量会被隐式地创建为全局变量。(它将成为全局对象的属性)
-
声明与未声明变量之间的差异:
- 声明变量的作用域限制在声明位置的上下文中。而非声明变量总是全局的。
function x() { y = 1; var z = 2; } x(); console.log(y); // 打印 1 console.log(z); // 报错
- 声明变量在任何代码执行前创建,而非声明变量只有在执行赋值操作的时候才会被创建。
console.log(a); // 抛出ReferenceError。 console.log('still going...'); // 打印"still going..."。 var a; console.log(a); // 打印"undefined"或""(不同浏览器实现不同)。 console.log('still going...'); // 打印"still going..."。
3.声明变量是其所在上下文的不可配置属性,非声明变量是可配置的(如非声明变量可以被删除)
var a = 1; b = 2; delete this.a; // 在严格模式(strict mode)下抛出 TypeError,其他情况下执行失败并无任何提示。 delete this.b; console.log(a, b); // 抛出 ReferenceError。 // 'b'属性已经被删除。
变量提升
变量声明总是在任何代码执行之前处理的,所以在代码中的任意位置声明变量总是等于在代码开发声明。意味着变量可以在声明之前使用。这就是变量提升(hoisting)。
function do_something() {
console.log(bar); // undefined
var bar = 111;
console.log(bar); // 111
}
do_something();
多个变量声明,初始化
// 多个变量声明,并初始化值为"A";
var a,
b = (a = 'A');
隐式全局变量和外部函数作用域
var x = 0; // x 是全局变量,并且赋值为0
console.log(typeof z); // undefined 因为 z 还不存在,发生变量提升,初始化为 undefined
function a() {
// 当 a 被调用时
var y = 2; // y 被声明称函数a作用域的变量,然后赋值称2
console.log(x, y); // 0 2
function b() {
// b 被调用时
x = 3; // 全局变量 x 被赋值为3,不生成全局变量。
y = 4; // 已存在的外部函数的y变量被赋值为4,不生成新的全局变量。
z = 5; // 创建新的全局变量z, 并赋值为5。严格模式下,会报 `ReferenceError`
}
b();
console.log(x, y, z); // 3 4 5
}
a(); // 调用 a 同时调用了 b
console.log(x, z); // 3 5
console.log(typeof y); // undefined 因为 y 是 a 函数的局部变量。
let
- 用法与 var 用法一致,语义上有所不同
- 用
let
声明一个变量,使用的是词法作用域
或块级作用域
。 - 块级作用域变量:在包含他们的块或
for
循环以外是不能访问的。
function f(input: boolean) {
let a = 100;
if (input) {
// a 可以访问
let b = a + 1;
return b;
}
return b; // error b 不能访问
}
- 在 catch 语句里声明的变量也具有同样的作用域规则。
try {
throw 'oh no!';
} catch (e) {
console.log('Oh well.');
}
// Error: 'e' doesn't exist here
console.log(e);
- 块级作用域的变量不能在声明之前读或写,虽然这些变量始终存在于“他们的作用域里”,但是直到声明他们的代码之前的区域都属于
暂时性死区
。
a++; //illegal to use 'a' before it's declared; 在声明之前 a 是非法的。
let a;
- 可以一个拥有块级作用域变量被声明前获取它, 只是不能在变量声明前去调用这个函数。
function foo() {
return a;
}
// 不能在 `a` 被声明前调用`foo`
// 运行时应该抛出错误
foo();
let a;
重定义以及屏蔽
var
声明时,不管声明多少次,只会得到 1 个
// 所有x 的声明实际上都引用一个相同的x
function f(x) {
var x;
var x;
if (true) {
var x;
}
}
let
声明时,同在一个作用域内不能声明相同的变量。
function f(x) {
// error: interface with parameter declaration
// 报错:属性已经声明
let x = 100;
}
function g() {
let x = 100;
var x = 100; // error: can't have both declaration of 'x';
// x 不能重复声明
}
- 块级作用域变量需要在明显不同的块里声明
function f(condition, x) {
if (condition) {
let x = 100;
return x;
}
return x;
}
f(false, 0); // return 0
f(false, 0); // return 100
- 一个嵌套作用域引入一个行为称作屏蔽。(尽量避免使用屏蔽,可以用不同变量名)
// 内层循环的 i 可以屏蔽掉外层循环的 i
function sumMatrix(matrix: number[][]) {
let sum = 0;
for (let i = 0; i < matrix.length; i++) {
var currentRow = matrix[i];
for (let i = 0; i < currentRow.length; i++) {
sum += currentRow[i];
}
}
return sum;
}
- 当
let
声明在循环体时,不仅引入一个新的变量环境,而且针对每次迭代都会创建一个新作用域。
for (let i = 0; i < 10; i++) {
setTimeout(function () {
console.log(i);
}, 100 * i);
}
// 结果
// 0, 1, 2, 3, 4, 5, 6, 7, 8, 9;
const
- 与
let
拥有相同的作用域规则,但是不能重新赋值。 - 声明是必须初始化。
const numLivesForCat = 9;
const kitty = {
name: 'Aurora',
numLives: numLivesForCat,
};
// Error 不能再次赋值
kitty = {
name: 'Danielle',
};