我的回答

let的特性

  • 在块级作用域内有效
  • 不能重复声明
  • 不能预处理,不存在变量提升,即未声明之前的代码不能调用

用匿名函数的作用域来模拟块级作用域,将用到let的代码放到匿名函数中,就不会造成变量污染了

  1. (function(){
  2. var c =3
  3. console.log(c) //1
  4. })()

const的特性

const用于声明一个常量

  • 在块级作用域内有效
  • 不能重复声明
  • 不能预处理,不存在变量提升,即未声明之前的代码不能调用
  • 不能修改(要注意,数组和对象属于引用数据类型,const保存的是指向对象的指针,所以修改其中的属性时指针不变,可以修改)
  • 使用时必须初始化(必须赋值)


数据属性

  • configurable:是否可以被 delete 删除或者改变特征值
  • enumerable:是否能通过 for-in 循环遍历返回属性
  • writable:是否可以修改属性的值
  • value:保存这个属性的数据值

访问器属性

  • configurable:能否通过 delete 删除,能否修改属性特性
  • enumerable:能否通过 for-in 循环返回属性
  • getter:读取属性时调用的函数,默认为 undefined
  • setter:写入属性时调用的函数,默认为 undefined

对于不可重复赋值,我们可以设置其configurable为false,同时 set中判断是否重复赋值,如果是即报错,而块级作用域可以用对象作为容器创造函数作用域代替 而如何修改这些属性呢,可以使用Object.defineProperty() 方法

Object.defineProperty(obj, prop, descriptor):会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
obj:要定义属性的对象
prop:要定义或修改的属性的名称
descriptor:要定义或修改的属性描述符

  1. function _const(key, value) {
  2. window[key] = value;
  3. Object.defineProperty(window, key, {
  4. enumerable: false,
  5. configurable: false,
  6. get: function () {
  7. return value;
  8. },
  9. set: function (newValue) {
  10. if (newValue !== value) {
  11. throw TypeError("这是只读变量,不可修改");
  12. } else {
  13. return value;
  14. }
  15. },
  16. });
  17. }

参考回答

ES6 let、const 块作用域实现原理

作用域是指在运行时代码中某些特定部分中变量、函数和对象的可访问性。可以这样理解:作用域就是一个独立的地盘,让变量不会外泄、暴露出去;也就是作用域最大的用处就是隔离变量,不同作用域下同名变量不会有冲突

编译阶段
    1. 通过 var 声明的变量,会提升到函数变量环境中
    1. let、const 声明的变量会被推入词法环境中
    1. 在代码块内,通过 let、const 声明的变量,既不会推入变量环境,也不会推入词法环境。
      执行阶段
    1. 代码执行到代码块,在代码块内,通过 let、const 声明的变量会被推入到词法环境,因此在代码块内这些变量是可访问的
    1. 当代码块执行结束时,在代码块内,通过 let、const 声明的变量会从词法环境中推出,因此在代码块外访问不到这些变量

      let、const 的实现

      let、const 块级声明用于声明在指定块的作用域之外无法访问的变量。let 用于声明变量,const 用于声明不可改变的常量

      let的看下 babel 的转化

      ```javascript // 源代码 var a = 2; { let a = 3; console.log(a); // 3 } console.log(a); // 2

// babel转换后的代码 var a = 2; { var _a = 3; console.log(_a); // 3 } console.log(a); // 2 // 可以看到使用的是babel的转换是使用var

  1. <a name="BAyld"></a>
  2. #### 通过立即执行函数实现 let
  3. ```javascript
  4. var a = 2;
  5. (function () {
  6. var a = 3;
  7. console.log(a); // 3
  8. })();
  9. console.log(a); // 2

通过 object.defineProperty(obj,prop,desc)实现 const

由于 ES5 环境没有 block 的概念,所以是无法百分之百实现 const,只要挂载到某个属性下,要么是全局的 window 要么就是自定义一个 object 来当容器

  1. function _const(data, value) {
  2. Object.defineProperty(window, data, {
  3. enumerable: false,
  4. configurable: false,
  5. get: function () {
  6. return value;
  7. },
  8. set: function (data) {
  9. throw new TypeError("Assignment to constant variable.");
  10. },
  11. });
  12. }
  13. _const("a", [1, 2]);
  14. a = [3, 4]; // 报错
  15. a.push(3);

注意事项

const 实际上保证的并不是变量的值不许改动,而是变量指向的那个内存地址所保存的数据不得改动。对于简单类型的数值,值就保存在变量指向的内存地址,因此等同于常量。
但是对于复杂类型的数值(数组,对象等),变量指向的内存地址,保存的是一个指向实际数值的指针,const只能保证这个指针式固定的(即是总指向另一个固定的地址),至于它指向的数据结构是不是可变的就说不清楚了。因此,将一个对象声明为常量的时候就必须小心使用了。