ECMAScript 6入門筆記

1. 做用域變量

做用域就是變量的做用範圍。也就是你聲明一個變量之後,這個變量能夠在什麼場合下使用。之前的JavaScript只有全局做用域,和函數做用域。javascript

1.1 var的問題

1.var 沒有塊級做用域,定義後在當前包中都均可以訪問,若是變量名重複,就會覆蓋前面定義的變量,而且很能夠被他人修改。css

if(true){
	var a = "a"; //指望a是某一個值
}
console.log(a);
複製代碼


2.var 在for循環標記變量共享,通常在循環中會使用的i會被共享,其本質也是因爲沒有塊級做用域形成的
html

for (var i = 0; i < 3; i++) {
     setTimeout(function () {
         alert(i);
     }, 0);
 }
 
 // 結果就是 彈窗三次 3
 
 for ( i = 0; i < 3; i++) {
     setTimeout(function () {
         alert(i);
     }, 0);
 }
 
 // 結果就是 彈窗三次 0-2
複製代碼

1.2 塊級做用域

在用var定義變量的時候,變量經過閉包進行隔離的,如今用了let,不只僅能夠經過閉包隔離,還能夠增長了一些塊級做用域隔離。塊級做用用一組大括號定義一個快,使用let定義的變量字啊大括號的外面是訪問不到的。java

1.2.1 實現會計做用域

if(ture){
	let name = 'wjh'
}

consloe.log('name'); // ReferenceError: name is not defined
複製代碼

1.2.2 不會污染全局對象

if(ture){
	let name = 'wjh'
}
console.log(window.name); // undefined
複製代碼

1.2.3 for 循環中也可使用i

// 嵌套循環不會相互影響
for (let i = 0; i < 3; i++) {
   console.log("out", i);
   for (let i = 0; i < 2; i++) {
   console.log("in", i);
  }
}

// 結果 out 0 in 0 in 1 out 1 in 0 in 1 out 2 in 0 in 1
複製代碼

1.2.4 重複定義會報錯

if(ture){
	let a = 1;
  let a = 2; //Identifier 'a'
}
複製代碼

1.2.5 不存在變量的預解釋

for(let i = 0;i<2;i++){
	console.log('inner',i);
  let i =100;
}

// 結果 i is not defined
複製代碼

1.2.6 閉包的新寫法

;(function(){

})();
複製代碼

如今git

{

}
複製代碼

2. 常量

使用 const 咱們能夠聲明一個常量,一旦聲明以後,就不能夠更改。github

2.1 常量不能從新賦值

const MY_NAME = 'wjw';
MY_NAME = 'wjw2';//Assignment to constant variable
複製代碼

2.2 變量能夠改變

注意const限制的是不能給變量從新賦值,而變量的值自己是能夠改變的,下面的操做是能夠的ajax

const names = ['wjw1'];
names.push('wjw2');
console.log(names);
複製代碼

2.3 不一樣的塊級做用域能夠屢次定義

const A = "0";
{
    const A = "A";
    console.log(A)
}
{
    const A = "B";
    console.log(A)
}
console.log(A)

// 結果 A B 0
複製代碼

3. 解構

3.1 解構數組

解構意思就是分解一個東西的結構,能夠用一種相似數組的方式定義N個變量,能夠將一個數組中的值按照規則賦值過去。
npm

var [name,age]=['wjh',8];
console.log(name,age);
複製代碼

3.2 嵌套賦值

let [x,[y],z]=[1,[2.1]];
console.log(x,y,z);

let [x,[y,z]] = [1,[2.1,2.2]];
console.log(x,y,z);

let [json,arr,num] = [{name:'wjw'},[1,2],3];
console.log(json,arr,num);

// 1 2.1 undefined 1 2.1 2.2 { name: 'wjw' } [ 1, 2 ] 3
複製代碼

3.3 省略賦值

let [,,x]=[1,2,3]
console.log(x);
複製代碼

3.4 解構對象

對象也能夠被解構json

var obj = {name:'wjw',age:8};
//對象裏的name屬性的值會交給name這個變量,age的值會交給age這個變量
var {name,age} = obj
//對象裏的name屬性的值會交給myname這個變量,age的值會交給myage這個變量
let {name: myname, age: myage} = obj;
console.log(name,age,myname,myage);
複製代碼

3.5 默認值

在賦值和傳參的時候可使用默認值bootstrap

let [a='a',b='b',c=new Error('C必須指定')] = [1, , 3];
console.log(a,b,c);

function ajax(options){
	var method = options.method || "get";
  var data = options.data || {};
}

function ajax(method='get',data){
	console.log(arguments);
}

ajax({
	method:'post',
  data:{'name':'wjh'}
})
複製代碼

4. 字符串

4.1 模板字符串

模板字符串用反應號(數字1左邊的那個建)包含,用${}括起來

var name = 'wjw',age = 8;
let desc = `${name} is ${age} old!`;
console.log(desc);

//全部模板字符串的空格和換行,都是被保留的
var str = `<ul> <li>a</li> <li>b</li> </ul>`;
console.log(str);
複製代碼

其中的變量會用變量的值替換掉

function replace(desc){
	return desc.replace(/\$\{([^}]+)\}/g,function(matched,key){
    return eval(key);
  });
}
複製代碼

4.2 帶標籤的模板字符串

