重读 ES6 标准入门(第3版)

Posted 掘金安东尼

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了重读 ES6 标准入门(第3版)相关的知识,希望对你有一定的参考价值。

重读

前言

仅将自己的理解做整理、归类并结合实际遇到的问题做记录,更推荐阅读 ​​ECMAScript 6 入门​​。

扩展篇

数组的扩展

  • 解构赋值

“ES6 中允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)”

作用:可以快速取得数组或对象当中的元素或属性,而无需使用arr[x]或者obj[key]等传统方式进行赋值

let [a,b,c] = [1,2,3];
  • 扩展运算符

“扩展运算符是三个点,它如同rest参数的逆运算,将一个数组转为用逗号分隔的参数序列”

作用:把数组或类数组对象展开成一系列用逗号隔开的值

console.log(...[1,2,3])
//1 2 3
console.log([1,2,3].join())
//1,2,3
console.log(typeof(...[1,2,3]))
//Uncaught SyntaxError: Unexpected token ...
console.log(typeof([1,2,3].join()))
//string
* 为什么typeof(...[1,2,3])报错Uncaught SyntaxError: Unexpected token ...???

常用

let [head,...tail] = [1,2,3,4]; //head:1,tail:[2,3,4]
let colors = ["red","green","blue"];
let [...cloneColors] = colors;
console.log(cloneColors) //["red", "green", "blue"]

1.将数组转为函数的参数,替代 apply 方法

Math.max.apply(null,[14,3,77])//ES5
Math.max(...[14,3,77])//ES6

2.合并数组

var arr1=[a,b]
var arr2=[c,d]
console.log(arr1.concat(arr2))//ES5
console.log([...arr1,...arr2])//ES6
  • Array.from()

“Array.from方法用于将两类对象转成真正的数组”

  • Array.of()

“Array.of方法用于将一组值转换为数组”

常用

console.log(Array.of(3,11,8))//[3, 11, 8]
  • copyWithin()

复制替换数据

Array.prototype.copyWithin(target,start=0,end=this.length)

target:必选,从该位置开始替换 start:替换内容的起点 end:到该位置前停止读取

console.log([1,2,3,4,5].copyWithin(0,3,4))//[4, 2, 3, 4, 5]
console.log([a,b,c].copyWithin(0,1,3))//["b", "c", "c"]
  • etc. find()、fill()、entries()、keys()、values()、includes、数组的空位(非重点关注)

对象的扩展

  • 属性简洁表示、属性名表达式、方法的name属性、Object.is()、Object.assign():合并对象-浅复制、属性可枚举性
  • 遍历对象的属性

1.for...in

2.Object.keys()

var obj =  0: a, 1: b, 2: c ;
console.log(Object.keys(obj)); // console: [0, 1, 2]
  • 对象的解构赋值

对象的属性没有次序,变量必须与属性同名,才能取到正确的值

let  foo, bar  =  foo: "aaa", bar: "bbb" ;
foo // "aaa"
bar // "bbb"

字符串的扩展

  • 字符串遍历 for...of
console.log(foo.split())//["f", "o", "o"]//ES5
for(let codePoint of foo)
console.log(codePoint)//f,"o","o"//ES6
  • 返回字符串给定位置的字符at()
console.log(lallallal.indexOf(a))//1 Number//ES5
console.log(lallallal.charAt(a))//1 String//ES6
  • includes():是否找到参数字符串、startsWith()/endswith():参数字符串是否在源字符串头/尾部...

函数的扩展

  • 函数参数的默认值
function log(x,y=world)
console.log(x,y)

log(moximoxi)//moximoxi world
  • rest参数(...变量名)
function test(...values)
console.log(values)
for(var val of values)
console.log(val)
//for...of可以不仅可以遍历字符串,还可以遍历数组 ES6

test(1,2,3)//[1, 2, 3]
  • name属性
name属性:function foo() foo.name//"foo"
  • 箭头函数 →→→

函数名=参=>返回值

  • 尾调用、尾递归

某个函数的最后一步是调用另一个函数

正则的扩展

数值的扩展

承诺篇

Promise

from MDN:

var promise1 = new Promise(function(resolve, reject) 
setTimeout(function()
resolve(foo);
, 300);
);

promise1.then(function(value)
console.log(value);
// expected output: "foo"
);

console.log(promise1);
// expected output: [object Promise]

Generator

from ​​CSDN​​:

function* helloGenerator() 
console.log("this is generator");

var h = helloGenerator();
h.next();
function* helloGenerator() 
yield "hello";
yield "generator";
return;

var h = helloGenerator();
console.log(h.next());// value: hello, done: false
console.log(h.next());// value: generator, done: false
console.log(h.next());// value: undefined, done: true

yield实际就是暂缓执行的标示,每执行一次next(),相当于指针移动到下一个yield位置。

总结一下,Generator函数是ES6提供的一种异步编程解决方案。通过yield标识位和next()方法调用,实现函数的分段执行。

async

迭代器=>Generator=>async

1.迭代器:不暴露对象的内部表示的情况下,能够遍历整个元素

遍历 ~ Traverse 访问一个集合(广义)的每个元素

迭代 ~ Iterate 反复调用同一个过程最终达成目的(迭代是循环的一种方式),这个过程如果是一个函数,那就是递归,如果是一个循环体,那就是狭义上的迭代。递归和迭代之间的关系、转换、优化等等又是另一个故事了。

function makeIterator (arr) 
let nextIndex = 0;
//返回一个迭代器方法
return
next: () =>
if(nextIndex < arr.length)
return value: arr[nextIndex++], done: false
else
return done: true




const it = makeIterator([1,2,3]);
console.log(1:, it.next());
console.log(2:, it.next());
console.log(3:, it.next());
console.log(end:, it.next());

2.Generator函数执行后会返回一个迭代器

3.async函数是Generator的语法糖

async 函数返回一个Promise对象,可以使用 then 方法添加回调函数

实际遇到

(function()
let pro=new Promise((resolve,reject)=>
resolve(true)
)
console.log(pro)//promise对象
)()

(async function()
let pro=await new Promise((resolve,reject)=>
resolve(true)
)
console.log(pro)//true
//(async函数的返回值是一个promise对象;waite命令后面是一个Promise对象,如果不是,会被转成一个立即resolve的Promise对象)
)()

数据结构篇

Set、Map

  • Set

Set 是成员值唯一的数据结构,类似于数组 Array

所以,有一种重要且好用的去重方法:

var set1 = Array.from(new Set([1,1,2,2,33,aa,aa,bb
]))
console.log(set1)//[1, 2, 33, "aa", "bb"]

或:

var set1 = [...new Set([1,1,2,2,33,aa,aa,bb
])]
console.log(set1)//[1, 2, 33, "aa", "bb"]

常用

Set 实例的方法分为两大类:操作方法(用于操作数据)、遍历方法(用于遍历成员)

分类

方法

操作方法

Set.prototype.add(value):添加某个值,返回 Set 结构本身


Set.prototype.delete(value):删除某个值,返回一个布尔值,表示删除是否成功。


Set.prototype.has(value):返回一个布尔值,表示该值是否为Set的成员。


Set.prototype.clear():清除所有成员,没有返回值。

遍历方法

Set.prototype.keys():返回键名的遍历器


Set.prototype.values():返回键值的遍历器


Set.prototype.entries():返回键值对的遍历器


Set.prototype.forEach():使用回调函数遍历每个成员

  • Map

Map 是可以用非字符串当作键的键值对数据结构,类似于对象 Object

Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。

常用

Map 实例的方法分为两大类:操作方法(用于操作数据)、遍历方法(用于遍历成员)

分类

方法

操作方法

Map.prototype.set(key, value):设置


Map.prototype.get(key):获取


Map.prototype.has(key):判断存在


Map.prototype.delete(key):删除


Map.prototype.clear():清除

遍历方法

Map.prototype.keys():返回键名的遍历器。


Map.prototype.values():返回键值的遍历器。


Map.prototype.entries():返回所有成员的遍历器。


Map.prototype.forEach():遍历 Map 的所有成员。

## Proxy、Reflect

  • Proxy

Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程。

Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。

Proxy就是对象的拦截器

典例:set()方法:用validator拦截person.age的创建;

let validator = 
set: function(obj, prop, value)
if (prop === age)
if (!Number.isInteger(value))
throw new TypeError(The age is not an integer);

if (value > 200)
throw new RangeError(The age seems invalid);



// 对于满足条件的 age 属性以及其他属性,直接保存
obj[prop] = value;

;

let person = new Proxy(, validator);

person.age = 100;
person.age // 100
person.age = young // The age is not an integer
person.age = 300 // The age seems invalid
  • Reflect

将Object对象的一些明显属于语言内部的方法(比如Object.defineProperty),放到Reflect对象上。

为了修改某些Object方法的返回结果,让其变得更合理。

例:用defineProperty,不抛出错误,返回false:

// 老写法
try
Object.defineProperty(target, property, attributes);
// success
catch (e)
// failure


// 新写法
if (Reflect.defineProperty(target, property, attributes))
// success
else
// failure

重点食用:使用 Proxy 实现观察者模式​

观察者模式(Observer mode)指的是函数自动观察数据对象,一旦对象有变化,函数就会自动执行。

const person = observable(
name: 张三,
age: 20
);

function print()
console.log(`$person.name, $person.age`)


observe(print);
person.name = 李四;
// 输出
// 李四, 20

上面代码中,数据对象person是观察目标,函数print是观察者。一旦数据对象发生变化,print就会自动执行。

下面,使用 Proxy 写一个观察者模式的最简单实现,即实现observable和observe这两个函数。 思路是observable函数返回一个原始对象的 Proxy 代理,拦截赋值操作,触发充当观察者的各个函数。

const queuedObservers = new Set();

const observe = fn => queuedObservers.add(fn);
const observable = obj => new Proxy(obj, set);

function set(target, key, value, receiver)
const result = Reflect.set(target, key, value, receiver);
queuedObservers.forEach(observer => observer());
return result;

上面代码中,先定义了一个Set集合,所有观察者函数都放进这个集合。 然后,observable函数返回原始对象的代理,拦截赋值操作。拦截函数set之中,会自动执行所有观察者。

lterator和for...of循环

Iterator 的作用有三个:一是为各种数据结构,提供一个统一的、简便的访问接口;二是使得数据结构的成员能够按某种次序排列;三是 ES6 创造了一种新的遍历命令for...of循环,Iterator 接口主要供for...of消费。

lterator有点难理解了 ​​传送门​​ 结合迭代器、生成器理解

默认调用 Iterator 接口(即Symbol.iterator方法)的场合:

(1)解构赋值

对数组和 Set 结构进行解构赋值时,会默认调用Symbol.iterator方法。

let set = new Set().add(a).add(b).add(c);

let [x,y] = set;
// x=a; y=b

let [first, ...rest] = set;
// first=a; rest=[b,c];

(2)扩展运算符

扩展运算符(...)也会调用默认的 Iterator 接口。

// 例一
var str = hello;
[...str] // [h,e,l,l,o]

// 例二
let arr = [b, c];
[a, ...arr, d]
// [a, b, c, d]

上面代码的扩展运算符内部就调用 Iterator 接口。

实际上,这提供了一种简便机制,可以将任何部署了 Iterator 接口的数据结构,转为数组。也就是说,只要某个数据结构部署了 Iterator 接口,就可以对它使用扩展运算符,将其转为数组。

let arr = [...iterable];

(3)yield*

yield*后面跟的是一个可遍历的结构,它会调用该结构的遍历器接口。

let generator = function* () 
yield 1;
yield* [2,3,4];
yield 5;
;

var iterator = generator();

iterator.next() // value: 1, done: false
iterator.next() // value: 2, done: false
iterator.next() // value: 3, done: false
iterator.next() // value: 4, done: false
iterator.next() // value: 5, done: false
iterator.next() // value: undefined, done: true

(4)其他场合

由于数组的遍历会调用遍历器接口,所以任何接受数组作为参数的场合,其实都调用了遍历器接口。下面是一些例子。

for...of
Array.from()
Map(), Set(), WeakMap(), WeakSet()(比如new Map([[a,1],[b,2]]))
Promise.all()
Promise.race()

模块化篇

Class

ES6 的class可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。

∴ JS实现继承还是通过原型链的方式!

ES5:

function Point(x, y) 
this.x = x;
this.y = y;


Point.prototype.toString = function ()
return ( + this.x + , + this.y + );
;

var p = new Point(1, 2);

ES6:

class Point 
constructor(x, y)
this.x = x;
this.y = y;


toString()
return ( + this.x + , + this.y + );

Modlule

ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。比如,CommonJS 模块就是对象,输入时必须查找对象属性。

ES6 模块不是对象,而是通过export命令显式指定输出的代码,再通过import命令输入。

// circle.js

export function area(radius)
return Math.PI * radius * radius;


export function circumference(radius)
return 2 * Math.PI * radius;
// main.js

import area, circumference from ./circle;

console.log(圆面积: + area(4));
console.log(圆周长: + circumference(14));

编程风格的变化

(1) let 取代 var

(2) 在let和const之间,建议优先使用const,尤其是在全局环境,不应该设置变量,只应设置常量。

(3) 静态字符串一律使用单引号或反引号,不使用双引号。动态字符串使用反引号。

// bad
const a = "foobar";
const b = foo + a + bar;

// acceptable
const c = `foobar`;

// good
const a = foobar;
const b = `foo$abar`;

(4) 善用解构赋值⭐

(5) 对象尽量静态化,一旦定义,就不得随意添加新的属性。如果添加属性不可避免,要使用Object.assign方法。

// bad
const a = ;
a.x = 3;

// if reshape unavoidable
const a = ;
Object.assign(a, x: 3 );

// good
const a = x: null ;
a.x = 3;

(6) 使用扩展运算符(...)拷贝数组。⭐

// bad
const len = items.length;
const itemsCopy = [];
let i;

for (i = 0; i < len; i++)
itemsCopy[i] = items[i];


// good
const itemsCopy = [...items];

(7)立即执行函数可以写成箭头函数的形式

(() => 
console.log(Welcome to the Internet.);
)();

(8)善用Map⭐

(9)Module 语法是 javascript 模块的标准写法,坚持使用这种写法。使用import取代require。

(10)ESLint



我是掘金安东尼: 一名人气前端技术博主(文章 100w+ 阅读量)

终身写作者(INFP 写作人格)

坚持与热爱(简书打卡 1000 日)

我能陪你一起度过漫长技术岁月吗(以梦为马)

觉得不错,给个点赞和关注吧(这是我最大的动力 )b( ̄▽ ̄)d



以上是关于重读 ES6 标准入门(第3版)的主要内容,如果未能解决你的问题,请参考以下文章

Symbol《ES6标准入门(第3版)》

对象的新增方法《ES6标准入门(第3版)》

对象的新增方法《ES6标准入门(第3版)》

JavaScript之ES6快速入门

JavaScript之ES6快速入门

ES6 之 解构赋值