ES2015也就是ES6知识点持续更新

      ES6,全名:ECMAScript2015,先扯点其他的,ECMA是一个国际标准化组织,它最重要最重要的作用就是让ECMAScript这门语言标准化,什么意思呢?我们知道,js这门脚本语言是运行在浏览器上的,准确的说是运行在浏览器的渲染引擎上的,毕竟这些语言跟java,C#类似,它不像C/C++这样的语言,可以直接通过编译器转化成计算机可以知晓的语言,就是0和1,js其实跟java一样都是一种解释性的语言,这种解释性的语言需要一个中间的媒介,比如java的虚拟机,js也一样,虽然它是脚本语言,但也是一种解释性的语言,它无法直接编译成计算机可以知晓的二进制代码,所以需要浏览器引擎来解释给编译器知晓,比较出名的浏览器引擎其实不少,最出名的应该就是被移植出来用于node.js运行的google引擎V8,这些就不在扯太远了,了解了js的运行环境,那你其实可以发现,当今可不仅仅就一家谷歌有浏览器,火狐,欧朋,苹果,微软...N多个浏览器厂商,这么多的引擎,就跟春秋战国一样,各国有各国的货币,各国有各国的语言与交流方式,战国后秦始皇他也发现这个问题了,他怎么做呢?(我觉得这是他最伟大的地方,是我们中华民族大一统的先驱,无论什么民族过来最终只有被我们同化的命,扯太远了...)

     秦始皇做的事其实很简单,统一标准,统一语言,统一货币,车同轨,书同文,大家一起用小篆吧!而js原来的处境其实很像很像,有了ECMA这个标准化组织,浏览器厂商都遵循他们制定的规则,这就好办了,最简单的例子就是html5的开头,<!DOCTYPE html>就是告诉浏览器你要用标准化给我渲染,别给我扯你原来那个怪异的渲染引擎,那一切其实就简单多了。

 

     闲话少扯了,ES6我的学习方式呢,是一个个来,一个个知识点过,就从最最简单的let和var的区别开始,以后有时间这篇博文会不时的更新,有时间就写一点。

 ①let,var,const的区别:

        相信熟悉ES5的人用过最多的关键字应该就非var莫属啦,那其实var这个东西啊,是有很多不好的地方的,只是绝大多数情况下不会暴露而已,怎么说呢,咱先来一段代码:

        

         这段代码是很简单的一个输出,在我们的设想中应该是打印一个0,1,2,然后var定义的是局部变量,所以这个变量只在for循环里面,外面那个打印“i”编辑器会给你报错对吧?呵呵!

实际上你错了,由于js异步的机制,for循环会被提前,也就是你实际上执行的代码应该是这样的,

          

(这真的非常让人难以理解,对吧?当然有很多办法可以实现我们想要的结果)

       但上面的var还有另一个问题,最后的console.log(i),按照我们的正常思维应该是for用完了这个变量i,不用了,外面调取不到内部的变量对吧?但实际上你可以去试一下,不会报错,还会帮你把i=3的值打印出来,这里好像没什么大不了的,但如果是很严谨的内存操作时,这就是非常恐怖的内存泄漏问题了,你会莫名其妙多了一堆全局的变量,是不是很恐怖?当然我这里的说法有点夸张(其实造成这一个的根源仅仅是js可以不声明就用变量)。

但如果你把上面的代码,仅仅是把var换成let,你试下:

输出的结果就是很符合我们思维模式的,也比较严谨的改正js内存泄漏这个问题(先声明,再使用)。

这里总结下:

 let这个东西是跟随代码块的(不想举太多例子),简单来说{let...}外面的代码是访问不到括号里let声明的变量的,这对变量是很好的保护,也就是模块思维,代码被分成一个个的模块,各做各的,除了一些必要的联系(通过接口)其他不会互相影响,不会这里定义的变量莫名其妙在那边可以用了,或者莫名其妙把你之前声明的变量值给改了(这其实是很恐怖的一个东西)而这个相比于原来的var而已显然是一个长足的进步。

const这个东西呢,跟let一样,只不过它是声明常量(constant缩写)的,而且一声明必须立即赋值,后面怎么赋值是修改不了的(还会报错),就比如这样:

 和这样

另一个呢?const声明常量跟C语言的不同,C中声明的变量就是变量,就是一个某种类型的值,但js的不一样,它可以是一个对象,可以是一个数组,可以是N多种东西,然后呢有什么用呢?看看下面:

猜猜输出什么?貌似const声明的变量是可以改的对吧?好像和我们想象的不一样,其实是这样的,声明的这个常量呢,实际上是一个指针,一个内存的地址,这个内存不是存在RAM(可读可写的寄存器)中的,是存在ROM即只能用于读取,不能写入,所以它的变量修饰符(也就是上面的obj,name,age)是不能被重新分配的,只不过呢它这些修饰符对应的值可以改变,那有什么用呢?你想想,既然是只读,而且一声明必须要赋值且后续该常量的数据类型是无法被修改的,那么js引擎在渲染或者说编译解释的时候就轻松多了,对吧?不用再去纠结变量数据类型因为已经是确定的了,这样程序的运行就可以快上很多很多,写代码多定义常量少定义变量能定义成常量就不定义成变量也是一个良好的习惯,当然全部用let也是没问题的,而且对于当今硬件如此之强大的环境下影响其实也不大,不过呢,这终究是一些优化以及超大项目的必备手段,良好的习惯可以让我们避免30%的bug。

 ②顶层对象属性:

       上一次在说明var,let的区别时,提到了js一个很重要的缺陷——变量未声明也能用,会默认置为全局变量,那它是怎么实现这一特性的?在实际项目的运行上,又给我们带来什么样的奇怪现象?

       对于未声明的变量,js实际上它是怎么处理的呢?这就不得不提到一个概念——顶层对象,别想太复杂,咱就理解成window就OK了,说到window嘿嘿,这个你就很熟悉了吧?当然了,js内置的顶层对象其实不是window,是global,这个是不能访问的,只不过js又定义了一个window对象指向它,实际上在应用的时候window就是顶层对象。ES6未出之前,js就是把未声明的变量置于顶层对象下(也就是window下)把这些全局变量当成顶层对象的一个属性来操作的。可以看下面一段代码,试试打印出什么?

       你发现没?全局变量a的赋值跟window对象下属性a的赋值实质上是等价的,或者说是可以当成指向的内存地址(对象指针)是一致的,明白了js对于全局变量或为声明变量的处理式,那这样的处理会带来什么不好的地方?咱可以试着来举一举。

       1、未声明的变量当做全局变量,那么一些因为粗心而误写的变量泄漏成了全局,然而这时候编译器是不会报错的,也就是作为检测与查错最重要的一层——编译器并不能及时地给我们一 个错误反馈,这是很致命的(当然运行是可以报错)。
       2、全局变量挂载在window对象下,而实际上window这个对象不是一个空的,虚的对象,它有实体含义(指代浏览器窗口)我们知道js一切皆对象,但让一个具备实体含义的对象作为一切 对象的顶层,或者说母公司,那当仅仅只是要表达它的实体本身时,会怎样呢?你可以思考下,当然对于js作为web行为层脚本的应用环境来说,这些问题绝大多数情况下不会暴露,但也仅仅只是这样。总之呢,要包含一切,要海纳百川,最好本身得虚怀若谷,得有空杯心态。
       3、不声明默认成全局这样(挂载到顶层对象下),可能会出现什么情况呢?如果我们的程序是设计成一个个的模块,而变量挂载到了一个可以在任何地方调用的顶层对象下,一定程度上 会造成冲击,也有可能因为全局变量的注入而影响模块本身功能的执行。

       其实还有个4,后面说。

既然有这么多不好的地方,那肯定不能放任不管对吧?ES6来了,它又带来了怎样的创新与解决方案呢?

       其实解决方案let,const,class那里已经是提到了,这里就不重复了,简单一句话就是:通过定义块级作用域,把变量的作用范围限制死,外部无法访问。

      先扯点其他的,js一个很伟大的创新是node,它让js走出了仅仅是作为网页脚本运行的前端,走到了以构建高性能服务器集群为核心的后端,实现了前后端语言的大一统,当然了,目前的node还仅仅只是个婴孩,也有人说node只能算小后台,还比较尴尬,只能说各有各的看法吧,反正我相信这种模式,这种思想一定会让node越来越好(也许以后不一定叫node)就如bootstrap的栅格系统,bootstrap很多人说很无脑,也许某一天它不在了,但我相信它的核心思想栅格系统一定会存续,只要还有多终端兼容这个刚需。

      先来说下node和javascript在顶层对象上的区别:简单说,javascript的顶层对象是window,node则是global,然后呢,我们还知道另一个东西关于函数function,我们知道一个函数单纯作为一个函数运行而非对象方法时,它里面的this会指向顶层对象window,对吧,你可以试试!比如下面这样的:

       可以发现最终打印的结果确实就是window对象,而并非这个函数。这就很奇怪了,按照一般思维,this,这,难道不是函数么?对吧.呵呵!不过呢,后面的node和ES6模块中,它返回的就不是全局的顶层对象,而仅仅是其模块本身,这样就很符合我们的思维了对吧?

       所以呢,ES6这样做,var,function声明的全局变量,依旧保持原有ES5挂载在顶层对象window下的原定义(保持兼容),但let,const,class定义的就是模块化的变量,不再挂载到顶层对象下,这样就渐渐与顶层对象脱钩,也许这里你会问了,function定义的怎么是变量?你没看错,function定义的就是变量,只不过这个变量所赋的值是一个方法,一个函数而已,function本质上是一个数据类型,自己可以用typeof打印下。

       前面说到个4,看了上面的说明也许你有一丢丢明白,不同环境下js的顶层对象并不一致,比如node的就完完全全没有window这个东西,假如还是之前那样挂载,母亲都不是同一个人了,得挂到哪?很奇怪对吧?(2018-05-18更)

