重温【 JavaScript 高级程序设计 】
读书须用意,
一字值千金;
变量声明
在书中明确指出 JavaScript
有 3 个关键字可以声明变量。分别是:var、let、const
。
var 关键字
//var 关键字使用
function foo(){
//重复使用 var 关键字命名同一个标识符是没问题的
var name = 'Yongy';
var name = 'Matt';
var name = '小木增春';
console.log(name);
}
foo(); ==> 小木增春
console.log(name); ==> Error
在使用 var
关键字时要注意,在 function
内部使用 var
关键字定义的变量,具有的作用域只在声明该变量的作用域内,这意思是说“函数内部使用 var
关键字声明的变量为局部变量”
书中也指出,也可以在函数内部定义全局变量
,使得任意函数都能访问到该变量。
function foo(){
name = '小木增春';
console.log(name);
}
foo(); ==> 小木增春
console.log(name); ==> 小木增春
运行上面的代码 foo 方法执行后是可以成功访问到 name
的,在不使用关键字直接对标识符赋值,相当于定义全局变量
。
那么为什么可以访问到呢,或者说是怎么做到的,作者是这么跟读者解释的:
假设有如下代码:
function foo(){
console.log(age);
var age = 24;
}
foo(); ==> undefined
执行 foo 方法之所以输出 undefined
而不是报错,这是因为在 ECMAScript 运行时,代码是这样的:
function foo(){
var age; //声明了变量 age 并未有初始化值
console.log(age); ==> undefined
age = 24;
}
foo();
使用var
关键字声明变量时会将“变量的声明置于函数顶部”在这种情况下,console age 时是一个未经初始化的变量,因此呢 访问到的值是 undefined 而不是未经定义的变量,自然也不会报错。
这种变量初始化声明置于函数顶部的行为,也就是提升“hosit”
let 关键字
let
跟 var
差不多,但是有着很重要的区别。let
的声明是块作用域,而 var
声明的范围是函数作用域。
if(true){
var _x_name = "小木增春";
}
console.log(_x_name); ==>小木增春
if(true){
let _x_age = 24;
}
console.log(_x_age); ==> 报错 Uncaught ReferenceError: _x_age is not defined
在访问变量_x_age
时报错是因为,let 关键字的作用域仅限于该代码块内部,也就是 if 块中。块作用域是函数作用域的子集,因此适用于 var 的作用域限制同样也适用 let。
此外 let 关键字在同一作用域内,重复声明变量也会报错,例如:
var age;
var age;
let name;
let name; ==> 报错
不同作用域下,使用 let 关键字, JavaScript 引擎检测到 if 的代码块中没有 let 关键字对 name 进行重复声明,因此是可以的。
let name;
if(true){
let name = 'zilla';
console.log(name);
}
此外书中同时指出了,对声明冗余报错不会因为混用了 let 和 var 而受影响。这两个关键字声明的并不是不同类型的变量,它们只是指出变量在相关作用域如何存在。也就是说,let 和 var 对我们来说是不同的单词,在 JavaScript 引擎中它们都是用于声明变量的,但是作用域不同所以无法混用。
var name = 'Matt';
let name = '小木增春';
var 关键字 和 let 关键字都声明了变量 name,但具体 name 的作用域该是什么呢,在函数内部,在块语句呢,或者是全局声明的情况下呢,所以还是不能混用好。
此外书中指出使用 let 关键字不会提升变量,在全局声明下,let 关键字声明的变量不会成为 window 对象的属性。而 var 关键字声明的变量是会的。
//全局声明
var name = '小木增春';
console.log(window.name); ==> 小木增春
let age = 24;
console.log(window.age); ==> undefined
由于在全局作用域声明的 age 因此可以在任何作用域内都可以访问到该变量
console.log(age); ==> 24
// 作用域提升
if(true){
var name = '小木增春';
let age = 24;
}
console.log(window.name); ==> 小木增春
console.log(age); ==> ReferenceError:age is not defined
书中有这么一段描述:在解析代码时,JavaScript 引擎会注意到 let 声明,在此前不能以任何方式引用未声明的变量。在 let 声明之前的执行瞬间被成为“暂时性死区”(temporal dead zone),在此阶段引用任何后面才声明的变量都会抛出 ReferenceError。
const 关键字
const
关键字与 let
基本相同,区别在于使用 const 关键字必须在声明变量时进行初始化值,且尝试修改 const 声明的变量值时会导致运行时错误。
const age = 24;
age = 25; ==> Uncaught TypeError: invalid assignment to const 'age'
此外 const 也不允许重复声明,也同样具有块作用域
const age = 24;
if(true){
const age = 25; //OK
console.log(age); ==> 25
}
console.log(age); ==> 24;
const age = 5; //SyntaxError
如果 const 声明的变量是一个对象,只要不更改引用的对象的地址,基于对象的操作都是合法的。
例如:
const person = {};
person.age = 25; //这是合法的操作的
person = new Person(24); //这是非法的操作,更改了对象的引用地址
在迭代中使用 let 关键字
在 let 关键字出现以前,for 循环定义的迭代变量会渗透到循环外部:
for(var i = 0; i < 5; i++){
// doSomething
}
console.log(i); ==> 4
使用 let 关键字
for(let i = 0; i < 4; i++){
// doSomething
}
console.log(i); ==> ReferenceError: i is not defined
常见的话用于解决循环绑定事件:
var menus = XXX; // 菜单 DOM 列表
for(var i = 0; i < menus.length; i++){
var item = menus[i];
item.onclick = function(){
console.log(i); ==> 拿到最终 i 的值
}
}
在 let 关键字以前是通过 闭包 来解决该问题的:
for(var i = 0; i < menus.length; i++){
var item = menus[i];
item.onclick = (function(){
var _i = i;
return function(){
console.log(_i);
}
})();
}
现在有 let 关键字可以这么解决:
for(let i = 0; i < menus.length; i++){
let item = menus[i];
item.onclick = function(){
console.log(i);
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。