能夠在模板字符串的前面添加一個標籤,這個標籤能夠去處理模板字符串 標籤其實就是一個函數,函數能夠接收兩個參數,一個是 strings 就是模板字符串裏的每一個部分的字符 還有一個參數可使用rest的形式values,這個參數裏面是模板字符串裏的值。

var name = 'wjh',age = 8;
function desc(strings,...values){
    console.log(strings,values);
}
desc`${name} is ${age} old!`;
複製代碼

字符串新方法

  • includes():返回布爾值,表示是否找到了參數字符串。
  • startsWith():返回布爾值,表示參數字符串是否在源字符串的頭部。
  • endsWith():返回布爾值,表示參數字符串是否在源字符串的尾部。
var s = 'wjh';
s.startsWith('w') // true
s.endsWith('h') // true
s.includes('j') // true
複製代碼

第二個參數,表示開始搜索的位置

var s = 'wjh';
console.log(s.startsWith('j',2)); // true
console.log(s.endsWith('j',2)); // true
console.log(s.includes('j',2)); // false
複製代碼

endsWith的行爲與其餘其餘方法有所不一樣。它針對前n個字符,而其餘方法是從第幾位開始到字符串結束

4.4 repeat

repeat 方法返回一個新字符串,表示將原字符串重複n次。

'x'.repeat(3);
 'x'.repeat(0);
複製代碼

5. 函數

5.1 默認參數

能夠給定義的函數接收的參數設置默認的值 在執行這個函數的時候,若是不指定函數的參數的值,就會使用參數的這些默認的值。

function ajax(url,method='GET',dataType="json"){
  console.log(url);
  console.log(method);
  console.log(dataType);
}
複製代碼

5.2 展開操做符

把...放在數組前面能夠把一個數組進行展開,能夠把一個函數而不須要使用apply

//傳入參數
let print = function(a,b,c){
    console.log(a,b,c);
}
print([1,2,3]);
print(...[1,2,3]);

// 能夠替代apply
var m1 = Math.max.apply(null, [8, 9, 4, 1]);
var m2 = Math.max(...[8, 9, 4, 1]);

// 能夠替代concat
var arr1 = [1, 3];
var arr2 = [3, 5];
var arr3 = arr1.concat(arr2);
var arr4 = [...arr1, ...arr2];
console.log(arr3,arr4);

//類數組的轉數組
function max(a,b,c) {
    console.log(Math.max(...arguments));
}
max(1, 3, 4);
複製代碼

5.3 剩餘操做符

剩餘操做符能夠把其他參數的值放在一個叫作b的數組裏

let rest = function(a,...rest){
    console.log(a,rest);
}
rest(1,2,3);
複製代碼

5.4 解構參數

let destruct = function({name,age}){
	console.log(name,age);
}
destruct({name:'wjh',age:10})
複製代碼

5.6 箭頭函數

箭頭函數簡化了函數的定義方式

[1,2,3].forEach(val=>console.log(val));
複製代碼

輸入參數若是多於一個要用()包含,函數體若是有多條語句須要用{}包起來

箭頭函數根本沒有本身的this,致使內部的this就是外層代碼塊的this。 正是由於它沒有this,從而避免了this指向的問題。

var person = {
    name:'wjh',
    getName:function(){
-        setTimeout(function(){console.log(this);},1000); //在瀏覽器執行的話this指向window
+        setTimeout(() => console.log(this),1000);//在瀏覽器執行的話this指向person
    }
}
person.getName();
複製代碼

6. 數組的新方法

// 相同的陣列
var people = [
  {
    name : 'Casper' ,
    like : '鍋燒意麪' ,
    age : 18
  },
  {
    name : 'Wang' ,
    like : '炒麪' ,
    age : 24
  },
  {
    name : 'Bobo' ,
    like : '蘿蔔泥' ,
    age : 1
  },
  {
    name : '滷蛋' ,
    like : '蘿蔔泥' ,
    age : 3
  }
];
複製代碼

6.1 Array.prototype.filter()

filter() 會回傳一個陣列,其條件是return 後方爲true 的物件,很適合用在搜尋符合條件的資料。

var filterEmpty = people.filter( function ( item, index, array ) {
});
console.log(filterEmpty);     //沒有條件,會是一個空陣列

var filterAgeThan5 = people.filter( function ( item, index, array ) {
  return item.age > 5 ;        //取得大於五歲的   若是這邊符合條件 只要爲ture便可
});
console .log(filterAgeThan5);    // Casper, Wang這兩個物件
複製代碼

6.2 Array.prototype.find()

find()與filter()很像,但find() 只會回傳一次值,且是第一次爲true的值。

var findEmpty = people.find( function ( item, index, array ) {
});
console .log(findEmpty);           //沒有條件,會是undefined

var findAgeThan5 = people.find( function ( item, index, array ) {
  return item.age > 5 ;            //取得大於五歲的
});

console .log(findAgeThan5);        //雖然答案有兩個,但只會回傳Casper這一個物件
var findLike = people.find( function ( item, index, array ) {
  return item.like === '蘿蔔泥' ;   //取得陣列like === '蘿蔔泥'
});
console .log(findLike);            //雖然答案有兩個,但只會回傳第一個Bobo物件
複製代碼

6.3 Array.prototype.forEach()

forEach 是這幾個陣列函式最單純的一個,不會額外回傳值,只單純執行每一個陣列內的物件或值。

