let const var 总结

写在前面

该格式的文字为个人理解,纯大白话

文章标题前加* 代表该知识点为不好理解或容易遗漏的知识点

1.var关键字

1.1 没有块级作用域的概念,有全局作用域、函数作用域的概念

例1:

//Global Scope
{
  var a = 10;
}
console.log(a);  //10

在大括号内部声明a,在大括号外部仍然可以访问,由此可以看出var声明的变量不存在块级作用域(Block Scope)的概念

例2:

//Global Scope
var a = 10;
function checkscope(){
    //Local Scope
    var b = 20;
    console.log(a);  //10
    console.log(b);  //20
}
checkscope();
console.log(b);  //ReferenceError: b is not defined

上面代码中,在 Global Scope 中用 var 声明了 a,在 checkscope 函数中的 Local Scope(本地作用域、函数作用域)中打印出了 10,但是在 Global Scope 中打印的变量 b 报错了。

在全局中声明了a,存在全局作用域中,函数(checkscope)内部也可以访问。在函数(checkscope)内部声明的变量,仅存在与函数内部作用域中,函数外无法访问

1.2 存在变量提升

例3:

//Global Scope
console.log(a);  //undefined
var a = 10;

checkscope();
function checkscope(){
    //Local Scope
    console.log(a);  //undefined
    var a;
}

先打印了a,然后用var声明变量a,打印结果为undefined。
变量提升是因为js需要经理编译和执行阶段。js在编译阶段的时候,会搜集所有的变量声明并且提前声明变量

1.3 全局作用域用var声明的变量会挂载到window对象上
//Global Scope
var a = 10;
console.log(a);  //10
console.log(window.a);  //10
console.log(this.a);  //10

上面代码中,打印出了 310,访问 awindow.a 或是 this.a 都是等价的。
举个例子:比如我要访问 location 对象,使用 location 可以访问,使用 window.location 也可以访问,只不过 window 对象可以省略不写,就像 new Array( )new window.Array( ) 是等价的。

1.4 同一作用域中允许重复声明
//Global Scope
var a = 10;
var a = 20;
console.log(a);  //20

checkscope();
function checkscope(){
    //Local Scope
    var b = 10;
    var b = 20;
    console.log(b);  //20
}

上面代码中,在 Global Scope 中声明了 2a,以最后一次声明有效,打印为 20。同理,在 Local Scope 也是一样的。

1.5 不初始化值默认为undefined
//Global Scope
var a;
console.log(a);  //undefined

Global Scope 中用 var 声明了 a,但没有初始化值,它的值默认为 undefined
这里是 undefinedundefined 类型,而不是字符串。

2.let关键字

2.1 有块级作用域的概念
{
   //Block Scope
   let a = 10;
}
console.log(a);  //ReferenceError: a is not defined

在大括号内声明的变量,在大括号外无法被访问

2.2 不存在变量提升
 console.log(a);  // Uncaught ReferenceError: a is not defined
  let a = 10;

上面代码中,打印 a 报错:无法在初始化之前访问。说明不存在变量提升。

2.3 同一作用域内不允许重复声明
{
  //Block Scope
  let A;
  var A;  //SyntaxError: Identifier 'A' has already been declared
}
{
  //Block Scope
  var A;
  let A;  //SyntaxError: Identifier 'A' has already been declared
}
{
  //Block Scope
  let A;
  let A;  //SyntaxError: Identifier 'A' has already been declared
}
*2.4 暂时性死区(dead zone)

在作用域内第一行到使用let、const声明语句之间,存在暂时性死区,访问该变量就会报错。

ECMAScript2015文档中的解释:
当程序的控制流程在新的作用域(module、functionblock 作用域)进行实例化时,在此作用域中用 let/const 声明的变量会先在作用域中被创建出来,但因此时还未进行词法绑定,所以是不能被访问的,如果访问就会抛出错误。因此,在这运行流程进入作用域创建变量,到变量可以被访问之间的这一段时间,就称之为暂时死区。

