var、let、const
// var -- (variable)、let、const -- (constant)
var name1 = 'varName'; // 没有作用域(会变量提升[作用域提升]) - 可重复定义后一个会覆盖前一个
let name2 = 'letName'; // 拥有块级作用域(不会变量提升) - 不可重复定义会直接报错
const name3 = 'constName'; // var、let可重新赋值 const不可重新赋值(let有的const也都有)
// 这种修改本质上修改的不是const的代理
const obj = {
// 因为test只是obj内部的一个值现在obj只是保护当前对象的内存地址不被改但并不保护内存地址指向内存中的值
test: '改我不会报错'
// obj = {} 等于一个新的对象会报错因为这代表要改引用
};
// 同样维护本数组地址并不会理会内部值
const arr = [];
var foo = 'foo';
var msg1 = 'Hello Word!';
let msg2 = '我不在window中';
console.log(window.foo);
console.log(msg1);
// 为什么是VE不是VO因为VO是早期非标准的(window = GO 而var声明的变量都会保存到GO中所以window可以根据内存地址访问这个值或修改)
window.msg1 = 'var在非函数作用域下声明的事物都会被绑定到VE中与window产生关联'
console.log(msg1);
// 因为window中没有你在这里window.msg2 等于是往window这个对象中追加了一个属性而并不是修改上面的msg2
window.msg2 = '别做梦了使用windo访问不到我了'
console.log(msg2);
块级作用域
// 块级作用域(内部变量、方法外部无法访问只限于let、const而var不存在块级作用域的概念) js中只存在三种作用域:1.全局作用域 2.函数作用域 3.块级作用域
{
var foo = 'foo';
// let是有块级作用域的外界访问不到
let bar = 'bar';
// const 也一样
const baz = 'baz';
// 这里是能访问到的因为在当前块级作用域内
console.log(bar);
// 块级作用域是生效的
class Person { };
// 函数在块级作用域中是不生效的(因为浏览器要兼容早期的代码)
function demo() {
console.log('demo');
}
// 不想让外部访问可以写成这样
let fn = function () {
// 或者用const
console.log('demo');
}
}
console.log(foo); // 因为var在块中定义的事物是不起作用域作用的所以可以访问
// console.log(bar); 因为bar在块级作用域内所以在全家作用域下是访问不到的
demo();
// 其它拥有块级作用域的:if、for、switch
if (true) {
var varIfVarable = 'varIfVarable';
// 请记住let有的const都有
let letIfVarable = 'letIfVarable';
console.log(letIfVarable);
}
console.log(varIfVarable);
// console.log(letIfVarable); 访问不到因为if有块级作用域
switch ('block') {
case 'block':
var varSwitchVarable = 'varSwitchVarable';
let letSwitchVarable = 'letSwitchVarable';
console.log(letSwitchVarable);
break;
}
console.log(varSwitchVarable);
// console.log(letSwitchVarable); 访问不到因为switch有块级作用域
for (let i = 0; i < 1; i++) {
var varForVarable = 'varForVarable';
let letForVarable = 'letForVarable';
};
console.log(varForVarable);
// 访问不到因为有块级作用域
// console.log(i);
// console.log(letForVarable);
for (var k = 0; k < 1; k++) {
// 内部同理
}
// 访问的到因为var没有作用域
console.log(k);
没有块级作用域时var存在的问题
for (var i = 0; i < 3; i++) {
let btn = document.createElement('button');
// 这里的 i 是没问题的因为每次循环创建时访问的的确是正确的值
btn.innerText = `按钮${i}`;
btn.addEventListener('click', () => {
// 但是这里不行 原因是循环结束了 i 已经变成了 3 这里触发事件每次访问的都是固定数字了
console.log(i);
/*
详解:
(1).因为 var 创建的变量是存在早期的 GO 中的
(2).每次循环 i++ 时都会去全局中找 i 并进行修改
(3).当你触发事件时当前作用域并不存在 i 就会一层一层的找到 GO
(4).而此时 GO 中存储的是循环最后一次 ++ 后的值
*/
});
document.body.append(btn);
};
// 循环结束后GO中 i 为 3
console.log(i);
早期解决方法
for (var i = 0; i < 3; i++) {
let btn = document.createElement('button');
btn.innerText = `按钮${i}`;
// 注意这里的沙箱
(function (i) {
// 等于这里有 var i = 0;、var i = 1;、var i = 2;
btn.addEventListener('click', () => {
// 这里访问的时候会优先找到上级作用域(也就是上面的伪代码)
console.log(i);
});
document.body.append(btn);
})(i);
};
// 但你只要用了 var 全局 GO 中依旧会受到影响
console.log(i);
现代解决方法
// 解决方法(只需要把var换成let)
for (let i = 0; i < 3; i++) {
let btn = document.createElement('button');
btn.innerText = `按钮${i}`;
btn.addEventListener('click', () => {
// 而此时 log 去找上层的时候就找到了 i
console.log(i);
});
document.body.append(btn);
};
// console.log(i); 这里就访问不到 i 了因为let、const声明的变量不会存储到GO中window也就访问不到了
暂时性死区(Temporary dead zone)
// 上面说过var定义的variable存到了全局GO中
var foo = 'foo';
{
// 因为log时会先在本块级作用域找foo找到了但是还没赋值就造成了暂时性死区(直至foo赋值后)
console.log(foo);
// 在赋值之前无法访问
let foo = '不会变量提升但会造成暂时性死区';
};