var forEachIt = people.forEach( function ( item, index, array ) {
  console .log(item, index, array); //物件,索引,所有陣列
  return item;                      // forEach沒在return的,因此這邊寫了也沒用
});

console .log(forEachIt);             // undefined

people.forEach( function ( item, index, array ) {
  item.age = item.age + 1 ;          // forEach就如同for,不過寫法更容易
});
console .log(people);                //所有age + 1
複製代碼

6.4 Array.prototype.map()

使用map() 時他須要回傳一個值,他會透過函式內所回傳的值組合成一個陣列。 若是不回傳則是 undefined 回傳數量等於原始陣列的長度 這很適合將原始的變數運算後從新組合一個新的陣列。

var mapEmpty = people.map( function ( item, index, array ) {
});
console .log(mapEmpty);     // [undefined, undefined, undefined, undefined]

var mapAgeThan5 = people.map( function ( item, index, array ) {
  return item.age > 5 ;     //比較大於五歲的
});
console .log(mapAgeThan5); // [true, true, false, false]

var mapAgeThan5_2 = people.map( function ( item, index, array ) {
  // 錯誤示範
  if (item.age > 5 ) {
    return item;               //回傳大於五歲的
  }
  return  false ;                //別覺得空的或是false就不會回傳
});
console .log(mapAgeThan5_2);    // [{name: 'Casper'...}, {name: 'Wang'...}, false, false]
var mapEat = people.map( function ( item, index, array ) {
  if (item.like !== '蘿蔔泥' ) {
    return  ` ${item.like}好吃` ;
  } else {
    return  ` ${item.like}很差吃` ;
  }
});
console .log(mapEat);           // ["鍋燒意麪好吃", "炒麪好吃", "蘿蔔泥很差吃", "蘿蔔泥很差吃"]
複製代碼

6.5 Array.prototype.every()

every()能夠檢查全部的陣列是否符合條件,這僅會回傳一個值trueor false,能夠用來檢查陣列中的內容是否符合特定條件。

var ans = array.every( function ( item, index, array ) {
  console .log(item, index, array); //物件,索引,所有陣列
  return item.age > 10  //當所有age大於10才能回傳true
});
console .log(ans); // false:只要有部分不符合,則爲false

var ans2 = array.every( function ( item, index, array ) {
  return item.age < 25
});
console .log(ans2); // true:所有age都小於25
複製代碼

6.6 Array.prototype.some()

some() 與every() 很是接近,都是回傳true or false,差別僅在every() 需徹底符合,some() 僅須要部分符合。

var ans = people.some( function ( item, index, array ) {
  return item.age > 10  //當所有age大於10才能回傳true
});
console .log(ans);   // true:只要有部分符合,則爲true

var ans2 = people.some( function ( item, index, array ) {
  return item.age < 25
});
console .log(ans2); // true:只要有部分符合,則爲true 

var ans2 = people.some( function ( item, index, array ) {
  return item.age > 25
});
console .log(ans2); // false:所有都不符合則爲false
複製代碼

6.7 Array.prototype.reduce()

reduce() 和其餘幾個差別就很大了,他能夠與前一個回傳的值再次做運算,參數包含如下: accumulator: 前一個參數,若是是第一個陣列的話,值是以另外傳入或初始化的值 currentValue: 當前變數 currentIndex: 當前索引 array: 所有陣列

var reduceEmpty = people.reduce( function ( accumulator, currentValue, currentIndex, array ) {
});
console .log(reduceEmpty);                  //沒有條件,會是undefined

var reducePlus = people.reduce( function ( accumulator, currentValue, currentIndex, array ) {
  // 分別爲前一個回傳值, 目前值, 當前索引值
  console .log(accumulator, currentValue, currentIndex);
  return accumulator + currentValue.age;   //與前一個值相加
}, 0 );                                     //傳入初始化值爲0
console .log(reducePlus);                   //總和爲46

var reducePlus = people.reduce( function ( accumulator, currentValue, currentIndex, array ) {
  console .log( 'reduce' , accumulator, currentValue, currentIndex)
  return  Math .max( accumulator, currentValue.age ); //與前一個值比較哪一個大
}, 0 );
console .log(reducePlus);                   //最大值爲24
複製代碼

7. 對象

7.1 對象字面量

若是你想在對象裏添加跟變量名同樣的屬性,而且屬性的值就是變量表示的值就能夠直接在對象里加上這些屬性

let name = 'wjh';
let age = 8;
let getName = function(){
		console.log(this.name)
}
let person ={
		name,
    age,
    getName
}
person.getName();
複製代碼

7.2 Object.is

對比兩個值是否相等

console.log(Object.is(NaN,NaN));
複製代碼

7.3 Object.assign

把多個對象的屬性複製到一個對象中,第一個參數是複製的對象,從第二個參數開始日後,都是複製的源對象

var nameObj = {name:'wjh'}
var ageObj = {age:8};
var obj = {};
Object.assign(obj,nameObj,ageObj);
console.log(obj);

//克隆對象
function clone(obj){
	return Object.assgin({},obj);
}
複製代碼

7.4 Object.setPrototypeOf

將一個指定的對象原型設置爲另外一個對象或者null

