【ES6】var和let/const的区别

1. var声明的变量会挂载在window上,而let和const声明的变量不会

let / const允许把变量的作用域限制在块级域中;var 申明变量要么是全局的,挂载在window上,要么是函数级的,限制在函数作用域内,而无法是块级的。

function fn(){
    var username = '1';
    console.log(username);
}
fn();
console.log(username);  // 报错  变量存在函数作用域内。

2. var声明变量存在变量提升,let和const不存在变量提升

console.log(mom);    // undefined  虽然开始没有声明mom但是,默认赋值undefined。
var mom = '我是妈妈';  
// 整个过程相当于
var mom;
console.log(mom);
mom = '我是妈妈';

console.log(mom);      // error 报错
let mom = '我是妈妈';   // let和const不存在变量提升
// var 存在变量提升,而 let,const(后面会提及)声明的变量却不存在变量提升,所以用 let 定义的变量一定要在声明后再使用,否则会报错。

参照后文中暂存死区部分。

3. let和const声明形成块作用域

  • var:只有全局作用域和函数作用域概念,没有块级作用域的概念。但是会把{}内也假称为块作用域。
  • let / const:只有块级作用域的概念 ,由 { } 包括起来,if语句和for语句里面的{ }也属于块级作用域。
  • 块级作用域可以嵌套。 内部的作用域可以访问到外层作用域

4. 同一作用域下let和const不能声明同名变量,而var可以

for (var i = 0; i < 3; i++) {
    console.log(i);   // 0 1 2
}
console.log(i);       // 3      var声明了变量之后,就一直存在,for循环结束后,他还在。
var i;
console.log(i);       // 依然还是3
for (var i = 0; i < 3; i++) {    // 可以继续声明i,并重新赋值
    console.log(i);   // 0 1 2
}

let son = '这是son';
let son;   // error 变量已经声明过了

// 但是
let letClass = function(){
    let name='xiaoming';
    if(true){
        let name='saucxs';
        console.log(name);
    }
    console.log(name);
}
letClass(); 
// 上面的结果说明了let只在{}内使用。

// 先let后var
let subClass = function(){
    let name='xiaoming';
    if(true){
        var name='saucxs';     // error
        console.log(name);
    }
    console.log(name);
}
subClass();
// var 是函数级作用域,相当于这个函数作用域中有let和var声明的两个name的变量了
// var 作用于整个 subClass,不仅仅是if(if只是个块级作用域,无法限制var) ,和let冲突了,let不能重复声明,already been declared=已经被声明

// 先var后let     可以正常运行,块级作用域内let覆盖了var定义的变量
let subClass = function(){
    var name='xiaoming';
    if(true){
        let name='saucxs';
        console.log(name);
    }
    console.log(name);
}
subClass();

5. const和let区别

const一旦声明必须赋值, 必须赋值,必须赋值!
不能使用null占位;声明后不能再修改 ;如果声明的是复合类型数据,可以修改其属性。具体参照后文中const的补充部分。

暂存死区

ES6明确规定,如果区块中存在let命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。所以在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)。

var monkey = '111';
{
    console.log(monkey);  //  111  块级作用域对于var没效果,会自动查找上面的变量
    var monkey = '222';
}
console.log(monkey);     //   222  块级作用域中修改了变量,生效了
let monkey = '111';
{
    console.log(monkey);   //  error  let直接封闭作用域,这里的变量,不能向上找了,向下又找不到
    let monkey = '222';
}
console.log(monkey);
let monkey = '111';
{
    let monkey = '222';
    console.log(monkey);   // 222  222这个let只在当前作用域内生效
}
console.log(monkey);       // 111
let a = 'outside';
if(true) {
   console.log(a);			//Uncaught ReferenceError: a is not defined
   let a = "inside";
}
// 当前作用域顶部到该变量声明位置中间的部分,都是该let变量的死区,在死区中,禁止访问该变量。由此,我们给出结论,let声明的变量存在变量提升, 但是由于死区我们无法在声明前访问这个变量。
// “暂时性死区”也意味着typeof不再是一个百分之百安全的操作,因为会使typeof报错。 
{
	typeof name;		//ReferenceError
	let name;
}
// 只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。在代码块中,使用let命令声明变量之前,该变量都是不可用的,这在语法上称为“暂时性死亡”。

常量const的补充

除了let以外,ES6还引入了cons,const 和 let 的作用域是一致的,不同的是 const 变量一旦被赋值,就不能再改变了,但是这并不意味着使用 const 声明的变量本身不可变,只是说它不可被再次赋值了,而且const 声明的变量必须经过初始化。

  • 声明常量
