[toc]
## Proxy和Reflect
### Proxy
Proxy用來修改某些操作的默認行為,可以理解為在目標對象前設置一個攔截層,外界對該對象的訪問都必須通過這層攔截,從而實現對屬性值的訪問、修改以及對屬性的其他操作的監視。
```
var obj = new Proxy({},{
get:function(target,key,receiver){
console.log("get value");
return Reflect.get(target,key,receiver);
},
set:function(target,key,value,receiver){
console.log("set value");
return Reflect.set(target,key,value,receiver);
}
})
obj.count = 1;
// set value
obj.count;
// get value
// 1
```
在對目標對象設置攔截時候首先要為目標對象生成一個Proxy實例,然后對實例進行操作,而不是對目標對象進行操作:
```
var proxy = new Proxy(target,handler);
// target為目標對象,handler也是一個對象,用來定義攔截行為。
```
對屬性的操作有讀取、設置值、刪除屬性等等,如果沒有設置對應的攔截操作則默認為直接對原對象操作,比如設置了get攔截,但是沒有設置set攔截,那么在讀取值時會執行攔截,而在設置值時直接設置,不進行任何操作。
### Proxy實例的方法
**get()**方法
get方法用以攔截對某個屬性的讀取操作
```
var proxy = new Proxy(target,{
get:function(target,property){
if(property in target){
return target[property];
}else{
return "No property in target";
}
}
})
//如果存在對應的屬性則返回屬性值,如果不存在就返回"No property in target",而不是Undefined
```
**set()**
set方法用來攔截對某個屬性的賦值操作
```
var proxy = new Proxy(target,{
set:function(target,prop,value,receiver){
if(prop === 'age'){
if(value<0 || value>120){
throw new Error("Error age!")
}
}
target[prop] = value;
}
})
//如果設置的屬性名為age則限制值的范圍,否則拋出錯誤
```
**apply()**
apply方法用以攔截函數的調用,call以及apply,apply方法接受三個參數:目標對象,目標對象上下文(this)以及目標對象參數數組
```
var target = function(){
return "I am the target";
}
var handler = {
apply:function(){
return "I am the proxy";
}
}
var p = new Proxy(target,handler);
p(); //"I am the proxy"
//運行了Proxy實例中定義的apply方法
```
**has()**
has方法用來設置in運算符時的操作,可以隱藏某些屬性不被in發現
```
var proxy = new Proxy(target,{
has:function(target,key){
if(key[0] === "_"){
return false;
}
return key in target;
}
})
//將以'_'開始的屬性名隱藏(返回false)
```
**construct()**
construct方法用來攔截new操作,返回必須為對象
```
//target為構造函數
var proxy = new Proxy(target,{
construct:function(target,args){
return {
value:args
}
}
})
new proxy(10); //{value:10}
```
**deleteProperty()**
deleteProperty方法用來攔截delete操作
```
var proxy = new Proxy({a:2},{
deleteProperty:function(target,key){
if(key in target){
delete target.key;
console.log("success");
}else{
throw new Error("No that property");
}
}
})
delete proxy.a; // success
delete proxy.b; //Error:No that property
```
**enumerate()**
enumerate方法用于攔截for...in循環,注意與has方法區分。has方法攔截的是in操作符。
```
var proxy = new Proxy(target,{
enumerate:function(target){
return Object.keys(target).filter(key=>key[0]!=='_');
}
})
將以'_'開頭的屬性隱藏起來,避免被for...in遍歷到。
```
**getOwnPropertyDescriptor()**
攔截Object.getOwnPropertyDescriptor
**getProrotypeOf()**
攔截Object.getPeototypeOf()運算符
**ownKeys()**
攔截Object.keys()操作
**preventExtensions()**
攔截Object.preventExtensions()
**setPrototypeOf()**
攔截Object.setPrototypeOf()方法
## Promise
Promise是一個可以傳遞異步操作的對象。有以下特點:
- 對象的狀態不受外界影響。Promise對象代表一個異步操作,有3種狀態:Pending(進行中),Resolved(已完成)和Rejected(已失敗)。
- 對象的狀態一旦改變就不會再次被其他手段改變,狀態的改變有兩種可能:從Pending變成Resolved和從Pending變成Rejected。
### 基本用法
Promise是一個對象,在使用前首先對其進行實例化:
```
var promise = new Promise(function(resolve,reject){
//...do something
if(//操作成功){
resolve(value);
}else{
reject(error);
}
})
```
Promise構造函數接受一個函數為參數,這個函數的兩個參數分別為resolve和reject。這兩個參數也為函數,不需要自己指定。
resolve函數的作用是當異步操作執行成功時候將操作結果作為參數傳遞出去,reject的作用是當異步操作失敗時候將失敗信息作為參數傳遞出去。
在實例化Promise對象之后,可以用then方法分別指定成功或失敗后的回調函數:
```
promise.then(function(value){
//成功時執行
},function(error){
//失敗時執行
})
```
下面是一個異步加載圖片的例子:
```
function loadImage(url){
return new Promise(function(resolve,reject){
var image = new Image();
image.onload = function(){
resolve(image);
}
image.onerror = function(){
reject(new Error("Couldn't load image at "+url));
}
image.src = url;
})
}
loadImage("./img.png").then((image)=>{
console.log(image)
document.body.append(image); //如果成功將其追加到body中
},(err)=>{
console.log(err); //失敗的話打印失敗信息
})
```
在上述例子中,resolve和reject函數的參數都為一個確切的值,除此之外resolve的值還可以為另一個Peomise實例,這樣就形成了多個異步操作之間的銜接。
```
var p1 = new Promise((resolve,reject)=>{
//...
})
var p2 = new Promise((resolve,reject)=>{
//...
resolve(p1);
})
```
在上述代碼中,p2的resolve函數參數為p1,也就是p2的異步操作結果返回另一個異步操作。
此時p1的狀態決定了p2的狀態,當p1為Pending時,p2的回調就會等待p1的結果。如果p1的狀態改為Resolved則p2的回調就會立即執行。如果p1的狀態改為Rejected則p2也會變為Rejected。
### Promise.prototype.then()
用來為Promise實例添加狀態改變時的回調函數。
### Promise.prototype.catch()
可以看做是.then(null,rejection)的別名,用以指定發生錯誤時的回調函數。
```
promise.then((value)=>{
//success
}).catch((err)=>{
//failed
})
```
### Promise.all()
用來將多個Promise實例包裝成一個Promise實例
```
var p = Promise.all([p1,p2,p3]);
```
此時p的狀態就由p1,p2,p3共同決定,只有p1,p2,p3的狀態都為Resolve時p的狀態才為Resolve.
### Promise.race()
也用來將多個Promise實例包裝成一個Promise實例,與Promise.all()不同的是p1,p2,p3中只要有一個狀態改變則包裝后的實例狀態就跟著改變
### Promise.resolve()
將現有的對象轉為Promise對象。
比如:
```
var promise = Promise.resolve($.ajax("./data.json"))
```
### done()和finally()
done()方法也可以鏈式使用。不管使用then方法還是catch方法,本身then方法和catch方法也可能拋出錯誤,這些錯誤沒有辦法被捕捉到,因為Promise內部的錯誤不會被冒泡到全局,因此在鏈式回調的末端需要使用done()方法可以保證出錯時候被捕捉到。
```
promise.then(...)
.catch(...)
.then(...)
.catch(...)
.done()
```
finally方法用于指定不管Promise對象的最后狀態如何都會執行的操作。與done不同的是finally接受一個普通的函數為參數,無論最后結果如何都會執行。