var obj1 = {name:'wjh1'};
var obj2 = {name:'wjh2'};
var obj = {};
Object.setPrototypeOf(obj,obj1);
console.log(obj.name);
console.log(Object.getPrototypeOf(obj));
Object.setProtoypeOF(obj,obj2);
console.log(obj.name);
console.log(Object.getPrototypeOf(obj));
複製代碼

7.5 proto

直接對象表達式中設置prototype

var obj1 = {name:'wjh'};
var obj3 = {
	_proto_:obj1
} 
console.log(obj3.name);
console.log(Object.getPrototypeOf(obj3));
複製代碼

7.6 super

經過super能夠調用protype上的屬性或方法

let person ={
	eat(){
  	return 'milk';
  }
}
let student = {
	_proto_:person,
  eat(){
  	return super.eat()+'bead'
  }
}
console.log(student.eat());
複製代碼

8. 類

8.1 class

使用 class 這個關鍵詞定義一個類,基於這個建立實例之後就會建立 constructor 方法,此方法能夠用來初始化

class Person{
	constructor(name){
  	this.name = name;
  }
  
  getName(){
		console.log(this.name)  
  }

}

let person = new Person('wjh');
person.getName();
複製代碼

8.2 get與set

getter 能夠用來獲取屬性,setter 能夠去設置屬性

class Person {
	constructor(){
  	this.hobbies = [];
  }
  set hobby(hobby){
  	this.hobbies.push(hobby);
  }
  get hobby(){
  	return this.hobbies;
  }
}
let person = new Person();
person.hobby = 'aa';
person.hobby = 'bb';
console.log(person.hobby)
複製代碼

8.3 靜態方法-static

在類裏面添加靜態的方法可使用static 這個關鍵詞,靜態方法就是不須要實例化類就能使用的方法

class Person{
	static add(a,b){
  	return a+b;
  }
}
console.log(Person.add(1,x));
複製代碼

8.4 繼承extends

一個類能夠繼承其餘的類裏的東西

class Person{
	constructor(name){
  	this.name = name;
  }
}

class Teacher extends Person{
	constructor(name,age){
  	super(name);
    this.age = age;
  }
}

var teacher = Teacher('wjh',8);
console.log(teacher.name,teacher.age)
複製代碼

9. 生成器(Generator)與迭代器(Iterator)

Generator 是一個特殊的函數,執行它會返回一個Iterator對象。經過遍歷迭代器,Generator函數運行後悔返回遍歷器對象,而不是函數的返回值。

9.1 Iterators模擬

迭代器有一個next方法,每次執行的時候會返回一個對象 對象裏面有兩個函數,一個是value表示返回的值,還有就是布爾值done,表示是迭代完成

function buy(books){
  let i = 0;
  return{
    next(){
      let done = i ===books.length;
      let value = !done ? books[i++]:undefined;
      return {
      	value:value,
        done:done
      }
    }
  }
}

let iterators = buy(['js','html']);
var curr;
do{
	curr = iterators.next();
  console.log(curr);
}while(!curr.done);
複製代碼

9.3 Generators

生成器用於建立迭代器

function* buy(boos){
	for(var i=0;i<boos.length;i++){
  		yield books[i];
  }
}
let buying = buy(['js','html]);
var curr;
do {
	curr = buying.next();
  console.log(curr);
}while(!curr.done);
複製代碼

10. 集合

10.1 Set

一個Set是一堆東西的集合,Set 有點像數組,不過跟數組不同的是,Set裏面不能有重複的內容

var books = new Set();
books.add('js');
books.add('js');//添加劇復元素的集合元素個數不會變化
books.add('html');
books.forEach(function(book){ // 循環集合
	console.log(book);
})
console.log(book.size);//集合中元數的個數
console.log(books.has('js'));//判斷集合是否有此元素
books.delete('js');
console.log(books.size);
console.log(books.has('js'));
books.clear();//清空set
console.log(books.size);
複製代碼

10.2 Map

可使用Map來組織這個名值對的數據

var books = new Map();
books.set('js',{name:'js'});//向map中添加元素
books.set('html',{name:'html'});
console.log(books.size);//查看集合中的元素
console.log(books.get('js'));//經過key獲取值
books.delete('js');//執行key刪除元素
console.log(books.has('js'));//判斷map中有沒有key
book.forEach((value,key)=>{
	console.log(key+'='+value);
})
books.clear();//清空map
複製代碼

11. 模塊

能夠根據應用的需求吧代碼分紅不一樣的模塊,每一個模塊裏能夠導出它須要讓其餘模塊使用的東西,在其餘模塊裏面能夠導入這些模塊,導出的東西。

11.1 模塊

在瀏覽器中使用模塊須要藉助 導出

export var name = 'wjh';
export var age = 8;
複製代碼

導入

//import {name,age} from './school.js';
import * as school from './school.js';
console.log(school.name,school.age);
複製代碼

在頁面中引用

<script src="https://google.github.io/traceur-compiler/bin/traceur.js"></script>
<script src="https://google.github.io/traceur-compiler/bin/BrowserSystem.js"></script>
<script src="https://google.github.io/traceur-compiler/src/bootstrap.js"></script>
<script type="module" src="index.js"></script>
複製代碼

11.2 重命名

導出時重命名

function say(){
	console.log('say');
}
export {say as say2};
複製代碼

導入時重命名

import {say2 as say3} from './school.js'
複製代碼

11.3 默認導出

每一個模塊均可以有一個默認要導出的東西

export default function say(){
	console.log('say')
}
複製代碼

導入

import say from './school.js'
複製代碼

11.4 深度克隆

var parent = {
  age: 5,
  hobby: [1, 2, 3],
  home: {city: '北京'},
};

var child = extendDeep(parent);
child.age = 6;
child.hobby.push('4');
child.home.city = '廣東';
console.log('child ', child); //[1, 2, 3, 4]
console.log('parent ', parent);
function extend(parent) {
  let child;
  if (Object.prototype.toString.call(parent) == '[object Object]') {
    child = {};
    for (let key in parent) {
      child[key] = extend(parent[key])
    }
  } else if (Object.prototype.toString.call(parent) == '[object Array]') {
    child = parent.map(item => extend(item));
  } else {
    return parent;
  }
  return child;
}

function extendDeep(parent, child) {
  child = child || {};
  for (var key in parent) {
    if (typeof parent[key] === "object") {
      child[key] = (Object.prototype.toString.call(parent[key]) === "[object Array]") ? [] : {};
      extendDeep(parent[key], child[key]);
    } else {
      child[key] = parent[key];
    }
  }
  return child;
}
複製代碼

12.JavaScript(ES6) 中條件語句

12.1 使用 Array.includes 來處理多個條件

function test(fruit) {
  if (fruit == 'apple' || fruit == 'strawberry') {
    console.log('red');
  }
}
複製代碼

優化變成 ->>

function test(fruit) {
  // 條件提取到數組中
  const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];
 
  if (redFruits.includes(fruit)) {
    console.log('red');
  }
}
複製代碼

12.2 減小嵌套,提早使用 return 語句

function test(fruit, quantity) {
  const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];
 
  // 條件 1:fruit 必須有值
  if (fruit) {
    // 條件 2:必須爲紅色
    if (redFruits.includes(fruit)) {
      console.log('red');
 
      // 條件 3:數量必須大於 10
      if (quantity > 10) {
        console.log('big quantity');
      }
    }
  } else {
    throw new Error('No fruit!');
  }
}
 
// 測試結果
test(null); // 拋出錯誤:No fruits
test('apple'); // 打印:red
test('apple', 20); // 打印:red,big quantity
複製代碼

優化

/* 在發現無效條件時提早 return */
 
function test(fruit, quantity) {
  const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];
 
  // 條件 1:提早拋出錯誤
  if (!fruit) throw new Error('No fruit!');
 
  // 條件2:必須爲紅色
  if (redFruits.includes(fruit)) {
    console.log('red');
 
    // 條件 3:數量必須大於 10
    if (quantity > 10) {
      console.log('big quantity');
    }
  }
}
複製代碼

爲了減小一個嵌套層級,優化編碼風格

/* 在發現無效條件時提早 return */
 
function test(fruit, quantity) {
  const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];
 
  if (!fruit) throw new Error('No fruit!'); // 條件 1:提早拋出錯誤
  if (!redFruits.includes(fruit)) return;  // 條件 2:當 fruit 不是紅色的時候,提早 return
 
  console.log('red');
 
  // 條件 3:必須是大量存在
  if (quantity > 10) {
    console.log('big quantity');
  }
}
複製代碼

12.3.使用函數的默認參數 和 解構

function test(fruit, quantity) {
  if (!fruit) return;
  const q = quantity || 1; // 若是沒有提供 quantity 參數,則默認爲 1
 
  console.log(`We have ${q} ${fruit}!`);
}
 
// 測試結果
test('banana'); // We have 1 banana!
test('apple', 2); // We have 2 apple!
複製代碼

可是q在這邊不直觀全部優化

function test(fruit, quantity = 1) { // i若是沒有提供 quantity 參數,則默認爲 1
  if (!fruit) return;
  console.log(`We have ${quantity} ${fruit}!`);
}
 
// 測試結果
test('banana'); // We have 1 banana!
test('apple', 2); // We have 2 apple!
複製代碼

可是這邊 也多是個對象

// 解構 —— 只得到 name 屬性
// 參數默認分配空對象 {}
function test({name} = {}) {
  console.log (name || 'unknown');
}
 
//測試結果
test(undefined); // unknown
test({ }); // unknown
test({ name: 'apple', color: 'red' }); // apple
複製代碼

12.4 選擇 Map / Object 字面量,而不是Switch語句

function test(color) {
  // 使用 switch case 語句,根據顏色找出對應的水果
  switch (color) {
    case 'red':
      return ['apple', 'strawberry'];
    case 'yellow':
      return ['banana', 'pineapple'];
    case 'purple':
      return ['grape', 'plum'];
    default:
      return [];
  }
}
 
//測試結果
test(null); // []
test('yellow'); // ['banana', 'pineapple']
複製代碼

這邊建議使用對象,更加清晰

// 使用對象字面量,根據顏色找出對應的水果
  const fruitColor = {
    red: ['apple', 'strawberry'],
    yellow: ['banana', 'pineapple'],
    purple: ['grape', 'plum']
  };
 
function test(color) {
  return fruitColor[color] || [];
}
複製代碼

可是這邊是頗有可能爲網絡數據,沒法判斷red這樣的變量,那麼就用arry.filter 來過濾

const fruits = [
    { name: 'apple', color: 'red' }, 
    { name: 'strawberry', color: 'red' }, 
    { name: 'banana', color: 'yellow' }, 
    { name: 'pineapple', color: 'yellow' }, 
    { name: 'grape', color: 'purple' }, 
    { name: 'plum', color: 'purple' }
];
 
function test(color) {
  // 使用 Array filter ,根據顏色找出對應的水果
 
  return fruits.filter(f => f.color == color);
}
複製代碼

12.5 使用 Array.every 和 Array.some 來處理所有/部分知足條件

咱們想檢查全部水果是否都是紅色的

const fruits = [
    { name: 'apple', color: 'red' },
    { name: 'banana', color: 'yellow' },
    { name: 'grape', color: 'purple' }
  ];
 
function test() {
  let isAllRed = true;
 
  // 條件:全部的水果都必須是紅色
  for (let f of fruits) {
    if (!isAllRed) break;
    isAllRed = (f.color == 'red');
  }
 
  console.log(isAllRed); // false
}
複製代碼

使用 arry.every來過濾

const fruits = [
    { name: 'apple', color: 'red' },
    { name: 'banana', color: 'yellow' },
    { name: 'grape', color: 'purple' }
  ];
 
function test() {
  // 條件:簡短方式,全部的水果都必須是紅色
  const isAllRed = fruits.every(f => f.color == 'red');
 
  console.log(isAllRed); // false
}
複製代碼

若是咱們想要檢查是否有至少一個水果是紅色的,咱們可使用 Array.some 僅用一行代碼就實現出來

const fruits = [
    { name: 'apple', color: 'red' },
    { name: 'banana', color: 'yellow' },
    { name: 'grape', color: 'purple' }
];
 
function test() {
  // 條件:是否存在紅色的水果
  const isAnyRed = fruits.some(f => f.color == 'red');
 
  console.log(isAnyRed); // true
}
複製代碼

13 promise

13.1 異步回調

13.1.1 回調地獄

在須要多個操做的時間,會致使多個回調函數嵌套,致使代碼不夠直觀,就常說的回調地獄

13.1.2 並行結果

若是幾個異步操做之間並無先後順序之分,但須要等多個異步完成操做完成後才能執行後續的任務,沒法實現並行節約時間

13.2 Promise

promise本意是承諾,在程序中的意思就是承諾我過一段時間後會給你一個結果。什麼時間會用到過一段時間?答案是異步操做,異步是指可能比較長時間纔有結果的才作,例如網絡請求、讀取本地文件等

13.3 Promise的三種狀態

  • Pending Promise對象勢力建立時候的初始化狀態
  • Fulfilled 能夠理解爲成功的狀態
  • Rejected 能夠理解爲失敗的狀態

then方法就是用來指定Promise 對象的狀態改變時肯定執行的操做,resolve時執行第一個函數(onFulfilled),reject時執行第二函數(onRejected)

13.4 構造一個Promise

13.4.1 使用Promise

let promise = new Promise((resolve,reject)=>{
		setTimeout(()=>{
    	if(Math.random()>0.5)
      	resolve('This is resolve!')
      else
      	reject('This is reject')
    },1000);
});
promise.then(Fulfilled,Rejected)
複製代碼
  • 構造一個Promise實例須要給Promise構造函數傳入一個函數
  • 傳入的函數須要有兩個形參,兩個形參都是function類型的參數。
    • 第一個形參運行後會讓Promise實例處於resolve狀態,因此咱們通常給第一個形參命名爲resolve,使 Promise對象的狀態改變成成功,同時傳遞一個參數用於後續成功後的操做
    • 第一個形參運行後悔讓Promise實例處於reject狀態,因此咱們通常給第一個形參命名爲reject,將Promise對象的狀態改變爲失敗,同事將錯誤的信息傳遞到後續錯誤處理的操做

13.4.2 es5模擬Promise

function Promise(fn){
	this.success(data);
},(error)=>{
	this.error();
}

Promise.prtotype.resolve = function (data){
	this.success(data);
}

Promise.prototype.then = function (success,error){
	this.success = success;
	this.error = error;
}
複製代碼

13.4.3 es5模擬Promise

class Promise{
	constructor(fn){
  		fn((data)=>{
      	this.success(data);
      },(error)=>{
      	this.error();
      })
  }
  
  resolve(data){
  	this.success(data);
  }
  
  reject(error){
  	this.error(error);
  }
  
  then(success,error){
  	this.success = success;
    this.error = error;
    console.log(this);
  }

}
複製代碼

13.5 promise 作爲函數的返回值

function ajaxPromise(queryUrl){
	return new Promise((resolve,reject)=>{
  	xhr.open('GET',queryUrl,ture);
    xhr.send(null);
    xhr.onreadystatechange = () =>{
    		if(xhr.readyState === 4 ){
        	if(xhr.status === 200){
          	resolve(xhr.responseText);
          }else{
          	reject(xhr.responseText);
          }
        }
    }
  })
}


ajaxPromise('http://www.baidu.com')
	.then((value)=>{
  	console.log(value);
  })
  .catch((err)=>{
  	console.error(err);
 });
複製代碼

13.6 promise的鏈式調用

  • 每次調用返回的都是一個新的Promise實例
  • 鏈式調用的參數經過返回值傳遞

then 可使用鏈式調用的寫法緣由在於,每一次執行該方法時老是會返回一個 Promise 對象

