目录
声明风格及最佳实践
- 不使用 var
- const 优先,let 次之
var 关键字
1. var 使用
var message; // undefined (未赋初值)
var test = 'hi'; // 字符串类型
test = 100; // test 重写成数值 100 ,合法,但不推荐
2. var 声明作用域
- 如果使用 var 定义的变量是在函数内部定义,那么它是一个局部变量,会在函数退出时销毁。
function test() {
var message = 100; // 局部变量
}
test();
console.log(message); // 出错!
- 但是当省略 var 操作符的时候,就会使得变量成为全局变量
function test() {
message = 100; // 全局变量
}
test();
console.log(message); // 100
- 定义多个变量直接用逗号隔开
var message = 'hi', age = 10, time = 100;
3. var 声明提升
- 使用 var 声明的变量会自动提升搭配函数作用域的顶部
function foo() {
console.log(age);
var age = 20;
}
foo(); // undefined 不会报错
// 不会报错,因为提升后等价于
function foo() {
var age;
console.log(age);
age = 20;
}
foo(); // undefined
- 同时,可以反复多次使用 var 声明同一个变量
function foo() {
var age = 10;
var age = 11;
var age = 20;
console.log(age);
}
foo(); // 20
let 声明
1. let 作用域
let 是块级作用域,var 是函数级作用域
// 函数级作用域
if(ture) {
var name = 'hi';
console.log(name); // hi
}
console.log(name); // hi
// 块级作用域
if(ture) {
let name = 'hi';
console.log(name); // hi
}
console.log(name); // ReferenceError: name 没有定义
- 而且,同一个块级作用域不能出现冗余声明,会报错
var name;
var name;
let age;
let age; // SyntaxError: 标识符 age 已经声明过了
- let 和 var 混用也会报错
var name;
let name; // SyntaxError: 标识符 name 已经声明过了
let age;
let age; // SyntaxError: 标识符 age 已经声明过了
- 但是,Js 引擎会记录用于变量声明的标识符以及他们所在的作用域,因此嵌套使用相同的标识符不会出错,因为只要同一个块没有重复声明即可
var name = 'mike';
console.log(name); // mike
if(true) {
var name = 'alice';
console.log(name); // alice
}
let age = 10;
console.log(age); // 10
if(true) {
let age = 25;
console.log(age); // 25
}
2. 暂时性死区
产生暂时性死区的原因是因为 let 声明的变量不会在作用域中被提升,在使用未声明的变量到该变量声明之前被称为 “ 暂时性死区 ”
// name 会被提升
console.log(name); // undefined
var name = 'alice';
// age 不会被提升
console.log(age); // ReferenceError
let age = 10;
3. 全局声明(网易前端笔试题)
- var 关键字声明的对象会成为 window 对象的属性
- let 不会
var name = 'mike';
console.log(window.name); // mike
let age = 10;
console.log(window.age); // undefined
4. for 循环中的 let 声明
- let 声明的迭代变量的作用域仅限于 for 循环块的内部,而 var 声明的变量会渗透到循环体外部
for(var i = 0;i < 5;i++){
// 循环体
}
console.log(i); // 5
for(let i = 0;i < 5;i++){
// 循环体
}
console.log(i); // ReferenceError: i 没有定义
- var 渗透保存存在的问题(有关宏任务和微任务)
for(var i = 0;i < 5;i++) {
setTimeout(() => {
console.log(i);
},0)
}
// 输出结果为 5,5,5,5,5
原因:在退出循环时,迭代变量保存的时导致循环退出的值:5。在之后执行超时逻辑时,所有的 i 都是同一个变量,因而输出的都是同一个最终值。
- 而如果使用 let 声明迭代变量,Js 引擎在后台会为每个迭代循环声明一个新的迭代变量,所以每个 setTimeout 引用的都是不同的变量实例,所以每个值就不同。
for(let i = 0;i < 5;i++) {
setTimeout(() => {
console.log(i);
},0)
}
// 输出结果为 1,2,3,4,5
const 声明
1.基本用法
const 的行为与 let 基本相同,唯一一个重要区别是,const 声明变量时一定要初始化,且不能修改 const 声明的变量,会导致运行错误。
const age = 10;
age = 20; // TypeError: 给常量赋值
- const 不允许重复声明
- 作用域也是块级
- const 声明的限制只适用于它指向的变量的引用。例如:如果 const 变量引用的是一个对象,那么修改这个对象内部的属性并不违反 const 的限制,不会报错。
const person = {};
person.name = 'mike'; // ok
2. for 循环中的 const 声明
-
不能用 const 声明迭代变量,虽然跟 let 一样每次都会声明一个新的迭代变量,但是迭代变量会自增,所以不能用 const。
for(const i = 0;i < 5;i++) { // TypeError: 给常量赋值
// 循环体
}
- 同时,如果仅仅只是声明一个不会被修改的 for 循环变量,那是可以的。
let i = 0;
for(const j = 7;i < 5;i++) { // ok
console.log(j);
}
// 输出结果:7,7,7,7,7
for(const key in {a:1, b:2} ) {
console.log(key);
}
// a, b
for(const key of [1,2,3,4,5]) {
console.log(key);
}
// 1,2,3,4,5