炒冷饭
刚开始入门学的时候了解的,现在翻出来炒炒。
const let问题
刚在复习js info的对象的时候遇到解释const声明的对象可以改变的问题,进而又了解到变量提升问题,发现有些东西还不是很熟悉,拜读下别人的详解,做下笔记方便review.
1. 基本概念
const一旦声明一个只读的变量,常量的值就不可修改。const声明的常量的值不得改变,所以一开始声明的时候就要赋值初始化,不能留到以后赋值,只声明不赋值就会报错。
const的作用域跟let一样,只在声明的块级作用域内生效
2. const声明对象
const实际上保证的,并不是变量的值不能改动,而是变量指向的那个内存地址所保存的数据不得改动。对于简单类型的变量,值就保存在变量指向的内存地址,因此等于常量。但对于复合型的数据,主要是对象和数组,变量指向的内存地址保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的,至于它指向的数据结构是不是可变的,就完全不能控制了。------摘自ES6入门
const foo = {}
//为foo添加属性,可以成功
foo.name = 'wawawwa'
foo.name //wawawa
//将foo指向另一个对象,就会报错
foo = {} //TypeError: "foo" is read-only
3. let const 变量是否会提升?
以前接触到的知识点都是说明let const变量是不存在变量提升的,但是这次复习深入的去搜了一下关于暂时死区(Temporal Dead Zone=TDZ)的知识时,发现let const的变量是否提升问题的答案不是那么一致。最终的结论是变量的声明确实提升了。下面是论证:
1.变量提升概念
- 其实js和其他语言一样都需要经历编译和执行阶段,但是js编译器在编译阶段会收集所有变量声明,并将变量声明提前到变量当前所在作用域的顶部。使用var声明变量,在函数内部是局部变量,在函数外部是全局变量,没有用var声明的变量,在函数外部内部都是全局变量,但如果在函数内部声明,也叫隐式全局变量(这点在后面的编译我会再讲下)
- 函数声明也会提升function foo(),但是函数表达式不会提升var foo = function(),这是因为js编译器在编译阶段优先读取函数声明的代码,以确保函数能够被引用到,也可以理解成foo = function声明的变量foo被提升了,但是赋值语句 = function被留在原地,所以函数表达式不会被提升,函数提升优于变量提升。
console.log(foo) //[Function: foo]
var foo = 10;
function() foo {
}
2.let和const存在变量提升吗
结论是其实let和const是会被提升的,准确来说是创建被提升了,但是初始化没有被提升。
根据ECMA-262中13.3.1的NOTE中所述大概意思:let和const定义了变量执行的词法环境(上下文环境),变量会在这个环境实例化时被创建,但是在变量的词法绑定(赋值操作)之前不允许以任何方式对其进行访问......var和let的区别在于后者是在编译时才初始化的。 所以说我们可以这样理解:let声明的变量分为三部分1.创建,2.初始化,3.赋值。而通过const声明分为两部分:1.创建,2.初始化,没有赋值操作,其实是相当于把初始化赋值整合为一步,在初始化的时候进行赋值。而var声明则分成两部分,1.创建同时进行初始化,2.赋值。 所以会有
//通过var声明的变量,创建和初始化都提升,被提升后初始化为undefined
console.log(a) //undefined
var a = 1
而let和const都仅仅是提升了变量的创建
let a = 1;
if(true) {
//此时其实a的创建已经被提升到了if代码块的顶部
a = 2; //ReferenceError
let a = 3; //完成对a的初始化并赋值
}
这里可以拆成这样理解
if(true) {
//此时a的创建已经被提升到了if代码块内部
a = 2; //ReferenceError; 此时对a进行赋值,由于a仅仅被创建,还没有被初始化,所以会报错,a is not define
let a 完成a的初始化,此时a为undefined
a = 3完成对a的操作
}
引入暂时性死区(TDZ),就是let从创建到被提升到初始化这中间部分,在这部分中变量都是不可用的
if (true) {
// a的创建被提升,TDZ的开始
a = 2; // ReferenceError;
let a; // 完成a的初始化,TDZ的结束
a = 3; // 完成对a的赋值操作。 }