教学要求
介绍ES6的基本上文本深入细致探究ES6文本的同时实现介绍甚么是babel,怎么去同时实现babel科学知识文本
ECMAScript
ECMAScript是一种由Ecma国际性(创建者为西欧计算机系统生产商联合会,European Computer Manufacturers Association)透过ECMA-262国际性技术标准的JAVA面向对象词汇。此种词汇在因特网上应用领域广为,它常常被称作JavaScript或JScript,因此它可以认知为是JavaScript的两个国际性标准,但事实上后二者是ECMA-262国际性标准的同时实现和扩充。而ES6事实上是ECMAScript6版,因为是2015年正式宣布正式发布的因此也叫ES2015
let
在let出现以后,在js中表达式的返回值只有自上而下返回值以及表达式返回值,因此在外部新闻稿的一些表达式单厢被装载到window上引致自上而下自然环境污染。想化解此种问题根本无法用闭区努维预防表达式的自然环境污染。接着ES6就明确提出了let的新闻稿形式,就有了块级返回值的基本上概念。那甚么是块级返回值呢?
块级返回值
其本质上他还是两个返回值,所以返回值的促进作用换句话说基本上概念是啥呢,是对外部表达式没有配套措施出访返回值外部的表达式,就比如说对表达式返回值而言在表达式外部新闻稿的表达式,表达式外是根本无法出访的,所以块级返回值反之亦然,对两个块中的表达式而言,外部表达式也是根本无法出访块中的表达式的。而这个块只不过是被括弧包覆的标识符块。比如
if() { /*这儿的标识符块*/ }
for(let i in [1,2,3]) { /*这儿的标识符块*/ }
{ /*这儿的标识符块*/ }
譬如的返回值覆盖范围都是块级返回值。但只不过他们会发现两个较为奇妙的现像
var a = 0;
window.a = ?? // 0
let a = 0;
window.a = ?? // undefined
这是咋呢。在ES6以后新闻稿表达式的形式不外乎是透过function和var的形式去做的,而这样新闻稿的表达式如果是在自上而下返回值下新闻稿的表达式那会把这些表达式装载到第二层第一类的特性上来,而在应用领域程序自然环境中第二层第一类也是他们的window第一类。也是说在ES6以后第二层第一类也是等同自上而下第一类。但有了let、const此类的新闻稿形式之后,只不过他们新闻稿的自上而下第一类并不全然等同第二层第一类了。此种形式新闻稿的表达式是不会被装载到他们第二层对象的特性上的,因此从ES6开始他们需要把第二层第一类和自上而下第一类的基本上概念给解藕开。
除此之外在ES6以后他们有个很重要的现像也是表达式提升,在新闻稿表达式以后js会帮他们把所有表达式的新闻稿前置,接着再在原来的地方进行
const
const的定义是新闻稿两个常量。常量的意思只不过大家都很清楚,那是两个不可被更改的值。而他相较于let不同的点也就在于此,同样const新闻稿的表达式也会有块级返回值,并且不能被表达式提升。但现在在实际业务中大家也单厢习惯于对数组、第一类用const新闻稿,但他们去改变他们的值的时候却可以更改。这又是咋呢?
这个主要还是js数据存储形式的问题,对基本上数据类型,js是透过栈的形式存储的。而对引用数据类型,他们的数据是用堆进行存储的,而对数据的读取根本无法透过栈中的引用地址去查找,因此不难认知的是对const的新闻稿来说。他们保证的是栈中的数据不会发生变更,而对引用数据而言,引用地址不发生变化,我改的是引用地址对应的值那也就不影响本身栈中的地址了。因此在某种意义上const还是两个新闻稿常量的形式。
那他们在思考一下,在ES5中他们怎么去同时实现两个不允许被更改的表达式呢
Object.defineProperty(obj, key, { value: xx, writable: false })
// 主要是定义了对特性一些行为的约束
想介绍更多的同学可以看这篇文章
深入细致浅出Object.defineProperty()
暂时性死区
只要块级返回值内存在const和let的新闻稿所以,他们无法在新闻稿表达式以后去
箭头表达式
箭头表达式的出现无疑是开辟了表达式定义的新大门,箭头表达式成功帮他们化解了表达式中this指向不明确的问题,箭头表达式的this指向是定义的时候确定的,指向父级所在的上下文自然环境,而普通表达式的this则是运行时确定的通常执行调用者或者window。除此之外箭头表达式还有几个特点
this指向在定义的时候确定无法作为构造表达式去使用不具备arguments特性class类
class类的出现无疑是对学过数据结构、java这些后端词汇的同学而言非常利好的两个技术点,当然对纯前端同学而言类的出现也他们更加能够贴近后端词汇。在没有class出现以后他们常用的单厢用一些表达式的继承形式去同时实现继承。但class出现了之后就更方便的帮助他们去同时实现继承以及构造实例了。
// 传统的构造表达式
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype.getX = function() { return this.x };
var p = new Point(1,2);
// 有了class之后
class Point {
constuctor(x,y) {
this.x = x;
this.y = y
}
getX() {
return this.x
}
}
只不过二者同时实现的功能没有甚么区别,只不过ES6提供的class的语法糖能够更好的去书写两个面向第一类的语法。并且class
除此之外class还具有一些特性
// class 允许定义set和get对特性的劫持
class Point {
_name = 123;
constuctor(x,y) {
this.x = x;
this.y = y
}
set name(newName) {
console.log(set, newName)
_name = newName;
}
get name() {
console.log(get)
return _name;
}
}
// class还允许定义一些静态方法
class Point {
static sp() { console.log(1) }
}
Point.sp()
// class能够用关键词extends去同时实现继承
class Parent {
static sp() { console.log(1) }
}
class Child extends Parent {
constructor() {
super();
}
}
Child.sp()
结构赋值
这个基本上概念不好解释。还是透过实际运用而言明吧
// 对已知的数组进行赋值
const [a,b,c] = [1,2,3]
// a = 1, b = 2, c = 3
// 也可以嵌套的使用
let [a, [[b], c]] = [1, [[2], 3]];
// 也可以忽略某个值
const [a, ,c] = [1,2,3]
// 也可以剩余运算
let [a, …b] = [1, 2, 3];
// 对第一类而言也是一样的
const { a, b } = { a : 1, b: 2 };
// 并且还能做一些默认赋值的操作
const { a = 1, b } = { b: 2 };
// 反之亦然也是可以嵌套使用的
const { a : { c }, b } = { a: { c: 1}, b: 2 }
// 业务中通常会用的一些场景
// 对部分特性重写
const data = { name: ss, data: 123 };
const newData = { …data, name: alis }
// 避免null和undefined对取值的容错
const { name = , data = 1 } = params || {};
Symbol
ES6引入了两个新的数据类型Symbol用来表示两个独一无二的值
// 通常可能会用于定义两个不可更改的常量
const M = new Symbol(MMM)
Symbol 值作为特性名时,该特性是公有特性不是私有特性,可以在类的外部出访。但不会出现在 for…in 、 for…of 的循环中,也不会被 Object.keys() 、 Object.getOwnPropertyNames() 返回。如果要读取到两个第一类的 Symbol 特性,可以透过 Object.getOwnPropertySymbols() 和 Reflect.ownKeys() 取到。Map和Set
Map第一类主要用于去保存两个键值对。key可以是任何值,字符串、第一类、表达式等等
const map = new Map();
map.set(a, 1);
map.set(b, 2);
for(let t of map) {
console.log(t);
}
// [a, 1]
// [b, 2]
// 利用解构的科学知识
for(let [key,value] of map) {
console.log(key, value);
}
Set第一类则是允许你存储任何类型的唯一值
// 最有用的莫过于数组去重了
const arr = [1,2,3,3,4,4];
const uniArr = […new Set(arr)]
// 还可以做交并差
var a = new Set([1, 2, 3]);
var b = new Set([4, 3, 2]);
var union = new Set([…a, …b]); // {1, 2, 3, 4}
var a = new Set([1, 2, 3]);
var b = new Set([4, 3, 2]);
var intersect = new Set([…a].filter(x => b.has(x))); // {2, 3}
var a = new Set([1, 2, 3]);
var b = new Set([4, 3, 2]);
var difference = new Set([…a].filter(x => !b.has(x))); // {1}
其他的一些使用就不一一赘述了感兴趣的可以去相关网站学习
ES6 入门教程
babel
相信前端同学对他们这个单词肯定是不陌生的,在早起很多应用领域程序根本无法兼容ES6的语法的时候他们单厢在webpack中去做一些babel相关的处理。只不过babel是将他们的一些应用领域程序还根本无法直接识别的语法转换成应用领域程序能够识别的语法,比如ES6转ES5的语法。那babel是怎么工作的呢。
首先要讲babel肯定要说js是怎么进行解析语法的。
首先进行分词,将整个标识符字符串分割成最小语法单元数组 只不过这一步是生成他们所谓的token流在分词基础上建立分析语法单元之间的关系 接着将token流整理成两个AST树所以babel是基于现有的AST树去做一层目标AST树的转换,再让解析器将AST转换成对应的js标识符
const a = 123;
// 其生成的两个AST应该是啥样
{
“type”: “File”,
“start”: 0,
“end”: 16,
“loc”: {
“start”: {
“line”: 1,
“column”: 0
},
“end”: {
“line”: 1,
“column”: 16
}
},
“errors”: [],
“program”: {
“type”: “Program”,
“start”: 0,
“end”: 16,
“loc”: {
“start”: {
“line”: 1,
“column”: 0
},
“end”: {
“line”: 1,
“column”: 16
}
},
“sourceType”: “module”,
“interpreter”: null,
“body”: [
{
“type”: “VariableDeclaration”,
“start”: 0,
“end”: 16,
“loc”: {
“start”: {
“line”: 1,
“column”: 0
},
“end”: {
“line”: 1,
“column”: 16
}
},
“declarations”: [
{
“type”: “VariableDeclarator”,
“start”: 6,
“end”: 15,
“loc”: {
“start”: {
“line”: 1,
“column”: 6
},
“end”: {
“line”: 1,
“column”: 15
}
},
“id”: {
“type”: “Identifier”,
“start”: 6,
“end”: 7,
“loc”: {
“start”: {
“line”: 1,
“column”: 6
},
“end”: {
“line”: 1,
“column”: 7
},
“identifierName”: “a”
},
“name”: “a”
},
“init”: {
“type”: “StringLiteral”,
“start”: 10,
“end”: 15,
“loc”: {
“start”: {
“line”: 1,
“column”: 10
},
“end”: {
“line”: 1,
“column”: 15
}
},
“extra”: {
“rawValue”: “123”,
“raw”: “123”
},
“value”: “123”
}
}
],
“kind”: “const”
}
],
“directives”: []
},
“comments”: []
}
比如说他们想把上面两个简单的const a = 123转换成ES5的语法。首先他们要知道在ES5中var a = 123的AST样子是啥样的
var a = 123;
{
“type”: “File”,
“start”: 0,
“end”: 10,
“loc”: {
“start”: {
“line”: 1,
“column”: 0
},
“end”: {
“line”: 1,
“column”: 10
}
},
“errors”: [],
“program”: {
“type”: “Program”,
“start”: 0,
“end”: 10,
“loc”: {
“start”: {
“line”: 1,
“column”: 0
},
“end”: {
“line”: 1,
“column”: 10
}
},
“sourceType”: “module”,
“interpreter”: null,
“body”: [
{
“type”: “VariableDeclaration”,
“start”: 0,
“end”: 10,
“loc”: {
“start”: {
“line”: 1,
“column”: 0
},
“end”: {
“line”: 1,
“column”: 10
}
},
“declarations”: [
{
“type”: “VariableDeclarator”,
“start”: 4,
“end”: 10,
“loc”: {
“start”: {
“line”: 1,
“column”: 4
},
“end”: {
“line”: 1,
“column”: 10
}
},
“id”: {
“type”: “Identifier”,
“start”: 4,
“end”: 5,
“loc”: {
“start”: {
“line”: 1,
“column”: 4
},
“end”: {
“line”: 1,
“column”: 5
},
“identifierName”: “a”
},
“name”: “a”
},
“init”: {
“type”: “StringLiteral”,
“start”: 8,
“end”: 10,
“loc”: {
“start”: {
“line”: 1,
“column”: 8
},
“end”: {
“line”: 1,
“column”: 10
}
},
“extra”: {
“rawValue”: “”,
“raw”: “”
},
“value”: “”
}
}
],
“kind”: “var”
}
],
“directives”: []
},
“comments”: []
}
透过对比他们发现只不过是有个kind的字段不一样。那他们可能就会有这样的一段babel标识符
export default function (babel) {
const { types: t } = babel;
return {
name: “ast-transform”, // not required
visitor: {
VariableDeclaration(path) {
path.node.kind = var;
}
}
};
}
当然这是两个最简单的标识符实例。有兴趣的可以用下面这个在线网址玩一下
AST explorer
后面有时间可以给大家带来一节关于怎么去编写两个AST插件并且运用到自己项目中的实践