readFile('1.txt').then(function(data){
	console.log(data);
}).then(function (data){
	console.log(data);
  return readFile(data);
}).then(function (data){
	console.log(data);
}).catch(function (err){
	console.log(err);
})
複製代碼

13.7 promise API

13.7.1 Promise.all

  • 參數:接受一個數組,數組內都是Promise實例
  • 返回值: 返回一個 promise 實例,這個promise 實例的狀態轉移取決於參數的 promise實例的狀態變化。當參數處於resolve狀態時,返回resolve狀態。若是參數中任意一個實例處於reject狀態,返回的promise實例變爲reject狀態。
Promise.all([p1,p2]).then(function(result){
	console.log(result); //[ '2.txt', '2' ]
})
複製代碼

無論兩個promise誰先完成,Promise.all 方法會按照數組裏面的順序將結果返回

13.7.2 Promise.race

  • 參數:接受一個數組,數組內都是Promise實例
  • 返回值: 返回一個 promise 實例,這個promise 實例的狀態轉移取決於參數的 promise實例的狀態變化。當參數處於resolve狀態時,返回resolve狀態。若是參數中任意一個實例處於reject狀態,返回的promise實例變爲reject狀態。
Promise.race([p1,p2]).then(function(result){
	console.log(result); //[ '2.txt', '2' ]
})
複製代碼

13.7.3 Promise.resolve

返回一個Promise 實例,這個實例處於resolve狀態。
根據傳入的參數不一樣有不一樣的功能:

  • 值(對象、數組、字符串等):做爲resolve傳遞出去的值
  • Promise 實例 : 原封不動返回

Promise.reject

返回一個Promise實例,這個實例處於reject狀態

  • 參數通常就是拋出的錯誤信息。

13.8 q

Q是一個在Javascrip中實現promise的模塊

13.8.1 q的基本用法

var Q = require('q');
var fs = require('fs');
function read(filename){
	var deferred = Q.defer();
  fs.readFile(filename,'utf8',function)(err,data){
  	if(err){
    	deferred.reject(err);
    }else{
  		deferred.resolve(data);
  	}
  });
}

read('1.txt1').then(function(data){
	console.log(data);
},funtcion(error){
	console.error(error);                    
})
複製代碼

13.8.2 q的簡單實現

module.exports = {
    defer(){
        var _success,_error;
        return {
            resolve(data){
                _success(data);
            },
            reject(err){
                _error(err);
            },
            promise:{
                then(success,error){
                    _success = success;
                    _error = error;
                }
            }
        }
    }
}
複製代碼

13.8.3 q的實現

var defer = function () {
    var pending = [], value;
    return {
        resolve: function (_value) {
            if (pending) {
                value = _value;
                for (var i = 0, ii = pending.length; i < ii; i++) {
                    var callback = pending[i];
                    callback(value);
                }
                pending = undefined;
            }
        },
        promise: {
            then: function (callback) {
                if (pending) {
                    pending.push(callback);
                } else {
                    callback(value);
                }
            }
        }
    };
};
複製代碼

13.9 bluebird

實現 promise 標準的庫是功能最全,速度最快的一個庫

13.9.1 bluebird經典使用

var Promise = require('./bluebird');

var readFile = Promise.promisify(require("fs").readFile);
readFile("1.txt", "utf8").then(function(contents) {
    console.log(contents);
})

var fs = Promise.promisifyAll(require("fs"));

fs.readFileAsync("1.txt", "utf8").then(function (contents) {
    console.log(contents);
})
複製代碼

13.9.2 bluebird簡單實現

module.exports = {
    promisify(fn){
        return function () {
            var args = Array.from(arguments);
            return new Promise(function (resolve, reject) {
                fn.apply(null, args.concat(function (err) {
                    if (err) {
                        reject(err);
                    } else {
                        resolve(arguments[1])
                    }
                }));
            })
        }
    },
    promisifyAll(obj){
        for(var attr in obj){
            if(obj.hasOwnProperty(attr) && typeof obj[attr] =='function'){
                obj[attr+'Async'] = this.promisify(obj[attr]);
            }
        }
        return obj;
    }
}
複製代碼

13.10 動畫

<!DOCTYPE html>
<html lang="en"> <head> <meta charset="UTF-8"> <title>move</title> <style> .square{ width:40px; height:40px; border-radius: 50%; } .square1{ background-color: red; } .square2{ background-color: yellow; } .square3{ background-color: blue; } </style> </head> <body> <div class="square square1" style="margin-left: 0"></div> <div class="square square2" style="margin-left: 0"></div> <div class="square square3" style="margin-left: 0"></div> </body> <script> var square1 = document.querySelector('.square1'); var square2 = document.querySelector('.square2'); var square3 = document.querySelector('.square3'); /*function move(element,target,resolve){ let timer = setInterval(function(){ var marginLeft = parseInt(element.style.marginLeft, 10); if(marginLeft == target){ resolve(); }else{ element.style.marginLeft = ++marginLeft+'px'; } },13); }*/ function move(element,target,resolve){ let current = 0; let timer = setInterval(function(){ element.style.transform=`translateX(${++current}px)`; if(current>target){ clearInterval(timer); resolve(); }; },13); } function animate(element,target){ return new Promise(function(resolve,reject){ move(element,target,resolve); }); } animate(square1,100) .then(function(){ return animate(square2,100); }) .then(function(){ return animate(square3,100); }); </script> </html> 複製代碼

13.11. co

