文章为个人原创手写,内容参考部分书籍(《深入理解ES6》)与博客(阮一峰),个人的汇总与总结。
如有不对,希望指出。
话题 | 链接 |
---|---|
es6严谨性 | https://juejin.im/post/5eea3020f265da02ab172265 |
es6简洁性 | https://juejin.im/post/5ef0116df265da02ba14e261 |
es6原对象方法的扩展 | 正在更新中 |
es6新概念的引入 | 正在更新中 |
ES6,全称ECMAScript 2015,于2015年发布,成为新一代前端标准。值得一提的是,当前的浏览器并不兼容ES6语法。我们需要借助Babel去编译。
本人把ES6的改革从性质上分成四个部分,包括严谨性,简洁性,原对象方法的扩展,以及新概念的引入
前端严谨性从发展角度越来越强。如后期的typescript更加明显,由”弱语言“,逐步发展为常规语言。ES6的更加严谨,可以从以下几点进行分析。
我们都知道,变量声明(var)会有变量提升。这里的变量会提前初始化,也可以提前访问。当项目变量复杂的时候,很容易产生bug。es6就在这个时候,引入了let跟const。它解决了下边的问题:
1)局部作用域
新引入的let,const声明,再不会再产生变量提升。避免了变量提前访问的场景,间接的提高了严谨性。我们可以在程序运行时就知道了报错,而非后期的调试中。案例如下
alert("a=" + a);//此时a为underfined,因为变量a已提前声明,但还未赋值
alert("b=" + b);//此时程序已抛出异常,因为此时未找到变量b
var a = 1;
let b = 2;
2)禁止重复声明
如果一个标识符已经在代码块内部被定义,那么在此代码块内使用同一个标识符进行 let 声明就会导致抛出错误。
3)区分常量与变量
这是let与const的区别。const 声明会阻止对于变量绑定与变量自身值的修改,避免了我们日常开发中,了不小心改到常量的问题。
4)暂时性死区
用案例理解该概念。
for( var i = 0; i<10; i++ ){
setTimeOut( function(){
alert(i );
}, 1000);
}
for( let i = 0; i<10; i++ ){
setTimeOut( function(){
alert(i );
}, 1000);
}
通过案例你可以发现,用var声明的,会受setTimeOut所影响,
而let的没有影响。可以说明let有暂时性死区。
在 for-in与 for-of 循环中, let 与 const 都能在每一次迭代时创建一个新的绑定,这意味着在循
环体内创建的函数可以使用当前迭代所绑定的循环变量值
(而不是像使用 var 那样,统一使用循环结束时的变量值)。
这一点在 for 循环中使用 let 声明时也成立,不过在 for 循
环中使用 const 声明则会导致错误。
简述var,let,const之间的区别?
参考答案:可参考上述解释几个要点。作用域,禁止声明重复,区分常量与变量,暂时性死区。
需要理解,什么是变量作用域的提升。
怎么解决for循环延迟打印?如下案例,怎么让输出正确的结果?
for( var i = 0; i<10; i++ ){
setTimeOut( function(){
alert(i );
}, 1000);
}
参考答案:1.let声明 2.利用闭包 3.立即执行函数
为什么要模块化?
在以前,js一直没有模块化的体系。这就会产生一个问题,当项目到达大型时,很大可能性出现方法重叠,以及安全性问题,成为大型项目的一个痛点与障碍。而es6模块化正式为此诞生。
关于es6模块化的冷知识:
模块化引入,跟常规js引入方法有什么不同?
参考答案:
常规js引入,script标签src为"text/javascript"。
而如果你想直接引入模块化js,则src="module"。
可能看到这里,你会反驳,react跟vue打包编译后,
他们模块化后的代码引入的script标签,
为什么没有用到src="module",
那是因为人家已经经过webpack编译后,
已经把你的模块化代码,打包成一个全局js文件中。
Web 浏览器中的模块加载顺序是怎么样的?
参考答案:
1)当使用模块加载的时候,浏览器立即开始下载模块文件,但并不会执行它,
直到整个网页文档全部解析完为止。
(默认是defer,可以了解一下defer跟async的区别)
2)模块执行的代码,是由上往下的,如果中间包含内联模块,则会优先执行内链模块
在es6模块化之前,你还知道那些前端模块化?他们之间有什么区别?
参考答案:
在es6模块化之前,社区还出现了一些模块化的方法,
例如commonJS 和 AMD 。此外还有CMD。
下边我们简述一下他们的概念与区别。
1)AMD, commonJS, 与es6,都属于预加载类型。而后期引入的CDM是懒加载。
何为预加载, 也就是说,在程序调用,所有的模块都加载完成。
而懒加载,是用到什么的时候,才去加载什么。
2)AMD跟cmd专注于前端的规范。而commonjs跟es6 moudle可用于前后端。
3)AMD的代表做为requirejs,cmd的代表作为seajs。commonjs 与 es6,则无需引入,
只需要引入编译器(如babel)即可。 seajs为淘宝引入的规范,我们都知道淘宝相对很大,
不采用懒加载,首屏的时间将会很长,不过现在已经停止维护。
4)es6 跟 commonJS做了如下改变:
1.ES6只能新增值,无法重新赋值就会报错
2.CommonJS 输出是值的拷贝,即原来模块中的值改变不会影响已经加载的该值,
ES6静态分析,动态引用,输出的是值的引用,值改变,引用也改变
,即原来模块中的值改变则该加载的值也改变。
3.CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。
4.CommonJS 加载的是整个模块,即将所有的接口全部加载进来,
ES6 可以单独加载其中的某个接口(方法)。
5.CommonJS this 指向当前模块,ES6 this指向undefined
Symbol个人也觉得是提高我们项目严谨性的方法之一。
它用于创建不可枚举的属性,并且这些属性在不引用符号的情况下是无法访问的。
只有在访问属性的时候用[]方法才能正确访问,这算不算大大提高了我们程序的严谨性呢?
首先我们理解一下声明是Symbol。Symbol是JS新引入的基本类型。我们都知道在ES5之前,JS 已有的基本类型(字符串、数值、布尔类型、 null 与 undefined )之外, ES6 引入
了一种新的基本类型。
思考一下,为什么要引入这个Symbol呢?
符号起初被设计用于创建对象私有成员,而这也
是 JS 开发者期待已久的特性。
在符号诞生之前,将字符串作为属性名称导致属性可以被轻易
访问,无论命名规则如何。
而“私有名称”意味着开发者可以创建非字符串类型的属性名称,由
此可以防止使用常规手段来探查这些名称。
看到这里,我们结合官方文档。列举一下Symbol常规作用。
可观察如下案例:
let name = Symbol();
let student = {};
person[name] = "weizhan";
console.log(person[name]); // "weizhan"
此代码创建了一个符号类型的 name 变量,并将它作为 student 对象的一个属性,而每
次访问该属性都要使用这个符号值。
为符号变量适当命名是个好主意,这样你就可以很容易地说明它的含义。
它的好处就是可以避免同参数名的覆盖。从这点,提高了程序的严谨性你是否赞同?因为每一个Symbol都独立存在,及时程序重名了,它也不会覆盖。我们可以通过变量获取它曾经覆过的值。
Symbol 作为对象的属性名,可以保证属性不重名。
用Symbol只能用个[]去访问,没有其他方式可以进行访问。
我们可以利用Symbol来创建一些常量。比如订单状态等。
const ORDER_RETRUN = Symbol();//订单退货状态
const ORDER_SUCCESS = Symbol();订单成功状态
const ORDER_PAY = Symbol();//订单支付状态
这一点更体现出程序的简洁性(下文)。如果没有Symbol。
我们只能这样去声明。
const ORDER_RETRUN = "RETRUN";//订单退货状态
const ORDER_SUCCESS = "SUCCESS";订单成功状态
const ORDER_PAY = "PAY";//订单支付状态
我们如果需要保证每个状态都为独立存在,则要保证“RETRUN”,“SUCCESS”,“PAY”不一致。假设出现手误
const ORDER_SUCCESS = “SUCCESS”;
const ORDER_PAY = “SUCCESS”;
两者值都为SUCCESS时,两个状态ORDER_SUCCESS跟ORDER_PAY则相等。跟我们原来的单一状态设计相违背。而用Symbol()可直接获取独一无二的值。
看到这里,是否觉得Symbol提高了严谨性。
关于Symbol的一些额外api,笔者个人是不建议深入的。本文只是列举,有兴趣可自行查询。
Symbol.hasInstance :供 instanceof 运算符使用的一个方法,用于判断对象继承关
系。
Symbol.isConcatSpreadable :一个布尔类型值,在集合对象作为参数传递给
Array.prototype.concat() 方法时,指示是否要将该集合的元素扁平化。
Symbol.iterator :返回迭代器(参阅第七章)的一个方法。
Symbol.match :供 String.prototype.match() 函数使用的一个方法,用于比较字符串。
Symbol.replace :供 String.prototype.replace() 函数使用的一个方法,用于替换子字
符串。
Symbol.search :供 String.prototype.search() 函数使用的一个方法,用于定位子字符
第六章 符号与符号属性
108
串。
Symbol.species :用于产生派生对象(参阅第八章)的构造器。
Symbol.split :供 String.prototype.split() 函数使用的一个方法,用于分割字符串。
Symbol.toPrimitive :返回对象所对应的基本类型值的一个方法。
Symbol.toStringTag :供 String.prototype.toString() 函数使用的一个方法,用于创建
对象的描述信息。
Symbol.unscopables :一个对象,该对象的属性指示了哪些属性名不允许被包含在
with 语句中。
怎么访问到Symbol的值?
Symbol 值作为属性名时,该属性是公有属性不是私有属性,可以在类的外部访问。
但是不会出现在 for...in 、 for...of 的循环中
,也不会被 Object.keys() 、 Object.getOwnPropertyNames() 返回。
如果要读取到一个对象的 Symbol 属性,
可以通过 Object.getOwnPropertySymbols() 和 Reflect.ownKeys() 取到。
假设需要创建一个数组,传统的new Array(2);第一个参数,可能为数组长度,也可能是第一个参数值。
设计上没有问题,但是程序中容易出现疏忽造成bug。
如果是克隆多一个数组呢?
而新引入数组方法,array.of跟array.form正是解决该问题。array详细部分,会在第三篇文章具体列出。
es6严谨性 : https://juejin.im/post/5eea3020f265da02ab172265
es6简洁性 : https://juejin.im/post/5ef0116df265da02ba14e261
es6原对象方法的扩展: https://juejin.im/post/5ef0116df265da02ba14e261
es6新概念的引入: 正在更新中
该文章已结束,下一篇文章,关于es6简洁性汇总,主要知识点包括:
字符串模版,箭头函数,class的引入,解构函数的引入,js原型扩展等。
https://juejin.im/post/5ef0116df265da02ba14e261