前言:你的 React 代码几乎就是构建在 ES6 之上的。虽然这样讲并不精准,因为 React 在前期版本中可以支持 ES 5.1 的语法,但 React 官方在新版本中几乎摒弃了 ES 5.1。ES6是一次重大的版本升级,与此同时,由于ES6秉承着最大化兼容已有代码的设计理念,你过去编写的JS代码将继续正常运行。事实上,许多浏览器已经支持部分ES6特性,并将继续努力实现其余特性。这意味着,在一些已经实现部分特性的浏览器中,你的JS代码已经可以正常运行。如果到目前为止你尚未遇到任何兼容性问题,那么你很有可能将不会遇到这些问题,浏览器正飞速实现各种新特性。因此,事实是你写 React 代码就要无时无刻的写 ES6 代码。天下之势浩浩荡荡,运用es6势在必行!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>es6新特性探索</title>
<style>

</style>
</head>
<body>

<script>
window.onload=function(){
//在react和Vue之前轻量化的获取es6合格证书!
//let:
/* 1、引入块级作用域的概念
2、不存在变量提升,会有暂时性死区,会出现is not defined而不是undefined
3、不允许重复定义 */
//const:
/* 1、申明了必须赋值
2、申明之后,常量的值不能重复更改
3、一样有块级作用域、不提升和暂时性死区 */



/* 数组去重开始 */
var array = [1, 1, '1', '1',2,4,6,6,{},{}];

function unique1(array) {
// res用来存储结果
var res = [];
for (var i = 0, arrayLen = array.length; i < arrayLen; i++) {

for (var j = 0, resLen = res.length; j < resLen; j++) {
if (array[i] === res[j]) {
break;
/* 回顾下breakcontinue的区别:break:跳出这个循环体,跳出了j值就不会再加了,
这里如果array[i]在res里面找不到相等的,那么执行完循环,j等于resLen。
continue是提前结束本次循环直接进入下次循环。 */
}
}

console.log(j);
console.log(resLen);
console.log("---------------------");

if (j === resLen) {
res.push(array[i])
}

}
return res;
}


function unique2(array) {
var res = [];
for (var i = 0, len = array.length; i < len; i++) {
var current = array[i];
// 数组也有跟字符串一样的indexOf操作,第二个参数就是开始匹配的位置
if (res.indexOf(current) === -1) {
res.push(current)
}
}
return res;
}
// 数组原型下的.filter()方法:传入一个参数,返回能匹配相关条件的值,return后面接的就是条件
function unique3(array) {
var res = array.filter(function (item, index, array) {
return array.indexOf(item) === index;
// item匹配到的如果和下表相等就说明它前面没有相同的,是唯一值
})
return res;
}
//es6的新特性,
function unique4(array) {
return Array.from(new Set(array));
}
// 还可以简写为:
function unique(array) {
return [...new Set(array)];
}
// 或者用箭头函数:
var unique = (a) => [...new Set(a)]




/* 关于其他值:for循环和indexOf均不能对NaN和对象,这点是好理解的:
对象存在引用NaN不与任何值相等。然而set方法可以对NaN去重 */

/* 数组去重结束 */
function fibonacci(n) {
return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2);
}

// ----------------------------------------------------------------------------Promise 操作
var imgs = [
'http://i1.piimg.com/1949/4f411ed22ce88950.jpg',
'http://i1.piimg.com/1949/5a35e8c2b246ba6f.jpg',
'http://i1.piimg.com/1949/1afc870a86dfec5f.jpg'
];
var p1 = new Promise(function ( resolve,reject ) {
var img = new Image();
img.src = imgs[0];
img.onload = function () {
resolve(this);
}
img.onerror = function () {
reject( new Error("加载失败!") );
}
})
// console.log(11);
/* p1.then(function (img) {
console.log("加载完成!");
document.body.appendChild(img);
}).catch(function (img) {
console.log("加载失败!");
}) */
// console.log(12);


function loadimg(url) {
var img = new Image();
img.src = url;
var p = new Promise(function (resolve,reject) {
img.onload = function () {
resolve(this);
}
img.onerror = function () {
reject(this);
}
})
return p;
}

/* loadimg(imgs[0]).then(function (img) {
console.log("加载完成!");
document.body.appendChild(img);
}).catch(function (img) {
console.log("加载失败!");
}) */
/*
- Promise.all 可以将多个Promise实例包装成一个新的Promise实例
- 当所有Promise实例的状态都变成resolved,Promise.all的状态才会变成resolved,此时返回值组成一个数组,传递给then中的resolve函数。
- 只要其中有一个被rejected,Promise.all的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
*/
const allpromise = Promise.all( [loadimg(imgs[0]), loadimg(imgs[1]), loadimg(imgs[2])] );

/* allpromise.then(function (imgarr) {
imgarr.forEach(function (img,index) {
document.body.appendChild(img);
console.log("全部加载完成!");
})
}).catch(function (img) {
console.log( new Error(`没有全部加载完成,${img}失败了`) )
}) */

function timoeut(delay) {
return new Promise( (resolve,reject) => {
setTimeout( resolve , delay , "done");
} )
}
// timoeut(1000).then( message => console.log(message) )

function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}
var hw = helloWorldGenerator();
// hw.next()
//console.log(hw.next())
// { value: 'hello', done: false }

// hw.next()
//console.log(hw.next())
// { value: 'world', done: false }

// hw.next()
//console.log(hw.next())
// { value: 'ending', done: true }

//------------------------------------------------------------------遍历器iterator
var it = makeIterator(['a', 'b']);

it.next() // { value: "a", done: false }
it.next() // { value: "b", done: false }
it.next() // { value: undefined, done: true }

function makeIterator(array) {
var nextIndex = 0;
return {
next: function () {
return nextIndex < array.length ?
{ value: array[nextIndex++], done: false } :
{ value: undefined, done: true };
}
};
}
/* 一个数据结构只要具有Symbol.iterator属性,就可以认为是“可遍历的”(iterable)。
Symbol.iterator属性本身是一个函数,就是当前数据结构默认的遍历器生成函数。
执行这个函数,就会返回一个遍历器。
原生具备 Iterator 接口的数据结构如下:
Array
Map
Set
String
TypedArray
函数的 arguments 对象
NodeList 对象 */

//如数组构造函数的原型下就有Symbol.iterator属性:
let arr = ['a', 'b', 'c'];
let iter = arr[Symbol.iterator]();

iter.next() // { value: 'a', done: false }
iter.next() // { value: 'b', done: false }
iter.next() // { value: 'c', done: false }
iter.next() // { value: undefined, done: true }
//也可以直接用for of 遍历
for (let item of arr) {
//console.log(item); // 'a', 'b', 'c'
}
//对于类数组,可以直接调用数组的Symbol.iterator来直接实现类数组的遍历:
let iterable = {
0: 'a',
1: 'b',
2: 'c',
length: 3,
[Symbol.iterator]: Array.prototype[Symbol.iterator]
};
for (let item of iterable) {
//console.log(item); // 'a', 'b', 'c'
}
//对于字符串的遍历器
var someString = "hi";
//console.log(someString[Symbol.iterator]) // "function"

var iterator = someString[Symbol.iterator]();

//iterator.next() // { value: "h", done: false }
//iterator.next() // { value: "i", done: false }
//iterator.next() // { value: undefined, done: true }
//同样地,也可以直接用for of 遍历
for (let item of someString) {
//console.log(item);
}
//除了数组和字符串,还有集合和广义上的json(map类型)
var engines = new Set(["Gecko", "Trident", "Webkit", "Webkit"]);
for (var e of engines) {
//console.log(e);
}
// Gecko Trident Webkit

var es6 = new Map();
es6.set("edition", 6);
es6.set("committee", "TC39");
es6.set("standard", "ECMA-262");
for (var [name, value] of es6) {
//console.log(name + ": " + value);
}
// edition: 6 committee: TC39 standard: ECMA-262
//以上这些 Symbol.iterator 均可以自己编写,打造不同需求的遍历器

//以下几种情况会默认调用遍历器(如果它具备的话)
//1、解构赋值
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、扩展运算符
var str = 'hello';
[...str] // ['h','e','l','l','o']


var arr1 = ['b', 'c'];
['a', ...arr1, 'd'] // ['a', 'b', 'c', 'd']
//3、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 }
//其他场合
/* for...of
Array.from()
Map(), Set(), WeakMap(), WeakSet() (比如new Map([['a', 1], ['b', 2]]) )
Promise.all()
Promise.race() */

//计算生成的数据结构
/* entries() 返回一个遍历器对象,用来遍历[键名, 键值]组成的数组。
对于数组,键名就是索引值;对于 Set,键名与键值相同。
Map 结构的 Iterator 接口,默认就是调用entries方法。
keys() 返回一个遍历器对象,用来遍历所有的键名。
values() 返回一个遍历器对象,用来遍历所有的键值。 */
let arr2 = ['a', 'b', 'c'];
for (let pair of arr.entries()) {
//console.log(pair);
}
//则依次输出:
/* [0, 'a']
[1, 'b']
[2, 'c'] */
//---------------------------------------------------------------------类的扩展class
/* 与 ES5 一样,实例的属性除非显式定义在其本身(即定义在this对象上),
否则都是定义在原型上(即定义在class上) */
//----------定义类
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
var point = new Point(2, 3);
//point.toString() // (2, 3)
//point.hasOwnProperty('x') // true
//point.hasOwnProperty('y') // true
//point.hasOwnProperty('toString') // false
//point.__proto__.hasOwnProperty('toString') // true
//hasOwnProperty是判断自身有没有此属性,x、y是挂在实例下的,toString是挂在原型下的

//----------class的表达式
const MyClass = class Me {
getClassName() {
return Me.name;
}
};
/* 上面代码使用表达式定义了一个类。需要注意的是,
这个类的名字是MyClass而不是Me,Me只在 Class 的内部代码可用,指代当前类。 */

//------------申明的class不存在变量提升
/* new Foo(); // ReferenceError
class Foo { } */
//类的方法内部如果含有this,它默认指向类的实例!
//关于静态方法:
/* 类相当于实例的原型,所有在类中定义的方法,都会被实例继承。
如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,
而是直接通过类来调用,这就称为“静态方法”。
静态方法包含this关键字,这个this指的是类,而不是实例!!!!!!!
父类的静态方法,可以被子类继承(默认是都继承的!),静态方法也是可以从super对象上调用的*/

//只有下面两种写法可以定义类的静态属性,因为 ES6 明确规定,Class 内部只有静态方法,没有静态属性。
//还有一种:在上面的实例属性写法前面,加上static关键字就可以了。
class Foo {
}

Foo.prop = 1;
Foo.prop // 1

//而定义实例的属性不一样,类的实例属性可以用等式,写入类的定义之中。
class myClass {

constructor() {
myrop = 42;//现在谷歌浏览器也支持把实例对象的属性写在构造函数之内,不能写在外面!
// console.log(this.myrop); // 42
}
}

//----------------------------------------------------------------------------类的继承

/*super这个关键字,既可以当作函数使用,也可以当作对象使用。在这两种情况下,它的用法完全不同。
第一种情况,super作为函数调用时,只能在子类构造函数里调用,代表父类的构造函数。
ES6 要求,子类的构造函数必须执行一次super函数。
super作为对象时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类:
super指向父类的原型对象,所以定义在父类实例上的方法或属性,是无法通过super调用的
如果属性定义在父类的原型对象上,super就可以取到
通过super调用父类的方法时,super会绑定子类的this*/

//------------如果super作为对象,用在静态方法之中,这时super将指向父类,而不是父类的原型对象。

class Parent {
static myMethod(msg) {
console.log('static', msg);
}

myMethod(msg) {
console.log('instance', msg);
}
}

class Child extends Parent {
static myMethod(msg) {
super.myMethod(msg);
}

myMethod(msg) {
super.myMethod(msg);
}
}

//Child.myMethod(1); // static 1

var child = new Child(); //child.myMethod(2); // instance 2



}
</script>
</body>
</html>