13.11.1 co初體驗

let fs = require('fs');
function getNumber(){
  return new Promise(function (resolve,reject) {
    setTimeout(function(){
      let number = Math.random();
      if(number >.5){
        resolve(number);
      }else{
        reject('數字過小');
      }
    },1000);
  });
}
function *read(){
  let a = yield getNumber();
  console.log(a);
  let b = yield 'b';
  console.log(b);
  let c = yield getNumber();
  console.log(c);
}

function co(gen){
  return new Promise(function(resolve,reject){
    let g = gen();
    function next(lastValue){
      let {done,value} = g.next(lastValue);
      if(done){
         resolve(lastValue);
      }else{
        if(value instanceof Promise){
          value.then(next,function(val){
            reject(val);
          });
        }else{
          next(value);
        }
      }
    }
    next();
  });
}
co(read).then(function(data){
  console.log(data);
},function(reason){
  console.log(reason);
});
複製代碼

13.11.2 co連續讀文件

let fs = require('fs');
function readFile(filename){
  return new Promise(function (resolve,reject) {
    fs.readFile(filename,'utf8',function(err,data){
      if(err)
        reject(err);
      else
        resolve(data);
    })
  });
}
function *read(){
  let a = yield readFile('./1.txt');
  console.log(a);
  let b = yield readFile('./2.txt');
  console.log(b);
}

function co(gen){
  let g = gen();
  function next(val){
    let {done,value} = g.next(val);
    if(!done){
      value.then(next);
    }
  }
  next();
}
複製代碼

13.12 Promise/A+完整實現

function Promise(executor) {
  let self = this;
  self.status = "pending";
  self.value = undefined;
  self.onResolvedCallbacks = [];
  self.onRejectedCallbacks = [];
  function resolve(value) {
    if (value instanceof Promise) {
      return value.then(resolve, reject)
    }
    setTimeout(function () { // 異步執行全部的回調函數
      if (self.status == 'pending') {
        self.value = value;
        self.status = 'resolved';
        self.onResolvedCallbacks.forEach(item => item(value));
      }
    });

  }

  function reject(value) {
    setTimeout(function () {
      if (self.status == 'pending') {
        self.value = value;
        self.status = 'rejected';
        self.onRejectedCallbacks.forEach(item => item(value));
      }
    });
  }

  try {
    executor(resolve, reject);
  } catch (e) {
    reject(e);
  }
}
function resolvePromise(promise2, x, resolve, reject) {
  if (promise2 === x) {
    return reject(new TypeError('循環引用'));
  }
  let then, called;

  if (x != null && ((typeof x == 'object' || typeof x == 'function'))) {
    try {
      then = x.then;
      if (typeof then == 'function') {
        then.call(x, function (y) {
          if (called)return;
          called = true;
          resolvePromise(promise2, y, resolve, reject);
        }, function (r) {
          if (called)return;
          called = true;
          reject(r);
        });
      } else {
        resolve(x);
      }
    } catch (e) {
      if (called)return;
      called = true;
      reject(e);
    }
  } else {
    resolve(x);
  }
}
Promise.prototype.then = function (onFulfilled, onRejected) {
  let self = this;
  onFulfilled = typeof onFulfilled == 'function' ? onFulfilled : function (value) {
    return value
  };
  onRejected = typeof onRejected == 'function' ? onRejected : function (value) {
    throw value
  };
  let promise2;
  if (self.status == 'resolved') {
    promise2 = new Promise(function (resolve, reject) {
      setTimeout(function () {
        try {
          let x = onFulfilled(self.value);
          resolvePromise(promise2, x, resolve, reject);
        } catch (e) {
          reject(e);
        }
      });

    });
  }
  if (self.status == 'rejected') {
    promise2 = new Promise(function (resolve, reject) {
      setTimeout(function () {
        try {
          let x = onRejected(self.value);
          resolvePromise(promise2, x, resolve, reject);
        } catch (e) {
          reject(e);
        }
      });
    });
  }
  if (self.status == 'pending') {
    promise2 = new Promise(function (resolve, reject) {
      self.onResolvedCallbacks.push(function (value) {
        try {
          let x = onFulfilled(value);
          resolvePromise(promise2, x, resolve, reject);
        } catch (e) {
          reject(e);
        }
      });
      self.onRejectedCallbacks.push(function (value) {
        try {
          let x = onRejected(value);
          resolvePromise(promise2, x, resolve, reject);
        } catch (e) {
          reject(e);
        }
      });
    });
  }
  return promise2;
}
Promise.prototype.catch = function (onRejected) {
  return this.then(null, onRejected);
}
Promise.all = function (promises) {
  return new Promise(function (resolve, reject) {
    let result = [];
    let count = 0;
    for (let i = 0; i < promises.length; i++) {
      promises[i].then(function (data) {
        result[i] = data;
        if (++count == promises.length) {
          resolve(result);
        }
      }, function (err) {
        reject(err);
      });
    }
  });
}

Promise.deferred = Promise.defer = function () {
  var defer = {};
  defer.promise = new Promise(function (resolve, reject) {
    defer.resolve = resolve;
    defer.reject = reject;
  })
  return defer;
}
/** * npm i -g promises-aplus-tests * promises-aplus-tests Promise.js */
try {
  module.exports = Promise
} catch (e) {
}

複製代碼
相關文章
相關標籤/搜索