③解构赋值:

简单来说:就是解析某种数据结构,然后一个个位置填上你想要填上的值。跟填坑或者说人事任命一样,嗯,对就是人事任命。

听起来貌似挺高大上的,其实:let [name,age,sex]=["dorseyCh","25","男"];这样就是一个解构与赋值。

回想下我们平时定义或声明变量的时候是怎样的?

是不是这样?

或者说这样:

 要修改里面某个值呢?也是一样对吧?

那解构赋值呢?(用人事任命来理解哈)

 首先是解构,先列份清单,比如说董事长-总经理-技术总监-技术专家-高级工程师-中级工程师-初级工程师,然后呢每个职位先列出来,那对应这个位置呢?要填入谁的名字?就是赋值了,这样呢就是解构赋值了呵呵,简单吧?咱来看看下面的小例子:

可以自己试试打印出什么?

当然了,既然是解构赋值,那结构不对的话可以吗?答案是不可以的,里面的键(keys),也就是CEO,CTO那些,可以值是空的,就是这个职位暂时没人,会返回一个undefined,但结构得对。

这个结构赋值呢,你完完全全可以理解成一个模板引擎,一个个的位置是固定的,比较适合应用于某种协议间传值,或者说两个公司之间某种数据json格式的传输与动态修改,甚至你跟后端接口之间的内部json格式规定,一次代码,终身复用,而且呢这样看起来非常的直观,无需你把一个数据一层层去解析,也不易出错,上面看起来好像也蛮麻烦的对吧?其实用用也是不错的,我们获取它的键,再动态的修改其中的键值,当然简单一些的数据类型要用也是完完全全没问题的。

 ④新增的数据类型set与map:

 首先是set,set是一种类数组对象,咱先来看看set与传统的数组和对象之间的区别:

//    普通的数组是这样的: ["a","b","c","d"];
//    而set数据集合是这样的:{"a","b","c","d"};
//    是一种类数组的对象,需要记住的是它是没有下标的,也就是set[0]是访问不到这个"a"的值的
//    而一般的对象是这样的:{"1":"a","2":"b","3":"c","4":"d"};
//    三者的区别就很明显了吧?

那为什么要加这个set呢?set最大的作用又是什么?

 

        set其实呢在很对其他的开发语言(如java)是很早就有的一种数据结构,ES6把它给加进来了,它最大的作用是set数据集合中是不会出现重复数据的,呵呵,是不是立马就想到了它最重要的一个作用——去重!是的,set一个非常重要的作用就是可以让我们很方便的去重。咱一起来看看吧:

let set=new Set(['小明','小红','小华','小明','小白']);
console.log(set);//看看这个

看看输出是什么?你会发现重复的小明只剩一个。

        传统的去重方式是怎样的呢?可能我们拿到某个集合比如一个数组,首先要套个循环遍历一下整个数组对吧?然后呢,从第1个数据开始一个个赋值给新定义的某个空数组,但是呢在赋值过程中还需要判断,判断什么呢?那就是后面的值不能跟前面的重复,那是不是又得再套个循环?(循环新数组了这时),把前面的已填进新数组的所有值一个个的去与要新添的这个值比较,不相等的时候才填进去,对吧?最后再把新输出给返回出去。当然了,既然这种东西这么有规律,那肯定会被弄成一个api的对吧?减少重复劳动,两个字,优雅!对吧。

那有了这个set数据后呢?我们怎么做呢?先不说,咱先来看看set内部究竟是怎样的?有什么属性跟方法。

其实呢,打开谷歌一目了然,我们大致的说几个比较不同的吧(像forEach,values这些相信大家比我还熟悉了)

说哪几个呢?

add,clear,delete,has; 跟size

顾名思义:add:添加,clear:清空,delete:删除,has:有这个值没有?

看看下面一段代码,自己打印下值看一看:

let set=new Set(['小明','小红','小华','小明','小白']);
    let obj1=new Object(['小明','小红','小华','小明','小白']);
    console.log(set);//看看这个

    console.log("==========================")
    console.log(obj1); //对象
    set.add('小离');
    console.log(set); //看看是不是多了一个小离
    console.log(set.add("小清"));//看看这个方法有木有返回值?是否可以做成一种日志?

    console.log(set.delete('小华'));
    console.log(set);//看看小华是不是被删除了?

    console.log("集合里面有小明吗?有回答true,没有回答false!============"+set.has('小明'));
    console.log("集合里面有dorsey吗?有回答true,没有回答false!============="+set.has('dorsey'));
    
//    console.log(set.clear());
    set.clear();//清空set数据集合(这里你就理解成数组就可以了)
    console.log(set);//返回一个空的set集合

    console.log(set.clear());//undefined

还有一个size,对吧?既然set数据类型是一种类数组,我们数组是不是有一个arr.length返回数组的长度?当然了,set.length是没有的,返回不了的,因为它没有这个属性,那要得到它的长度怎么办呢?就用size,可以自行set.size打印看看,这个是去除重复值后剩下的长度。

好了有了上面几个方法的铺垫,咱来看看set去重,我们怎么做:

咱先来个程序看起来非常笨的办法,当然直观粗暴容易理解:

呐:

假如说我们对下面这个数组做去重处理,怎么做呢?

let arr1=["小明","小红","小华","小贾","小离","小清","小华","小许","小红"];//这里的小华跟小红是重复的

可以这么做:

let set1=new Set();
arr1.forEach(function(item){
    set1.add(item);
});
console.log(set1);//set1帮我们自动去重了
console.log(arr1);//arr1原数组
arr1=[];//咱把原数组清空用来接收去重后的结果,当然如果原来的要备份,你也可以重新定义一个新的数组。
set1.forEach(function(item){
    arr1.push(item);
});
console.log(arr1);

很简单的思维,把arr1的值遍历后一个个的添加到定义好的set数据集合,再重新去取,这种方式可以是可以但太笨了,咱不推荐这样写对吧?

看看下面的:

let removeTheSame=function(array){
    return Array.from(new Set(array));
};
console.log(removeTheSame(arr1));//是不是很简单,妈妈再也不用担心我不会去重了对吧?

上面大概的意思就是:把arr数组的值在set里面给过一遍,并通过from把set这个类数组对象转化成真正的数组。

 不用各种for去套了是不是简单多了?当然set类数组结构既然是一种新数据类型肯定不单单这样的用法对吧?咱先这样。

map:

首先,map跟数组的方法Array.map()是不一样的,数组的map()方法返回的是一个重组的数组,而这里的map是一种新数据类型,一起来看看吧。

既然增加了一种set数据类型类似于数组,那按照强迫症重度患者的思维,那也得有一个类似于obj的数据类型,对吧?嗯,显然ECMAScript那群人非常符合我们的心意,他们肯定也是新增了一种数据类型,那就是map。当然了,它本质上还是一个数组。

那map到底是啥?它跟传统的对象又存在着怎样的区别呢?

我们先来看看传统的对象是怎样的:

obj={keys:values};差不多就这样,一个个的键值对对吧?但是呢,对象有个不好的地方是什么呢?那就是keys这个东西最终都被当成字符串来处理,也就是{string:value}这样的方式,但实际的开发呢,有时候我们并不单单满足于一个string类型的keys,最好是number,object等等各种数据类型都可以,是吧?嗯,map就是来满足你的。

先来看看这个:

let obj5={},obj6={};//命名大家别嫌弃哈。主要是理解
    const map=new Map([
        ["董事长","马云"],
        ["总经理","逍遥子"],
        ["技术总监","王坚"],
        [obj5,"诸葛亮是我的偶像"],
        [obj6,"my name is ChenDuoXin,my English is dorseyCh"],
        [[1,2],"你好啊!"]
    ]);
    console.log(map);

看看输出是什么?

呐,是不是发现它的键,它的keys,可以是对象,可以是数组,可以是字符串等等各种类型,而不仅仅是局限于字符串。

map跟set一样也有很多方法,什么size(返回长度),set(设置值),get(根据键得到值),keys(返回键名),values(返回键值),entries(两个都返回)等等还有上面的has,delete等等等等,这些没必要去记,太多了,这些处理方式跟数组或者对象是很类似的,通一个就全通了,举一反三,减少大脑记忆负担。记住它本质上就是一种数据类型,数据类型本身的各种处理方式该有的它都会有,也必须有,即使某些属性不太一样,但换汤不换药,实际上是一样的,你说size和length有区别吗?细究肯定有,但是呢,有必要不?

 

暂时先这么多!后续不断更新ES6带来了怎样的创新与优雅?以及babel编译器(有些浏览器暂时不支持新语法,这个主要是把ES6代码转化成ES5代码以供部分落后浏览器识别)...

 

posted @ 2018-05-14 00:34  追~梦  阅读(683)  评论(0编辑  收藏  举报