2018-12-20-周末学习

ES6的深入认识(1)

let&const

首先列出我对于let和cons起初粗浅的认识

  • let ≈ var,const只是定义一个不可变常量
  • 二者均不支持提升

学习总结后的一些认识:
(1) var在全局创建的变量,全局各处均可以访问到,let则不是,如果在代码块中创建,则只能在代码块中访问。

验证:
第一点认识起初不是很明朗,于是进行了如下的验证:

var arr01 = [],
        arr02 = [];

    for(let i = 0; i < 5; i++) {
      arr01.push(function() {
        console.log('i in arr01', i);
      });
    }

    for(var i = 0; i < 5; i++) {
      arr02.push(function() {
        console.log('i in arr02', i);
      });
    }

    arr01.forEach(item => item());
    arr02.forEach(item => item());

结果

image.png

结论:很明显,var创建的i一轮又一轮被重新赋值,到最后我们执行打印函数的时候,全局的i只等于最后循环结束时被赋予的值了,前面的值均已经被覆盖掉,let创建的i则健全的保留了每一次创建时被赋予的新值。

(2) var创建的变量,在循环中始终保持只有一个变量被循环覆盖式赋值,而let在循环中每一次都会创建一个新的变量,赋予新值(js的内部引擎会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算。)

(3)暂时性死区
在代码块中,如果我们在没有用let或者const去声明一个变量之前就使用了该变量,那么就会报引用错误。换一句话说,也就是在进入当前作用域时,需要使用的变量就已经存在,但是不可获取,只有在遇到声明该变量的这一行代码出现之后,才可以正常的使用。

(4)块级作用域存在的意义
内层变量可能会覆盖外层变量
先解读一段代码

var tmp = new Date();
    function f() {
      console.log(tmp);
      if (false) {
        var tmp = 'hello world';
      }
    }

    f(); // undefined

起初并不明白为什么会返回Undefined,我最开始认为应当返回new Date(),但是后来发现var是存在变量提升的,所以上述代码应当解读为:

var tmp = new Date();
    function f() {
      var tmp = undefined;
      console.log(tmp);
      if (false) {
        tmp = 'hello world';
      }
    }
    f(); // undefined

用来计数的循环变量泄露为全局变量
同样先读一段代码

    var s = 'hello';
    for (var i = 0; i < s.length; i++) {
      console.log(s[i]);
    }
    console.log(i); // 5

在假设不考虑性能的前提下,如果我有两个以上的循环,

for() {
    //...
}
for() {
    //...
}
for() {
    //...
}
//...

那么我一个页面要设置多个计数的循环变量。但是如果换做是let,那么情况就发生变化了。

var s = 'hello';

    for (let i = 0; i < s.length; i++) {
      console.log(s[i]);
    }

    for(let i = 5; i < 10; i++) {
      console.log('第二个循环', i);
    }

    for(let i = 10; i < 15; i++) {
      console.log('第三个循环', i);
    }
    console.log(i); // 5

结果

image.png

我不用再考虑计数变量会泄露到全局了。

(5)ES6的块级作用域

function f1() {
  let n = 5;
  if (true) {
    let n = 10;
  }
  console.log(n); // 5
}

起初看这一段代码的时候,会有一些疑问,前面的学习告诉我let是不能重复对一个变量进行声明的,这一段代码必然会报错,但是运行之后没有报错甚至打印了5,这就说明函数中不仅有函数自己的局部作用域,还有if代码块中的块级作用域。
此时,就不得不联想到es5中常年为了躲避变量泄露二使用到的IIFE(Immediately-invoked-function-expression),哇,很方便了。

// before
(function(){
var temp = 'hello';
})();

// after
{
    let temp = ...;
    ...
}

(6)块级作用域与函数声明
在学习过程中,碰到这么一段代码,说在es5环境下运行和es6环境下运行是不一样的,前者环境下会执行内部函数,后者则会视执行环境的表现而定。

function f() { console.log('I am outside!'); }

(function () {
  if (false) {
    // 重复声明一次函数f
    function f() { console.log('I am inside!'); }
  }

  f();
}());

果然,在Chrome中执行结果如下:


image.png

这么一看,与理论上es6的块级作用域表现不符,我们期待的是会打印出外层函数的outside,但是这里的表现说明,我们的函数没有做到es5的提升,即

function f() { console.log('I am outside!'); }

(function () {
  // 重复声明一次函数f
    function f() { console.log('I am inside!'); }
  }
  if (false) {
    

  f();
}());

那具体是怎么回事呢,在查阅资料之后发现,es6的附录中规定了浏览器的表现可以不遵守理论上的规定,它可以有自己的表现方式,如下:

  • 允许在块级作用域内声明函数。
  • 函数声明类似于var,即会提升到全局作用域或函数作用域的头部。
  • 同时,函数声明还会提升到所在的块级作用域的头部。

那么上述代码的解读就变成了:

function f() { console.log('I am outside!'); }

    (function () {
      var f = undefined;
      if (false) {
        // 重复声明一次函数f
        function f() { console.log('I am inside!'); }
      }

      f();
    }());

另外,还有一个需要注意的地方。ES6 的块级作用域允许声明函数的规则,只在使用大括号的情况下成立,如果没有使用大括号,就会报错。

(7)const保证不变的并非是值
以前的学习只会粗浅的认为const会保证一个值不变,但是直到我遇到const设置对象为常量。

const foo = {};
foo.name = 'foo';
console.log('foo', foo);
foo.name = 'boo';
console.log('foo', foo);

foo = {};

结果

image.png

原来const只能保证设置的值指向的内存指针是不变的,而这个内存地址缩储存的值为多少const并不关心,所以上述代码中name属性如何变又或是foo的对象属性增或减与const无关,因为foo所在的内存地址始终没有变过。

结论:使用const去声明一个不变的对象时需要格外的谨慎

那么,我们真的想要一个始终都不会变的对象,又该怎么办呢?Object有一个方法叫做freeze

const foo = Object.freeze({});
    foo.name = 'foo';
    console.log('foo', foo);
    foo.name = 'boo';
    console.log('foo', foo);

    foo = {};

结果

image.png

严格模式下:


image.png

可是,这就结束了吗?我们以前复制对象的时候回去考虑深浅拷贝的问题,那么这里修改的时候,会不会也考虑到层级深浅的问题呢?于是就出现了以下的代码(函数声明或是表达式都可以)

var freezeComplete = (obj) => {
      Object.freeze(obj);
      Object.keys(obj).forEach( (key, i) => {
        if ( typeof obj[key] === 'object' ) {
          freezeComplete( obj[key] );
        }
      });
      return obj;
    };
    const foo = freezeComplete({
      person: {},
      otherKeys: 'otherKeys'
    });

    foo.person.name = 'foo';
    foo.person.height = '1.8';
    console.log('foo', foo);
image.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容