一天,你去面试了,面试官问你,你的前端基础能力怎么样?
你:我的基础很扎实。
面试官:变量相关的知识点能说出 10 个吗?
你:哈,哦,我尝试说一下
你:第一点,我们可以用 var、let、const、function 常规关键词声明变量,另外还有 class、import;
你:第二点,var 声明的变量会提升,function 声明的函数有同等变量提升的效果。其他变量声明关键词不会。什么是变量提升呢,举个例子:
function varUp() {
console.log(name); // 在 var 声明变量之前就直接使用变量,不会报错
var name = '超人迪迦';
}
varUp(); // 执行后打印 undefined ,不会报错
function functionUp(){
console.log(functionA); // 在 function 声明之前就直接使用 function,不会报错
function functionA(){}
}
functionUp(); // 执行后打印 ƒ functionA(){} ,不会报错
事实上,上面的代码等价于下面的代码:
function varUp() {
var name; // 执行时,JS 引擎偷偷的在背后帮你提前声明了变量,这就是变量提升
console.log(name);
name = '超人迪迦';
}
varUp(); // 执行后打印 undefined ,不会报错
function functionUp(){
function functionA(){} // JS 引擎在执行函数代码之前会把函数添加到作用域,类似变量提升功能
console.log(functionA);
}
functionUp(); // 执行后打印 ƒ functionA(){} ,不会报错
你:第三点,相比 var 和 let,const 声明的变量必须声明时立即赋值,否则后面就无法赋值了;
你:第四点,const 赋值后,无法重新赋值,不过,对于引用类型,还是可以修改其属性值的,请看:

你:第五点,var 声明的变量要么是函数级要么是全局作用域,而 let 和 const 则是块级(注意,块级是包含函数级和例如 if、for 等代码块级)。
/** ========== var 函数级变量 Start ========= */
function varFunctionLevel() {
var varFunction1 = '123';
if(true){
var varFunction2 = '456';
}
console.log(varFunction1); // 打印 “123”
console.log(varFunction2); // 打印 “456”
}
varFunctionLevel();
console.log(varFunction1); // 执行后报错,varFunction1 变量只是函数级,函数外无法访问
/** ========== var 函数级变量 End ========= */
/** ========== var 全局作用域 Start ========= */
// 直接在浏览器控制台输入
var varGlobal1 = '123'; // 省略 var 关键词可获得一样的效果
varGlobal2 = '456';
console.log(window.varGlobal1); // 打印 “123”
console.log(window.varGlobal2); // 打印 “456”
/** ========== var 全局作用域 End ========= */
/** ========== let、const 块级作用域 Start ========= */
function letBlockLevel(){
let letA = 1;
if(true){
let letB = 2;
}
console.log(letA); // 打印 “1”
console.log(letB); // 执行到此句会报错,letB 只能在 if 代码块内访问
}
letBlockLevel();
/** ========== let、const 块级作用域 End ========= */
你:第六点,循环中的变量声明。循环中使用 var 声明的变量会提升到整个函数或者全局作用域内,而使用 let 声明则不会,其原因是 let 声明时,JS 引擎会在每次循环的时候创建一个新的变量给当次循环使用。
function varFor() {
console.log(i); // 下面循环代码使用 var 定义的 i 变量会提升成函数级变量
for (var i = 0; i < 3; ++i) {
setTimeout(() => console.log(i), 0);
}
console.log(i); // 循环代码使用 var 定义的 i 变量会提升成函数级变量
}
varFor(); // 执行后,实际输出 1 次 undefined 和 4 次 “3”,而不是想当然的报错或者 “undefined”,“0”,“1”,“2”,“3”。setTimeout 是异步代码,其他代码是同步代码,同步代码执行完成后,i 的值已经是 3,此时再执行异步代码时,输出的自然是 3
function letFor1() {
console.log(i); // 执行到此处直接报错了,i 未定义
for (let i = 0; i < 3; ++i) {
setTimeout(() => console.log(i), 0);
}
console.log(i);
}
letFor1(); // 执行后,直接报错
function letFor2() {
for (let i = 0; i < 3; ++i) {
setTimeout(() => console.log(i), 0);
}
console.log(i); // 执行到此处后代码报错,因为 i 未定义(let 定义的变量不会提升)
}
letFor2(); // 执行后,打印 “0”、“1”、“2” 后,报错
你:第七点,使用 function 声明的函数,有一个不成文约定,声明的函数小写开头时即为普通函数,大写开头时即为构造函数,两者本质上无区别,就是命名上大小写区别。
/** 普通函数 */
function func(){}
/** 构造器函数 */
function Func(){}
你:第八点,使用 function 声明函数时,可不提供函数名,此时声明的函数为匿名函数。
/** 匿名函数 */
const func = function(){
console.log('这是一个匿名函数!');
};
func(); // 打印 “这是一个匿名函数!”
你:第九点,使用 function 声明函数时,用括号包裹,然后在后面加上括号 () 就可以立即执行,我们称为“立即执行函数”。现代打包工具,如:Webpack 打包出来的产物便是如此。
/** 匿名立即执行函数 */
(function(){
console.log('这是一个匿名立即执行函数!');
})(); // 打印 “这是一个匿名立即执行函数!”
你:第十点,class 其实是 JS 的语法糖,忠于面向对象编程的程序员,那必须是最爱,用它可方便实现继承等功能。
你:第十一点,至于最佳实践,那必须是尽可能放弃 var,放开怀抱拥抱 let 和 const,使用 const 定义枚举变量,那不是美滋滋。
面试官:口答:嗯。心想:这小子基础能力很 6 嘛!
期待面试官通知你面试!