let与var、const的区别
稍微熟悉前端,都应该回答出来,很经典的面试题
let与var的区别
let作用域不会被提升
- 示例
console.log(b) // 报错
let b = 1
console.log(a) // undefined
var a = 1
- 原因
js引擎遇到var声明,会把它的作用域提升到顶部,而遇到let或const会放到TDZ(Temporal Dead Zone,暂时性死区),访问TDZ的变量会触发运行时错误,只有执行变量声明的语句之后,变量才会从TDZ中取出。
let重复声明会报错
- 示例
var a = 2
var a = 22
console.log(a) // 22
let a = 2
let a = 22
console.log(a) // Identifier 'a' has already been declared
- 原因
因为let不允许在相同作用域内重复声明相同的变量,注意关键词 "相同作用域",下面的例子是没问题的
let a = 2
function f(){
let a =3
console.log(a)
}
f()
let不绑定全局作用域
- 示例
let a = 1
console.log(window.a) // undefined
var a = 2
console.log(window.a) // a
- 原因
暂没有想到
let与const的区别
const 一般用来声明常量,一旦声明一般类型,值就可以被修改,但是声明引用类型的值,值可以被修改,但是绑定不可以修改
- 示例
const a = 2;
a = 3 // 可以
const a = {name: "nanlan",age: 99}
a.name = "xiaojuju" // 可以
a.sex = "female" // 可以
a = { } // 不可以,"a" is read-only
a = {name:"nanlan”,age: 99,hobby: "coding"} // 不可以,"a" is read-only
const arr = [1,2,3,4]
arr[1] = 88 // 可以
arr.push(5) // 可以
arr.splice(0,2) // 可以
arr.pop() // 可以
arr = [] // 不可以,"arr" is read-only
- 原因
原始数据类型: undefined、null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)、Symbol
细分的话,普通类型(String、Null、Undefined、Number、Boolean、Symbol)与引用类型(Function、Object、Array)存储的方式不一样。 请看下图(太丑请尽情吐槽)
MDN截图
总结: 我理解的就是const声明就是一个值的只读引用。普通类型的地址也存放在栈内存中,引用类型在栈内存中存放了指向堆内存的地址,堆内存的值可以改变,栈内存的地址不可改变。
更好的答案:
Object.freeze
冻结对象,如果对象里面嵌套了对象,则无效,目前想到的办法就是递归调用。比如
// 代码1
const foo = { a: "aaa", b: "bbb", c: "ccc"}
Object.freeze(foo)
foo.c = "ddd"
console.log("foo",foo) // { a: "aaa", b: "bbb", c: "ccc"} // 不变
// 代码2
const foo = { a: "aaa", b: "bbb", c: "ccc", d:{e:"eee"} }
Object.freeze(foo)
foo.d.e ="ddee"
console.log("foo",foo) // { a: "aaa", b: "bbb", c: "ccc", d:{ e:"ddee"} }
// 会变
deepFreeze
function deepFreeze(obj) {
if (Object.prototype.toString.call(obj) !== "[object Object]") {
throw new TypeError("Excepted Object,got " + typeof obj);
}
Object.keys(obj).forEach(function (name) {
if (typeof obj[name] == "object" && obj[name] !== null)
deepFreeze(obj[name]);
});
return Object.freeze(obj);
}
const foo = { a: "ddd", b: "bbb", c: "ccc", d: { e: "eee" } };
deepFreeze(foo);
// Object.freeze(foo)
foo.d.e = "ddee";
console.log(foo); // { a: "ddd", b: "bbb", c: "ccc", d: { e: "eee" } }
// 不变
块级作用域
一个经典的面试题
// 代码1
var funs = []
for(var i=0;i<3;i++){
funs[i] = function(){
console.log(i)
}
}
// funs[0]() // 3
// 代码2
for(let i=0;i<3;i++){
funs[i] = function(){
console.log(i)
}
}
// funs[0]() // 0
// 代码2的伪代码
// 伪代码
(let i = 0) {
funcs[0] = function() {
console.log(i)
};
}
(let i = 1) {
funcs[1] = function() {
console.log(i)
};
}
(let i = 2) {
funcs[2] = function() {
console.log(i)
};
};
所以代码2的原因是因为存在块级作用域。即每次循环都会创建一个新的变量(按理来说,let声明的变量不可重复),其实循环的圆括号内会创建一个新的作用域,所以避免了let不可重复声明的特性,可以理解为模仿闭包来简化循环过程
下面是来自阮老师的例子,先不看答案
var tmp = new Date();
function f() {
console.log(tmp);
if (false) {
var tmp = 'hello world'; // 将这里换成let试试,思考下结果的区别和原因
}
}
f();
值得注意的是,在块级作用域中声明函数相当于是用var关键字声明,作用域会被提升到块级作用域的头部。猜猜下面的结果是多少
function f() { console.log('I am outside!'); }
(function () {
var f = undefined;
if (false) {
function f() { console.log('I am inside!'); }
}
f();
}());
参照:
ES6 系列之 let 和 const
MDN const
JavaScript 深入了解基本类型和引用类型的值
let 和 const 命令
「前端进阶」JS中的栈内存堆内存
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!