const a = 1;   		 // 常量在声明的时候必须被初始化
a = 2;  			 // error  常量不能被修改
const xiaoming = {   // 这是一个引用类型的常量  里面的内容可以修改
    age: 14,
    name: 'xiaoming',
}; 

// 注意,修改其属性值是没问题的,不能修改引用
xiaoming = { age: 13, }   // 报错
xiaoming.age = 22;        // const只会保证这个引用常量的地址不发生改变,修改里面的值不会改变地址。
console.log(xiaoming);   // 此处age变成了22
xiaoming = {};           // 报错

const ARR = [];
ARR.push(1);   // 可以改变数组中的元素。
  • Question1: 解决引用类型的常量可以被修改的问题
// 使用Object.freeze()
 const xiaoming = {
     age: 14,
     name: 'xiaoming',
 };
 Object.freeze(xiaoming);
 xiaoming.age = 22;
 console.log(xiaoming);   // 虽然不会报错,但是没有任何修改
  • Question2: ES6之前怎么声明常量
// 使用Object.defineProperty();
var CST = {};
Object.defineProperty(CST, 'BASE_NAME',{
    value: 'xiaoming',   // BASE_NAME = 'xiaoming'
    writable: false,    // 该对象,不可被修改
});
// 上面这样只是不能修改里面的属性。但是可以加属性   例如: CST.a = 2;   就可以加进去
Object.seal(CST);  // 只有freeze一半的功能,不允许拓展,但是可以修改内部的值

ES6之前声明常量 (实现freeze效果)
1.遍历属性和方法
2.修改遍历到的属性和描述
3.Object.seal()

Object.defineProperty(Object, 'freezePolyfill', {
    // 相当于 Object.freezePolyfill = function(){}  这是自己定义一个函数
    value: function (obj) {
        var i;
        for(i in obj){
            // for(i in obj)  不光会遍历到obj下的属性,还会遍历到obj._proto_下的属性。  hasOwnProperty确保遍历到的只是obj下的属性
            /*
            var obj1 = {a:1 , b :2,};
            var obj2 = Object.create(obj1);   // 把obj1作为原型创建obj2,此时obj1的属性就会作为obj2._proto_ 保存
            obj2.c = 3;                       // obj2 = { c: 3, d : 4, _proto : {a : 1, b : 2,} }
            obj2.d = 4;
            for(let i in obj2){ document.body.innerHTML += (i + ': ' + obj2[i] + '<br>');    }    // 此处会遍历出所有a,b,c,d
            */
            if(obj.hasOwnProperty(i)){
                Object.defineProperty(obj, i, {
                    writable: false,
                });
            }
        }
        Object.seal(obj);
    }
});
const xiaoming = {
    age: 14,
    name: 'xiaoming',
};
Object.freezePolyfill(xiaoming);   // 此时  这个引用类的常量内部的属性都不能修改了

相关问题

Question: 生成十个按钮,每个点击的时候弹出1 - 10

var i = 0;
for (i = 1; i <= 10; i++) {
    // 一个自运行的函数 (function (i))(i)       相当于(function (i))(1)   (function (i))(2)   (function (i))(3) ...
    // 这个函数里面的i是独立的作用域,是最后(i)传进来的
    (function (i) {
        var btn = document.createElement('button');
        btn.innerText = i;
        btn.onclick = function () {
            alert(i);
        };
        document.body.appendChild(btn);
    })(i)
}
// 去掉自运行的函数,结果会生成1-10个按钮,但是弹出来的永远是11
var i = 0;
for (i = 1; i <= 10; i++) {
    var btn = document.createElement('button');
    // 这个i会遵循1-10的循环,所以生成按钮内容都是1-10
    btn.innerText = i;
    //  这里的i并不参与循环,相当于给每个按钮绑定一个事件。此时alert里面的值没确定。每次点击按钮的时候,alert(i)就会向上去找i,此时i已经结束循环了,变成11
    btn.onclick = function () {
        alert(i);
    };
    document.body.appendChild(btn);
}
// 用let就和其他java等语言一样,这个i只作用于当前for循环的作用域内,不需要自运行函数
for (let i = 1; i <= 10; i++) {
    var btn = document.createElement('button');
    btn.innerText = i;
    btn.onclick = function () {
        alert(i);
    };
    document.body.appendChild(btn);
}

此问题详细解释,参考另一篇文章

  • 7
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值