欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

ES6第一章关于let和const不得不说的故事

程序员文章站 2023-12-21 23:37:16
...

发现自己有个毛病。。。不喜欢及时更博,喜欢干完之后再写,,要改要改要改!!!
在这里我就不啰嗦了,只写我认为重要的 其他的阮一峰老师(比心❤ )的书里都有。电子版传送门废话不说了 let’s go

1.let 与var

var a = [];
for (var i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[6](); // 10
var a = [];
for (let i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[6](); // 6

var 声明的是全局的变量,所以只有一个,所有数组a的成员里面的i,指向的都是同一个i,循环对于他就是循环赋值,最后一次赋给他的是10,所以i=10,导致运行时输出的是最后一轮的i的值,也就是10。but,let声明的变量仅在块级作用域内有效(即声明for循环的那一行),所以在每次循环时都会重新声明一个变量i,然后赋值给他,所以最后输出的是6。 讲道理重新赋值之后浏览器为什么还记得上一个i是多少呢,原来是因为这是因为 JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算。 这句话可以说是肥肠······ ~~(>_<)~~
另外还有一点,for循环存在两个块级作用域,第一个是声明for循环的那一行(没错就是一行!),第二个就是大括号{}里面的,

for (let i = 0; i < 3; i++) {
  let i = 'abc';
  console.log(i);
}
// abc
// abc
// abc

2 let不存在变量提升~

我们都知道var声明的变量存在变量提升,但是let没有啊 这就非常好了,总结一下就是:let命令,它所声明的变量一定要在声明后使用,否则报错。

3. 暂时性死区

var tmp = 123;

if (true) {
  tmp = 'abc'; // ReferenceError
  let tmp;
}

看上面的代码,var全局声明了一个变量tmp,照常理在if里给它重新赋值没什么毛病,但是下面有一句
let tmp 我们知道let声明的变量作用域是块级作用域,即tmp此时属于if里的,但是你在let被声明前就使用它对它赋值,这就报错了。 值得注意的有以下几点,直接贴代码

 typeof x; // ReferenceError
let x;
function bar(x = y, y = 2) {
  return [x, y];
}

bar(); // 报错
function bar(x = 2, y = x) {
  return [x, y];
}
bar(); // [2, 2]

暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。

4. let const 不允许重复声明

需要注意的是函数这里,

function func(arg) {
  let arg; // 报错
}

function func(arg) {
  {
    let arg; // 不报错,因为不在同一个作用域
  }
}

5. 块级作用域

其他内容比较好理解,就不提了

// IIFE 写法(立即执行函数)
(function () {
  var tmp = ...;
  ...
}());

// 块级作用域写法
{
  let tmp = ...;
  ...
}

6.块级作用域与函数声明

。。。。这一节我看的跌宕起伏(手动捂脸 ╯□╰),本来是贴了好多东西!但我决定删了,宝宝们(巨婴们啊)自己去看好了,就贴一下自己的理解,防止有照搬照抄的嫌疑
ES5 规定,函数只能在顶层作用域和函数作用域之中声明,不能在块级作用域声明。但是,浏览器没有遵守这个规定,为了兼容以前的旧代码,还是支持在块级作用域之中声明函数,因此上面两种情况实际都能运行,不会报错。ES6 引入了块级作用域,明确允许在块级作用域之中声明函数。ES6 规定,块级作用域之中,函数声明语句的行为类似于let,在块级作用域之外不可引用。
大概总结一下对于es6的浏览器 块级作用域里的函数声明还是相当于var声明,对于非es6,按let声明处理!!最好用函数表达式
需要注意。ES6 的块级作用域允许声明函数的规则,只在使用大括号的情况下成立,如果没有使用大括号,就会报错。

// 不报错
'use strict';
if (true) {
  function f() {}
}

// 报错
'use strict';
if (true)
  function f() {}

7.do表达式(一个提案!)

外面作用域无法获取到块级作用域里let const声明的东西(原因在前面)本质上,块级作用域是一个语句,将多个操作封装在一起,没有返回值。

let x = do {
  let t = f();
  t * t + 1;
};

do使得块级作用域可以变为表达式,也就是说可以返回值,x可以得到块级作用域里的返回值

8. const,浅冻结,深冻结(对象彻底冻结。。。)

对于const来说,只声明不赋值,就会报错。
const 与let:只在声明所在的块级作用域内有效,也存在暂时性死区,变量不提升,也不可重复声明

本质(这段话挺重要~~直接拷过来)
const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指针,const只能保证这个指针是固定的,至于它指向的数据结构是不是可变的,就完全不能控制了。因此,将一个对象声明为常量必须非常小心。
const foo = {};

// 为 foo 添加一个属性,可以成功,对象本身可以改变
foo.prop = 123;
foo.prop // 123

// 将 foo 指向另一个对象,就会报错,地址不可变
foo = {}; // TypeError: "foo" is read-only
const a = [];
a.push('Hello'); // 可执行,数组本身可写
a.length = 0;    // 可执行
a = ['Dave'];    // 报错,不能把另一个数组赋值给他

对象冻结:Object.freeze()用来冻结一个对象。冻结对象是指那些不能添加新的属性,不能修改已有属性的值,不能删除已有属性,以及不能修改已有属性的可枚举性、可配置性、可写性的对象。也就是说,这个对象永远是不可变的。该方法返回被冻结的对象。想将对象彻底冻结,它的属性也要冻结,下面贴一下彻底冻结一个对象的方法,如果属性值是对象,还是只能冻结该对象的地址,而内容还是可以进行修改,除非它也是个冻结对象。(浅冻结) 参考资料MDN object.freeze()

var constantize = (obj) => {
  Object.freeze(obj);
  Object.keys(obj).forEach( (key, i) => {
    if ( typeof obj[key] === 'object' ) {
      constantize( obj[key] );
    }
  });
};
function deepFreeze (o) {
  var prop, propKey;
  Object.freeze(o); // 首先冻结第一层对象.
  for (propKey in o) {  //循环属性名
    prop = o[propKey];  //得到此时对应的属性值
    if (!o.hasOwnProperty(propKey) || !(typeof prop === "object") || Object.isFrozen(prop)) {
      // 跳过原型链上的属性和对象的属性值不是对象的属性and已冻结的属性.
      continue;
    }
     deepFreeze(prop); //递归调用.
  }
}
deepFreeze({first: 1,second:{obj: [1,2,3]},third: 3})

上一篇:

下一篇: