ES6 新特性
簡(jiǎn)介:
- ECMAScript 6.0(以下簡(jiǎn)稱(chēng) ES6)是 JavaScript 新一代標(biāo)準(zhǔn),加入了很多新的功能和語(yǔ)法
- ECMAScript 和 JavaScript 的關(guān)系是:前者是后者的規(guī)格趴生,后者是前者的一種實(shí)現(xiàn)
var let const
var
1. ES5 中作用域有:全局作用域蝇刀、函數(shù)作用域甘萧。
// 全局作用域
var a = 123;
function test() {
console.log(a);
}
test(); // 123
//函數(shù)作用域
function test() {
var a = 123;
}
test();
console.log(a); // ReferenceError
// 不用var 全局定義
var z = 'n';
function test() {
z = 'z';
}
test();
console.log(z); // z
var z = 'n';
function test() {
var z = 'z';
}
test();
console.log(z); // n
2. var聲明的變量會(huì)掛載在window上。容易引起變量污染。
// i只是用來(lái)控制循環(huán)晰韵, 但泄露為全局變量
for (var i = 0; i < 3; i++) { }
console.log(window.i); // 3
3. 變量提升
var a = 123;
function test() {
console.log(a); // a在聲明前打印了
if(false) {
var a = 'abc'; // 變量提升,導(dǎo)致內(nèi)層的 a 變量覆蓋了外層的 a 變量
}
}
test(); // undefined
let
ES6 中新增了塊級(jí)作用域概念链瓦。
1. 聲明的變量只在塊級(jí)作用域內(nèi)有效
{
let i = 1;
{ let i = 2; }
}
if( ) { }
for( ) { }
2. 不存在變量提升(或者說(shuō)存在變量提升拆魏,但不賦值)盯桦,而是“綁定”在暫時(shí)性死區(qū)
var test = 1;
function func(){
console.log(test);
let test = 2;
};
func();
3. 不能重復(fù)聲明
{
let a = 1;
let a = 2;
}
// 報(bào)錯(cuò)
{
let a = 10;
var a = 1;
}
//報(bào)錯(cuò)
const
1. const一旦聲明變量,就必須立即初始化
const foo; // 報(bào)錯(cuò)
2. 只在聲明所在的塊級(jí)作用域內(nèi)有效渤刃。
{
const MAX = 5;
}
console.log(MAX) //報(bào)錯(cuò)
3. 同樣存在暫時(shí)性死區(qū)拥峦,只能在聲明的位置后面使用。
{
console.log(MAX); // 報(bào)錯(cuò)
const MAX = 5;
}
4. 不能重復(fù)聲明卖子。
var message = 'Hello';
const message = 'Goodbye!';
let age = 25;
const age = 30;
5. const聲明一個(gè)簡(jiǎn)單數(shù)據(jù)類(lèi)型略号。一旦聲明,常量的值就不能改變洋闽。
const PI = 3.1415;
PI // 3.1415
PI = 3; // 報(bào)錯(cuò)
6. const 定義的復(fù)合類(lèi)型的數(shù)據(jù)可以進(jìn)行修改:
const foo = { prop:1 };
foo.prop = 2; // 只改了數(shù)據(jù)結(jié)構(gòu)玄柠,成功
foo = {}; // 改變了指針指向,報(bào)錯(cuò)
const a = [];
a.push('Hello'); // 不改動(dòng)指針指向诫舅,只改了數(shù)據(jù)結(jié)構(gòu)
a = ['lily']; // 改變了指針指向羽利,報(bào)錯(cuò)
- 對(duì)于簡(jiǎn)單類(lèi)型的數(shù)據(jù)(數(shù)值、字符串刊懈、布爾值)这弧,值就保存在變量指向的那個(gè)內(nèi)存地址,因此等同于常量虚汛,const不允許更改
- 對(duì)于復(fù)合類(lèi)型的數(shù)據(jù)(主要是對(duì)象和數(shù)組)匾浪,變量指向的內(nèi)存地址,保存的只是一個(gè)指向?qū)嶋H數(shù)據(jù)的指針泽疆,const只能保證這個(gè)指針是固定的户矢,至于它指向的數(shù)據(jù)結(jié)構(gòu)是不是可變的,就完全不能控制了
編程風(fēng)格
- 建議不再使用var命令殉疼,let命令取代梯浪。兩者語(yǔ)義相同,而且let沒(méi)有副作用(全局污染瓢娜,變量提升)
- 在let和const之間挂洛,建議優(yōu)先使用const,一是const比較符合函數(shù)式編程思想眠砾,我不改變值虏劲,只新建值。二是JavaScript 編譯器會(huì)對(duì)const進(jìn)行優(yōu)化褒颈,提高運(yùn)行效率
- const聲明常量還有兩個(gè)好處柒巫,一是閱讀代碼的人立刻會(huì)意識(shí)到不應(yīng)該修改這個(gè)值,二是萬(wàn)一修改了會(huì)報(bào)錯(cuò)
- 所有的函數(shù)都應(yīng)該設(shè)置為常量谷丸。
二堡掏、解構(gòu)賦值
- 解構(gòu):ES6 允許按照一定模式,從數(shù)組和對(duì)象中提取值刨疼,對(duì)變量進(jìn)行賦值,
- 可以叫做“模式匹配”泉唁,只要等號(hào)兩邊的模式相同鹅龄,左邊的變量就會(huì)被賦予對(duì)應(yīng)的值。
1. 數(shù)組的解構(gòu)
let [a, b, c] = [1, 2, 3];
// 不完全解構(gòu):
let [x, y] = [1, 2, 3];
// 嵌套數(shù)組進(jìn)行解構(gòu)
let [foo, [[bar], baz]] = [1, [[2], 3]];
// 只取第三個(gè)
let [ , , third] = ['foo', 'bar', 'baz'];
// 中間的不取
let [x, , y] = [1, 2, 3];
// 取第一個(gè) 取剩余的
let [head, ...other] = [1, 2, 3, 4];
head
other
let [...other, last] = [1, 2, 3, 4]; // 報(bào)錯(cuò):Rest element must be last element
2. 對(duì)象的解構(gòu)
let { foo, bar } = { foo: 'aaa', bar: 'bbb' };
const { log, info, table, warn } = console;
3. 解構(gòu)不成功系列:
let [foo] = [];
// foo: undefined
// 如果等號(hào)的右邊 是不可遍歷的結(jié)構(gòu)
let [foo] = 1;
let [foo] = false;
let [foo] = NaN;
let [foo] = undefined;
let [foo] = null;
let [foo] = {};
// 都將報(bào)錯(cuò)
// 不同名
let { abc } = { foo: 'aaa', bar: 'bbb' };
// abc: undefined
4. 用途:
// 交換變量的值
let x = 1;
let y = 2;
[x, y] = [y, x];
// 函數(shù) return 數(shù)組 對(duì)象 進(jìn)行解構(gòu)
function getArr() {
return [1, 2, 3];
}
let [a, b, c] = getArr();
function getObj() {
return {
foo: 1,
bar: 2
};
}
let { foo, bar } = example();
// 傳參:參數(shù)有次序的
function f([x, y, z]) { console.log(x,y,z) }
f([1, 2, 3]);
// 無(wú)序 也會(huì)對(duì)應(yīng)著解構(gòu)
function f({x, y, z}) { console.log(x,y,z) }
f({z: 3, y: 2, x: 1});
// 提取接口返回的 JSON 數(shù)據(jù)
const jsonData = {
id: 42,
status: 'OK',
data: [867, 5309]
};
const { data } = jsonData;
import React, { Component } from 'react';
class SomeComponent extends Component { ... }
編程風(fēng)格
函數(shù)的參數(shù)如果是對(duì)象的成員亭畜,優(yōu)先使用解構(gòu)賦值扮休。
const user = {
firstName:'firstName',
lastName:'lastName'
}
// bad
function getFullName(user) {
const firstName = user.firstName;
const lastName = user.lastName;
}
// good
function getFullName(obj) {
const { firstName, lastName } = obj;
}
// best
function getFullName({ firstName, lastName }) { }
getFullName(user);
三、模板字符串
傳統(tǒng)的 JavaScript 語(yǔ)言拴鸵,輸出模板通常是這樣寫(xiě)的玷坠。
const basket = {
count:123,
onSale:20
}
let str = 'There are ' + basket.count +
'items in your basket, ' +
+ basket.onSale +
' are on sale!';
上面這種寫(xiě)法相當(dāng)繁瑣不方便,ES6 引入了模板字符串解決這個(gè)問(wèn)題:
1. 反引號(hào)(`)標(biāo)識(shí) , 變量名寫(xiě)在 ${ } 之中劲藐。
const basket = {
count:123,
onSale:20
}
let str = `
There are ${basket.count} items
in your basket, ${basket.onSale}
are on sale!
`
2. 進(jìn)行運(yùn)算
let obj = {x: 1, y: 2};
`${obj.x + obj.y}`
3. 調(diào)用函數(shù)
function fn() {
return 'and';
}
`foo ${fn()} bar`
編程風(fēng)格
靜態(tài)字符串一律使用單引號(hào)或反引號(hào)侨糟,不使用雙引號(hào)。動(dòng)態(tài)字符串使用反引號(hào)瘩燥。
四秕重、函數(shù)的擴(kuò)展
1. 默認(rèn)值
ES6 之前,不能直接為函數(shù)的參數(shù)指定默認(rèn)值厉膀,只能采用變通的方法溶耘。 對(duì) 0 false不友好
function log(y) {
y = y || '我是默認(rèn)值';
console.log(y);
}
log('傳值了');
log(0);
log(false);
ES6 允許為函數(shù)的參數(shù)設(shè)置默認(rèn)值,即直接寫(xiě)在參數(shù)定義的后面服鹅。
// 例子1:
function log(y = '我是默認(rèn)值') {
console.log(y);
}
log(0)
log(false)
// 例子2:
function Point(x = 0, y = 0) {
this.x = x;
this.y = y;
}
const p = new Point();
p // { x: 0, y: 0 }
2. 函數(shù)的length屬性
- length:該函數(shù)預(yù)期傳入的參數(shù)個(gè)數(shù)凳兵。
- 指定了默認(rèn)值后,后面參數(shù)的length屬性將失真企软。
// 默認(rèn)值在不同位置的例子:
(function (a) {}).length // 沒(méi)有默認(rèn)值
(function (a = 5) {}).length
(function (a, b, c = 5) {}).length
(function(...args) {}).length // rest 參數(shù)
(function (a = 0, b, c) {}).length
(function (a, b = 1, c) {}).length
3. rest 參數(shù)
// arguments 能通過(guò)下標(biāo)訪(fǎng)問(wèn) 轉(zhuǎn)成數(shù)組
function sortNumbers() {
console.log(arguments)
return Array.prototype.slice.call(arguments);
}
sortNumbers(1,2,3)
// rest參數(shù) 真正的數(shù)組
const sortNumbers = (...numbers) => {
console.log(numbers)
}
sortNumbers(1,2,3)
4. 箭頭函數(shù)
- ES6 允許使用“箭頭”(=>)定義函數(shù)庐扫。
let f = () => 5;
// 等同于
let f = function () { return 5 };
let sum = (num1, num2) => num1 + num2;
// 等同于
let sum = function(num1, num2) {
return num1 + num2;
};
[1,2,3].map(function (x) {
return x * x;
});
// 等同于
[1,2,3].map(x => x * x);
const person = {
first:'first',
last:'last',
};
function full(person) {
return person.first + ' and ' + person.last;
}
full(person)
// 等同于
const full = ({ first, last }) => first + ' and ' + last;
full(person)
- 不能使用箭頭函數(shù)的情況
// 定義對(duì)象方法的時(shí)候 不能用箭頭函數(shù)
const cat = {
lives: 9,
jumps: () => {
console.log(this); // window
this.lives--;
}
}
cat.jumps(); // 箭頭函數(shù)只能調(diào)用外層的this 把函數(shù)上下文綁定到了 window 上
// 解決方案:
const dog = {
lives: 9,
jumps() {
console.log(this === dog); // 寫(xiě)成普通函數(shù) this就指向dog
this.lives--;
}
}
dog.jumps();
// 不可以當(dāng)作構(gòu)造函數(shù)。 因?yàn)闃?gòu)造函數(shù)需要有自己的this仗哨,箭頭函數(shù)沒(méi)有自己的this形庭,用的是外層的this
let X = () => {};
x = new X();
// 定義原型方法的時(shí)候 不能用箭頭函數(shù)
function Cat(name) {
this.name = name;
}
Cat.prototype.sayCatName = () => {
console.log(this); // window 箭頭函數(shù)調(diào)用外層的this
return this.name;
};
const cat = new Cat('Mew');
cat.sayCatName(); // ''
// 定義事件回調(diào)函數(shù) 不能用箭頭函數(shù)
const button = document.getElementById('myButton');
button.addEventListener('click', () => {
console.log(this === window); // true
this.innerHTML = 'Clicked button';
});
編程風(fēng)格
- 使用匿名函數(shù)當(dāng)作參數(shù)的場(chǎng)合
// bad
[1, 2, 3].map(function (x) {
return x * x;
});
// best
[1, 2, 3].map(x => x * x);
- 箭頭函數(shù)取代Function.prototype.bind,不應(yīng)再用 self/_this/that 綁定 this厌漂。
// bad
const self = this;
const boundMethod = function(...params) {
return method.apply(self, params);
}
// best
const boundMethod = (...params) => method.apply(this, params);
- 不要在函數(shù)體內(nèi)使用 arguments 變量萨醒,使用 rest 運(yùn)算符(...)代替。
// bad
function concatenateAll() {
const args = Array.prototype.slice.call(arguments);
return args.join('');
}
// good
function concatenateAll(...args) {
return args.join('');
}
- 函數(shù)默認(rèn)值
// bad
function handleThings(opts) {
opts = opts || {};
}
// good
function handleThings(opts = {}) {
// ...
}
五苇倡、數(shù)組的擴(kuò)展
1.擴(kuò)展運(yùn)算符
console.log(...[1, 2, 3])
console.log(1, ...[2, 3, 4], 5)
[...document.querySelectorAll('div')]
// [<div>, <div>, <div>]
// 合并數(shù)組
function push(array, ...items) {
array.push(...items);
return array;
}
push([1,2,3]4,5,6)
// 函數(shù)參數(shù)展開(kāi)
const arr = [0, 1, 2];
function f(x, y, z) {
console.log(x,y,z)
}
f(...arr);
// 合并數(shù)組
const arr1 = ['a', 'b'];
const arr2 = ['c'];
const arr3 = ['d', 'e'];
// ES5
arr1.concat(arr2, arr3);
// ES6
[...arr1, ...arr2, ...arr3]
//拆分字符串
[...'hello']
// [ 'h', 'e', 'l', 'l', 'o' ]
// 遍歷map類(lèi)型的keys
let map = new Map([
[1, 'one'],
[2, 'two'],
[3, 'three'],
]);
[...map.keys()]
[...map.values()]
// Generator 返回?cái)?shù)組
const go = function*(){
yield 1;
yield 2;
yield 3;
};
[...go()] // [1, 2, 3]
2. Array.from()
- 將對(duì)象轉(zhuǎn)為真正的數(shù)組
- 偽數(shù)組:
- arguments對(duì)象
- document.querSelectAll('div') 返回的對(duì)象
- 字符串
// 偽數(shù)組的轉(zhuǎn)換
let arrayLike = {
'0': 'a',
'1': 'b',
'2': 'c',
length: 3,
};
console.log(arrayLike instanceof Array) // false;
let arr1 = [].slice.call(arrayLike); // ES5 轉(zhuǎn)換成數(shù)組
let arr2 = Array.from(arrayLike); // ES6 轉(zhuǎn)換成數(shù)組
// arguments對(duì)象
function foo() {
let args = Array.from(arguments);
console.log(arguments instanceof Array)
console.log(args instanceof Array)
}
foo(1,2,3);
// 字符串
Array.from('string');
- Array.from 第二個(gè)參數(shù)富纸,作用類(lèi)似于數(shù)組的map方法
let arr = [2,3,4];
Array.from(arr, x => x * x);
// 等同于
Array.from(arr).map(x => x * x);
3. 數(shù)組實(shí)例的 includes()
// ES5 判讀是否存在與數(shù)組
[1,2,3,NaN].indexOf(1) !== -1 ? console.log('存在') : console.log('不存在');
[1,2,3,NaN].indexOf(4) !== -1 ? console.log('存在') : console.log('不存在');
// 誤判 缺點(diǎn):1. 不夠語(yǔ)義化 2. 它內(nèi)部使用嚴(yán)格相等運(yùn)算符(===)進(jìn)行判斷
[1,2,3,NaN].indexOf(NaN) !== -1 ? console.log('存在') : console.log('不存在');
// ES6:
[1, 2, 3].includes(1) // true
[1, 2, 3].includes(4) // false
[1, 2, 3, NaN].includes(NaN) // true
六.對(duì)象的擴(kuò)展
1. Object.assign()
- 對(duì)象的合并
const target = { a: 1, b: 1 };
const source1 = { b: 2, c: 2 };
const source2 = { c: 3 };
Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}
- 自動(dòng)轉(zhuǎn)換成對(duì)象
typeof Object.assign(2) // 'object'
Object.assign(undefined) // 報(bào)錯(cuò)
Object.assign(null) // 報(bào)錯(cuò) undefined和null無(wú)法轉(zhuǎn)成對(duì)象
- 屬于淺拷貝 或者說(shuō)是一級(jí)深拷貝
const obj1 = {
a: { b: 1 },
c:2
};
const obj2 = Object.assign({}, obj1); // 與 {} 拼接會(huì)產(chǎn)生一個(gè)新的對(duì)象
obj1.c = 3;
obj2 // c不會(huì)變 一級(jí)深拷貝
obj1.a.b = 2;
obj2 // b會(huì)跟著變 并沒(méi)有開(kāi)辟新地址
- 數(shù)組的處理
Object.assign([1, 2, 3], [4, 5])
// [4, 5, 3]
- 用途:
// ES5寫(xiě)法 prototype 添加方法
SomeClass.prototype.someMethod = function (arg1, arg2) {
···
};
SomeClass.prototype.anotherMethod = function () {
···
};
// ES6 prototype 添加方法
Object.assign(SomeClass.prototype, {
someMethod(arg1, arg2) {
···
},
anotherMethod() {
···
}
});
2. Object.keys(),Object.values()旨椒,Object.entries()
- 拿對(duì)象的keys values 鍵值對(duì)
let obj1 = { foo: 'bar', baz: 42 };
Object.keys(obj1)
let obj2 = { foo: 'bar', baz: 42 };
Object.values(obj2)
let obj3 = { foo: 'bar', baz: 42 };
Object.entries(obj3)
- 與for of結(jié)合
let {keys, values, entries} = Object;
let obj = { a: 1, b: 2, c: 3 };
// 拿obj的keys
for (let key of keys(obj)) {
console.log(key);
}
// 拿obj的values
for (let value of values(obj)) {
console.log(value);
}
for (let [key, value] of entries(obj)) {
console.log(key, value);
}
3. Object.fromEntries()
- Object.entries()的逆操作晓褪,用于將一個(gè)鍵值對(duì)數(shù)組轉(zhuǎn)為對(duì)象。
Object.fromEntries([
['foo', 'bar'],
['baz', 42]
])
// { foo: 'bar', baz: 42 }
七. Promise
1. 簡(jiǎn)介
Promise 是異步編程的一種解決方案
有三種狀態(tài):pending(進(jìn)行中)综慎、fulfilled(已成功)和rejected(已失敾练隆)。 只有異步操作的結(jié)果寥粹,可以決定當(dāng)前是哪一種狀態(tài)变过。
缺點(diǎn):
無(wú)法取消Promise,一旦新建它就會(huì)立即執(zhí)行涝涤,無(wú)法中途取消媚狰。
其次,如果不設(shè)置回調(diào)函數(shù)阔拳,Promise內(nèi)部拋出的錯(cuò)誤崭孤,不會(huì)反應(yīng)到外部。
第三糊肠,當(dāng)處于pending狀態(tài)時(shí)辨宠,無(wú)法得知目前進(jìn)展到哪一個(gè)階段(剛剛開(kāi)始還是即將完成)。Promise對(duì)象是一個(gè)構(gòu)造函數(shù)货裹,創(chuàng)造Promise實(shí)例
resolve: “未完成”變?yōu)椤俺晒Α?/p>
reject: “未完成”變?yōu)椤笆 ?/p>
一般來(lái)說(shuō)嗤形,調(diào)用resolve或reject以后,Promise 的使命就完成了弧圆,后繼操作應(yīng)該放到then方法里面赋兵,而不應(yīng)該直接寫(xiě)在resolve或reject的后面。
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 異步操作成功 */){
return resolve(value);
} else {
return reject(error);
}
});
2. then()
- 第一個(gè)回調(diào)函數(shù) 狀態(tài)變?yōu)閞esolved時(shí)調(diào)用
- 第二個(gè)回調(diào)函數(shù) 狀態(tài)變?yōu)閞ejected時(shí)調(diào)用
promise.then(function(value) {
// success resolved時(shí)調(diào)用
}, function(error) {
// failure rejected時(shí)調(diào)用
});
拿ajax舉例:
const getJSON = function(url) {
const promise = new Promise(function(resolve, reject){
const handler = function() {
if (this.readyState !== 4) {
return;
}
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
}
};
const client = new XMLHttpRequest();
client.open('GET', url);
client.onreadystatechange = handler;
client.responseType = 'json';
client.setRequestHeader('Accept', 'application/json');
client.send();
});
return promise;
};
// 一個(gè)異步
getJSON('/posts.json').then(
json => console.log(json),
err => console.error('出錯(cuò)了', err)
);
// 嵌套異步
getJSON('/post/1.json').then(
post => getJSON(post.commentURL) // 返回的還是一個(gè)Promise對(duì)象(即有異步操作)
).then(
comments => console.log('resolved: ', comments), // 等待前一個(gè)Promise對(duì)象的狀態(tài)發(fā)生變化搔预,才會(huì)被調(diào)
err => console.log('rejected: ', err)
);
3. catch()
// bad
promise
.then(function(data) {
// success
}, function(err) {
// error
});
// good 可以捕獲then執(zhí)行中的錯(cuò)誤霹期,也更接近同步的寫(xiě)法(try/catch)
promise
.then(function(data) {
// success
})
.catch(function(err) {
// error
});
4. Promise.all()
- 6 個(gè) Promise 實(shí)例, 6個(gè)都成功或1個(gè)失敗 才會(huì)走回調(diào)
- 6個(gè)都成功:返回值組成一個(gè)數(shù)組給回調(diào)
const promises = [2, 3, 5, 7, 11, 13].map(function (id) {
return getJSON('/post/' + id + '.json');
});
Promise.all(promises).then(function (posts) {
// ...
}).catch(function(reason){
// ...
});
八. Iterator(遍歷器)
- 遍歷器(Iterator):是一種接口,為各種不同的數(shù)據(jù)結(jié)構(gòu)提供統(tǒng)一的訪(fǎng)問(wèn)機(jī)制拯田。任何數(shù)據(jù)結(jié)構(gòu)只要部署 Iterator 接口历造,就可以完成遍歷操作
- ES6新增:Map Set 結(jié)構(gòu)
let m = new Map();
m.set('numberA', 67);
m.set('numberB', 88);
m.get('numberA');
let s = new Set([1, 2, 3, 3, '3']);
s; // Set {1, 2, 3, "3"}
ES6 新增遍歷命令for...of循環(huán),Iterator 接口主要供for...of消費(fèi), Set 和 Map 結(jié)構(gòu)船庇、arguments對(duì)象吭产、DOM NodeList 對(duì)象、Generator 對(duì)象鸭轮,以及字符串垮刹。
任何數(shù)據(jù)結(jié)構(gòu)只要部署 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'
}
- 原生具備 Iterator 接口的數(shù)據(jù)結(jié)構(gòu):
Array, Map, Set, String, 函數(shù)的arguments對(duì)象, NodeList對(duì)象
// Array
const arr = ['red', 'green', 'blue'];
for(let v of arr) {
console.log(v); // red green blue
}
// Set
const engines = new Set(["Gecko", "Trident", "Webkit", "Webkit"]);
for (var e of engines) {
console.log(e);
}
let generator = function* () {
yield 1;
yield 2;
yield 5;
};
var iterator = generator();
for(let v of iterator) {
console.log(v);
}
九. Generator
1. 簡(jiǎn)介
- Generator 也是異步編程的一種解決方案
- function關(guān)鍵字與函數(shù)名之間有一個(gè)星號(hào)
- 函數(shù)體內(nèi)部使用yield表達(dá)式,暫停執(zhí)行的標(biāo)記
- Generator函數(shù)并不執(zhí)行张弛,返回的也不是函數(shù)運(yùn)行結(jié)果荒典,而是一個(gè)指向內(nèi)部狀態(tài)的指針對(duì)象,我們可以通過(guò)調(diào)用 next 方法吞鸭,使得指針移向下一個(gè)狀態(tài)
2.語(yǔ)法
- *的位置
function * foo(x, y) { ··· }
function *foo(x, y) { ··· }
function* foo(x, y) { ··· }
function*foo(x, y) { ··· }
let obj = {
* myGeneratorMethod() {
···
}
};
// 等價(jià)
let obj = {
myGeneratorMethod: function* () {
// ···
}
};
- 在另一個(gè)表達(dá)式之中寺董,必須放在圓括號(hào)里面。
- 函數(shù)參數(shù)或放在賦值表達(dá)式的右邊刻剥,可以不加括號(hào)遮咖。
function* demo() {
console.log('Hello' + yield); // SyntaxError
console.log('Hello' + yield 123); // SyntaxError
console.log('Hello' + (yield)); // OK
console.log('Hello' + (yield 123)); // OK
foo(yield 'a', yield 'b'); // OK
let input = yield; // OK
}
3. 方法屬性
- yield:暫停
- next:執(zhí)行
- value:是yield表達(dá)式的值
- done:表示遍歷是否結(jié)束。
function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}
var hw = helloWorldGenerator();
hw.next()
//' { value: 'hello', done: false }
hw.next()
// { value: 'world', done: false }
hw.next()
// { value: 'ending', done: true }
hw.next()
// { value: undefined, done: true }
3. next()
- next的參數(shù):設(shè)置成上一個(gè)yield表達(dá)式的返回值
- next的輸出:會(huì)執(zhí)行一條yield語(yǔ)句造虏,并返回yield后面的值
function* foo(x) {
var y = 2 * (yield (x + 1));
var z = yield (y / 3);
return (x + y + z);
}
let b = foo(5);
b.next() // { value:6, done:false } x=5御吞, 執(zhí)行第一行麦箍,輸出yield的值 x+1的值6
b.next(12) // { value:8, done:false } y=24,執(zhí)行第二行陶珠,輸出yield的值 將上一次yield表達(dá)式的值設(shè)為12 24/3=8
b.next(13) // { value:42, done:true } z=13 執(zhí)行第三行挟裂, return (x + y + z);
4. throw()、return()
function* gen(x,y){
yield 1;
yield 2;
yield 3;
}
let g = gen();
g.next(); //{value: 1, done: false}
g.return(5); //{value: 5, done: true}
g.next(); //{value: undefined, done: true}
function* foo(x,y){
yield 1;
yield 2;
yield 3;
}
let f = foo();
f.next(); //{value: 1, done: false}
f.throw(new Error('出錯(cuò)了')); // Error
f.next(); //{value: undefined, done: true}
5. 異步對(duì)比
// 傳統(tǒng)回調(diào)函數(shù)
ajax('url_1', () => {
// callback 函數(shù)體
ajax('url_2', () => {
// callback 函數(shù)體
ajax('url_3', () => {
// callback 函數(shù)體
})
})
})
// 無(wú)法取消 Promise 揍诽,錯(cuò)誤需要通過(guò)回調(diào)函數(shù)來(lái)捕獲
ajax('url_1')
.then(res => {
// 操作邏輯
return ajax('url_2')
}).then(res => {
// 操作邏輯
return ajax('url_3')
}).then(res => {
// 操作邏輯
})
// Generator
function *fetch() {
yield ajax('XXX1', () => {})
yield ajax('XXX2', () => {})
yield ajax('XXX3', () => {})
}
let it = fetch()
let result1 = it.next()
let result2 = it.next()
let result3 = it.next()
// async/await
async function test() {
let result1 = await fetch('XXX1')
let result2 = await fetch('XXX2')
let result3 = await fetch('XXX3')
}
十. class
1. 構(gòu)造函數(shù) 與 class
//ES5 生成實(shí)例對(duì)象的傳統(tǒng)方法
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype.toString = function () {
return `${this.x},${this.y}`
};
let p = new Point(1, 2);
// ES6 class
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return `${this.x},${this.y}`
}
}
//其實(shí)就是調(diào)用原型上的方法诀蓉。
// 等同于
Point.prototype = {
constructor() {},
toString() {}
};
2. new類(lèi)的實(shí)例
- 定義在this上的屬性才是屬于實(shí)例的本身,否則都是定義在其原型上
- x,y綁定在point上暑脆,toString綁定在Point上
//定義類(lèi)
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
this.toString = this.toString.bind(this);
}
toString() { // 普通函數(shù)需綁定this
return `${this.x},${this.y}`
}
//toString = () => { // 箭頭函數(shù)不需要綁定this 引用外層this
// return `${this.x},${this.y}`
//}
}
let point = new Point(2, 3);
let { toString } = point;
toString(); // (2, 3)
// 檢測(cè)自身屬性中是否具有指定的屬性
point.hasOwnProperty('x') // true
point.hasOwnProperty('y') // true
point.hasOwnProperty('toString') // false
point.__proto__.constructor === Point; // true
point.__proto__.hasOwnProperty('toString') // true 方法在point的原型Point上
3. 繼承
- ES5繼承:通過(guò)借用構(gòu)造函數(shù)來(lái)繼承屬性, 通過(guò)原型鏈來(lái)繼承方法
- 先創(chuàng)造子類(lèi)的實(shí)例對(duì)象this渠啤,然后再將父類(lèi)的方法屬性用call方法添加到this上面 。
function Father(name, age){
this.name = name;
this.age = age;
}
function Son(name, age){
Father.call(this, name, age);
}
Son.prototype = Object.create(Father.prototype); // 通過(guò)Object.crete()去關(guān)聯(lián)兩者的prototype
Son.prototype.constructor = Son; // 將 Son 的constructor指向自己
Son.prototype.show = function () {
console.log(this.name,this.age);
};
var s = new Son('子類(lèi)', 110);
s.show();
- ES6繼承
// class構(gòu)造一個(gè)父類(lèi)
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
p() {
return 2;
}
}
// extends,super關(guān)鍵字 繼承所有的屬性添吗,方法
class ColorPoint extends Point { // 先生成父類(lèi)實(shí)例
constructor(x, y) { // 調(diào)用子類(lèi)的構(gòu)造函數(shù)修飾父類(lèi)實(shí)例
super(x, y); // super當(dāng)函數(shù)用
console.log(x,y);
console.log(super.p()); // super當(dāng)對(duì)象用
}
}
let colorPoint = new ColorPoint('xx','yy');
// 如何判斷一個(gè)類(lèi)是否繼承了另一個(gè)類(lèi)
Object.getPrototypeOf(ColorPoint) === Point;
結(jié)束!