个人理解:let/const 命令会使区块形成封闭的作用域,在此作用域中,let/const* 声明的变量会先在作用域中被创建,一直到变量被声明之前,该变量都是不可用的,访问就会报错。在语法上称为 “暂时性死区”temporal dead zone,简称 TDZ

if (true) {
  //暂时性死区(TDZ)开始
  console.log(a);  //ReferenceError: Cannot access 'a' before initialization

  let a; //暂时性死区(TDZ)结束
  console.log(a);  //undefined

  a = 123;
  console.log(a);  //123
}

例4:

{
  //Block Scope
  console.log(a);  //ReferenceError: Cannot access 'a' before initialization
  let a = 20;
}

专属报错:初始化之前无法访问
在这里插入图片描述
区别:

这里的暂时性死区存在于块级作用域中,若在全局作用域中,则报错‘is not defined’

在这里插入图片描述

3.const关键字

const除了具有以上let中的四种特性(存在块级作用域、不存在变量提升、同一作用域内不允许重复声明、存在暂时性死区)之外,还有以下特点:

3.1 创建后必须立即初始化,不能留到以后赋值

在这里插入图片描述
const 声明的变量 a 没有进行初始化,所以报错。

*3.2 创建常量

const创建常量的特点:

const实际上保证的并不是变量的值不得改动,而是变量指向的那个内存地址不得改动。
对于简单类型的数据(数值、字符串、布尔值)而言,值就保存在变量指向的内存地址中,因此等同于常量。但对于复合类型的数据(主要是对象和数组)而言,变量指向的内存地址保存的只是一个指针,const只能保证这个指针是固定的,至于它指向的数据结构是不是可变的,这完全不能控制。

例5:
在这里插入图片描述

const 创建的变量的内存地址不可改变,而对于简单的数据类型,值就存在于变量指向的内存地址中,所有a的值不可以改变
例6:
在这里插入图片描述
对于复杂的数据类型,内存地址中保存的是一个指针,const只能保证这个指针是固定的,指针指向的数据结构是可以改变的

3.2.1 es5实现创建常量

Object.defineProperty
三个参数:

  1. 属性所在的对象
  2. 属性的名字
  3. 一个描述符对象:
    • configurable:表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性,默认值为false。
    • enumerable:表示能否通过for in循环访问属性,默认值为false
    • writable:表示能否修改属性的值。默认值为false。
    • value:包含这个属性的数据值。默认值为undefined。

实现:

Object.defineProperty(window,"args",{
value: ' this is es5',
writable: false
})

报错:常量变量赋值
在这里插入图片描述

3.2.1 实现const创建的引用类型为常量
const obj={
 id:1,
 age:18,
 name:'aaa'
}

obj.age=19       // cosnt 创建一个对象,对象中的属性可以被改变

追问1:如何使对象中的属性不可改变
解决:使用Object.freeze(),冻结对象,一个被冻结的对象再也不能被修改

const obj2={
 id:2,
 name:'bbb',
 age:20,
 food:['banana','apple']
}
Object.freeze(obj2)

obj2.age=21                 //被Object.freeze()冻结后,不可以改变    
obj2.foods[1]='pear'        //可以改变  freeze只能冻结根层 嵌套引用类型需要嵌套递归

解决:实现创建引用类型
思路:使用递归,将每一层的引用类型冰冻,实现创建引用类型的常量。

   function deepFreeze(obj) {
            Object.freeze(obj);
            (Object.keys(obj) || []).forEach((key) => {
                let innerObj = obj[key]
                if (typeof innerObj === 'object') {
                    deepFreeze(innerObj);
                }
            }
            )
        }


        const tempObj = {
            id: 23,
            name: '1',
            food: ['banana', 'apple']
        }
        deepFreeze(tempObj)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值