1. 程式人生 > >深入理解ES6--12.代理與反射介面

深入理解ES6--12.代理與反射介面

主要知識點:代理和反射的定義、常用的陷阱函式、可被撤銷的代理、將代理物件作為原型使用、將代理作為類的原型

代理與反射介面的知識點

1. 代理和反射

代理是什麼?

通過呼叫 new Proxy() ,你可以建立一個代理用來替代另一個物件(被稱之為目目標物件) ,這個代理對目標物件進行了虛擬,因此該代理與該目標物件表面上可以被當作同一個物件來對待。

代理允許你攔截目標物件上的底層操作,而這本來是JS引擎的內部能力,攔截行為適用了一個能響應特定操作的函式(被稱之為陷阱);

反射是什麼?

Reflect物件所代表的反射介面,是給底層操作提供預設行為的方法的集合,這些操作是能夠被代理重寫的

。每個代理陷阱都有一個對應的反射方法,每個方法都與對應的陷阱函式同名,並且接收的引數也與之一致。

建立一個簡單的代理

使用Proxy構建可以建立一個簡單的代理物件,需要傳遞兩個引數:目標物件以及一個處理器,後者是定義一個或多個陷阱函式的物件。如果不定義陷阱函式,則依然使用目標物件的預設行為。

2. 常用陷阱函式

2.1 基本陷阱函式

1.使用Set陷阱函式驗證屬性值

假如有這樣一個場景,必須要求物件的屬性值必須只能是數值,這就意味著該物件每個新增屬性時都要被驗證,並且在屬性不為數值屬性時就應該丟擲錯誤。因此就需要使用set陷阱函式來重寫set函式的預設行為,set陷阱函式接收四個引數:

  1. trapTarget:代理的目標物件;
  2. key:需要寫入的屬性的鍵;
  3. value:被寫入屬性的值;
  4. receiver:操作發生的物件(通常是代理物件)

Reflect.set()set陷阱函式對應的反射方法,同時也是set操作的預設行為,Reflect.set()方法與set陷阱函式一樣,能夠接受四個引數。

針對上述場景,示例程式碼:

//set陷阱函式
let target = {
    name:'target'
}
let proxy = new Proxy(target,{
    set(tarpTarget,key,value,receiver){

        if(!tarpTarget.hasOwnProperty(key)){
            if(isNaN(value)){
                throw new Error('property must be number');
            }
        }
        return Reflect.set(tarpTarget,key,value,receiver);
    }
});

proxy.msg='hello proxy'; //Uncaught Error: property must be number

通過set陷阱函式就可以檢測設定屬性時屬性值的型別,當屬性值不是數字時,就會丟擲錯誤。

2.使用get陷阱函式進行物件外形驗證

物件外形(Object Shape)指的是物件已有的屬性與方法的集合。能夠使用代理很方便進行物件外形驗證。由於使用屬性驗證只需要在讀取屬性時被觸發,因此只需要使用get陷阱函式。該函式接受三個引數:

  1. trapTarget:代理的目標物件;
  2. key:需要讀取的屬性的鍵;
  3. receiver:操作發生的物件(通常是代理物件);

相應的Reflect.get()方法同樣擁有這三個引數。進行物件外形驗證的示例程式碼:

//get陷阱函式

let target={
    name:'hello world'
}

let proxy = new Proxy(target,{
        get(tarpTarget,key,receiver){
            if(!(key in tarpTarget)){
                throw new Error('不存在該物件');
            }
            return Reflect.get(tarpTarget,key,receiver);
        }
    });
console.log(proxy.name); //hello world
console.log(proxy.age); // Uncaught Error: 不存在該物件

使用get陷阱函式進行物件外形驗證,由於target物件存在name屬性,所以可以正常返回,當獲取age屬性時,由於該屬性並不存在,所以會丟擲錯誤。

3.使用has陷阱函式隱藏屬性

in運算子用於判斷指定物件中是否存在某個屬性,如果物件的屬性名與指定的字串或符號值相匹配,那麼in運算子就會返回true。無論該屬性是物件自身的屬性還是其原型的屬性。

has陷阱函式會在使用in運算子的情況下被呼叫,控制in運算子返回不同的結果,has陷阱函式會傳入兩個引數:

  1. trapTarget:代理的目標物件;
  2. key:屬性鍵;

Reflect.has()方法接收相同的引數,並向in運算子返回預設的響應結果,用於返回預設響應結果。

例如想要隱藏value屬性:

//has陷阱函式
let target = {
    value:'hello world'
}

let proxy = new Proxy(target,{
    has(tarpTarget,key){
        if(Object.is(key,'value')){
            return false;
        }
        Reflect.has(tarpTarget,key);
    }
})

console.log('value' in proxy); //false

使用has陷阱函式,能夠控制in運算子的結果,value屬性在target物件中存在,通過代理的has陷阱函式使得在檢查value屬性時返回false,達到隱藏屬性的效果。

4.使用deleteProperty陷阱函式避免屬性被刪除

deleteProperty 陷阱函式會在使用delete 運算子刪除物件屬性時被呼叫,該方法接收兩個引數:

  1. trapTarget:代理的目標物件;
  2. key:需要刪除的鍵;

Reflect.deleteProperty() 方法也接受這兩個引數,並提供了 deleteProperty 陷阱函式的預設實現。你可以結合 Reflect.deleteProperty()方法以及 deleteProperty 陷阱函式,來修改 delete 運算子的行為。例如,能確保 value 屬性不被刪除:

let target = {
    name: "target",
    value: 42
};
let proxy = new Proxy(target, {
    deleteProperty(trapTarget, key) {
        if (key === "value") {
            return false;
        } else {
            return Reflect.deleteProperty(trapTarget, key);
        }
    }
});
// 嘗試刪除 proxy.value
console.log("value" in proxy); // true
let result1 = delete proxy.value;
console.log(result1); // false

2.2 原型代理上的陷阱函式

在呼叫Object.setPrototypeOf()和getPrototypeOf()方法時,可以使用setPrototypeOfgetPrototypeOf陷阱函式來影響Object上相應的兩個方法的效果。setPrototypeOf陷阱函式接收兩個引數:

  1. trapTarget:代理的目標物件;
  2. proto:需要被用作原型的物件;

setPrototypeOf()方法與Reflect.setPrototypeOf()傳入相同的引數。另外,getPrototypeOf陷阱函式只接收trapTarget引數,Reflect.getPrototype也只接收一個引數。

例如,通過返回 null 隱藏了代理物件的原型,並且使得該原型不可被修改:

//原型代理上的陷阱函式

let target = {};
let proxy = new Proxy(target, {
    getPrototypeOf(trapTarget) {
        return null;
    },
    setPrototypeOf(trapTarget, proto) {
        return false;
    }
});
let targetProto = Object.getPrototypeOf(target);
let proxyProto = Object.getPrototypeOf(proxy);
console.log(targetProto === Object.prototype); // true
console.log(proxyProto === Object.prototype); // false
console.log(proxyProto); // null
// 成功
Object.setPrototypeOf(target, {});
// 丟擲錯誤
Object.setPrototypeOf(proxy, {});

使用 target 物件作為引數呼叫Object.getPrototypeOf() 會返回一個物件值;而使用 proxy 物件呼叫該方法則會返回null ,因為 getPrototypeOf 陷阱函式被呼叫了。類似的,使用 target 去呼叫Object.setPrototypeOf() 會成功;而由於 setPrototypeOf 陷阱函式的存在,使用 proxy則會引發錯誤。

2.3 物件可擴充套件性的陷阱函式

ES5 通過Object.preventExtensions()Object.isExtensible() 方法給物件增加了可擴充套件性。而 ES6 則通過 preventExtensionsisExtensible 陷阱函式允許代理攔截對於底層物件的方法呼叫。這兩個陷阱函式都接受名為 trapTarget 的單個引數,此引數代表代理的目標物件。 isExtensible 陷阱函式必須返回一個布林值用於表明目標物件是否可被擴充套件,而 preventExtensions 陷阱函式也需要返回一個布林值,用於表明操作是否已成功。同時也存在Reflect.preventExtensions()Reflect.isExtensible() 方法,用於實現預設的行為。這兩個方法都返回布林值,因此它們可以在對應的陷阱函式內直接使用。

例如,不想讓代理物件的Object.preventExtensios()操作成功,可以強制preventExtensions陷阱函式返回false

//物件可擴充套件性的陷阱函式


let target = {};
let proxy = new Proxy(target, {
    isExtensible(trapTarget) {
    return Reflect.isExtensible(trapTarget);
    },
    preventExtensions(trapTarget) {
    return false
    }
});
console.log(Object.isExtensible(target)); // true
console.log(Object.isExtensible(proxy)); // true
Object.preventExtensions(proxy);
console.log(Object.isExtensible(target)); // true
console.log(Object.isExtensible(proxy)); // true

2.4 屬性描述符的陷阱函式

ES5 最重要的特徵之一就是引入了Object.defineProperty()方法用於定義屬性的特性。在JS 之前的版本中,沒有方法可以定義一個訪問器屬性,也不能讓屬性變成只讀或是不可列舉。而這些特性都能夠利用 Object.defineProperty()方法來實現,並且你還可以利用Object.getOwnPropertyDescriptor()方法來檢索這些特性。代理允許你使用 definePropertygetOwnPropertyDescriptor 陷阱函式,來分別攔截對於Object.defineProperty()Object.getOwnPropertyDescriptor() 的呼叫。 defineProperty
陷阱函式接受下列三個引數:

  1. trapTarget :需要被定義屬性的物件(即代理的目標物件) ;
  2. key :屬性的鍵(字串型別或符號型別) ;
  3. descriptor :為該屬性準備的描述符物件。

defineProperty 陷阱函式要求你在操作成功時返回 true ,否則返回 falsegetOwnPropertyDescriptor 陷阱函式則只接受 trapTargetkey 這兩個引數,並會返回對應的描述符。 Reflect.defineProperty()Reflect.getOwnPropertyDescriptor() 方法作為上述陷阱函式的對應方法,接受與之相同的引數。

defineProperty 陷阱函式要求你返回一個布林值用於表示操作是否已成功。當它返回 true時, Object.defineProperty() 會正常執行;而如果它返回了 false ,則Object.defineProperty()會丟擲錯誤。 你可以使用該功能來限制哪些屬性可以被Object.defineProperty() 方法定義。

etOwnPropertyDescriptor 陷阱函式有一個微小差異,要求返回值必須是 null
undefined ,或者是一個物件。如果返回值是一個物件,則只允許該物件擁有 enumerable
configurablevaluewritablegetset 這些自有屬性

2.5 ownKeys陷阱函式

ownKeys 代理陷阱攔截了內部方法 [[OwnPropertyKeys]] ,並允許你返回一個數組用於重寫該行為。返回的這個陣列會被用於四個方法: Object.keys() 方法、Object.getOwnPropertyNames() 方法、Object.getOwnPropertySymbols()方法與Object.assign() 方法,其中 Object.assign() 方法會使用該陣列來決定哪些屬性會被複制。

ownKeys 陷阱函式接受單個引數,即目標物件,同時必須返回一個數組或者一個類陣列物件。你可以使用 ownKeys 陷阱函式去過濾特定的屬性,以避免這些屬性被Object.keys() 方法、Object.getOwnPropertyNames() 方法、Object.getOwnPropertySymbols() 方法或 Object.assign() 方法使用。

2.6 apply與construct陷阱函式

只有 applyconstruct 要求代理目標物件必須是一個函式。函式擁有兩個內部方法:[[Call]][[Construct]] ,前者會在函式被直接呼叫時執行,而後者會在函式被使用 new 運算子呼叫時執行。 applyconstruct陷阱函式對應著這兩個內部方法,並允許你對其進行重寫。apply 陷阱函式會接收到下列三個引數( Reflect.apply() 也會接收這些引數) :

  1. trapTarget :被執行的函式(即代理的目標物件) ;
  2. thisArg :呼叫過程中函式內部的 this 值;
  3. argumentsList :被傳遞給函式的引數陣列。

當使用 new 去執行函式時, construct 陷阱函式會被呼叫並接收到下列兩個引數:

  1. trapTarget :被執行的函式(即代理的目標物件) ;
  2. argumentsList :被傳遞給函式的引數陣列。

Reflect.construct()方法同樣會接收到這兩個引數,還會收到可選的第三引數 newTarget,如果提供了此引數,則它就指定了函式內部的 new.target 值。

使用apply和construct陷阱函式有這樣一些應用場景:

驗證函式的引數

假如需要保證所有引數都是某個特定型別,可使用 apply 陷阱函式進行驗證:

//apply和construct陷阱函式

let sum = function (arr=[]) {
    return arr.reduce((previous,current)=>previous+current);
}

let proxy = new Proxy(sum,{
    apply(trapTarget,thisArg,argumentList){
        argumentList[0].forEach((item)=>{
            if(typeof item != 'number'){
                throw new Error('不是數字型別');
            }
        })
        return Reflect.apply(trapTarget,thisArg,argumentList);
    },

    construct(trapTarget,argumentList){
        throw new Error('不能使用new');
    }
});


console.log(proxy([1,2,3,4])); // 10
console.log(proxy([1, "2", 3, 4]));//Uncaught Error: 不是數字型別Uncaught Error: 不是數字型別
let result = new proxy(); //Uncaught Error: 不能使用new

3. 可被撤銷的代理

在被建立之後,代理通常就不能再從目標物件上被解綁。有的情況下你可能想撤銷一個代理以便讓它不能再被使用。當你想通過公共介面向外提供一個安全的物件,並且要求要隨時都能切斷對某些功能的訪問,這種情況下可被撤銷的代理就會非常有用。
你可以使用Proxy.revocable()方法來建立一個可被撤銷的代理,該方法接受的引數與Proxy 構造器的相同:一個目標物件、一個代理處理器,而返回值是包含下列屬性的一個物件:

  1. proxy :可被撤銷的代理物件;
  2. revoke :用於撤銷代理的函式;

revoke() 函式被呼叫後,就不能再對該 proxy 物件進行更多操作。例如:

let target = {
    name: "target"
};
let { proxy, revoke } = Proxy.revocable(target, {});
console.log(proxy.name); // "target"
revoke();
// 丟擲錯誤
console.log(proxy.name);

這個例子建立了一個可被撤銷的代理,它對Proxy.revocable()方法返回的物件進行了解構
賦值,把同名屬性的值賦給了 proxyrevoke 變數。此時 proxy 物件和代理的目標物件一樣,於是 proxy.name 屬性的值就是 "target" ,因為它直接傳遞了
target.name 的值。然而一旦revoke() 函式被呼叫, 代理物件就和所代理的目標物件就解除“繫結”關係,之後試圖訪問 proxy.name 會丟擲錯誤。

4. 將代理物件作為原型使用

代理物件可以被作為原型使用,在把代理物件作為原型時,僅當操作的預設行為會按慣例追蹤原型時,代理陷阱才會被呼叫。因此,將代理物件作為原型時,常見的應用場景有:

1.在原型上使用get陷阱函式

當內部方法 [[Get]] 被呼叫以讀取屬性時,該操作首先會查詢物件的自有屬性;如果指定名稱的屬性沒有找到,則會繼續在物件的原型上進行屬性查詢;這個流程會一直持續到沒有原型可供查詢為止。得益於這個流程,若你設定了一個 get 代理陷阱,則只有在物件不存在指定名稱的自有屬性時,該陷阱函式才會在物件的原型上被呼叫。當所訪問的屬性無法保證存在時,你可以使用 get 陷阱函式來阻止預期外的行為。例如,建立了一個物件,當你嘗試去訪問一個不存在的屬性時,它會丟擲錯誤:

//原型上使用get陷阱函式

let target = {};
let newTarget  = Object.create(new Proxy(
    target,{
        get(trapTarget,key,receiver){
            throw new Error('不存在該屬性');
        }
    }
));

newTarget.name = 'hello world';
console.log(newTarget.name); //hello world

console.log(newTarget.age); //Uncaught Error: 不存在該屬性

由於 name 屬性存在,所以不會呼叫 get 陷阱函式,而 age 屬性在物件上並不存在,所以,會從原型上去找該屬性,因此,會觸發 get 陷阱函式,從而丟擲錯誤。

2.在原型上使用 set 陷阱函式

內部方法 [[Set]] 同樣會查詢物件的自有屬性,並在必要時繼續對該物件的原型進行查詢。當你對一個物件屬性進行賦值時,如果指定名稱的自有屬性存在,值就會被賦在該屬性上;而若該自有屬性不存在,則會繼續檢查物件的原型,但預設情況下它會在物件例項(而非原型) 上建立一個新的屬性用於賦值,無論同名屬性是否存在於原型上。

3.在原型上使用has陷阱函式

has 陷阱函式會攔截物件上 in 運算子的使用。 in 運算子首先查詢物件上指定名稱的自有屬性;如果不存在同名自有屬性,則會繼續查詢物件的原型;如果原型上也不存在同名自有屬性,那麼就會沿著原型鏈一直查詢下去,直到找到該屬性、或者沒有更
多原型可供查詢時為止。has 陷阱函式只在原型鏈查詢觸及原型物件的時候才會被呼叫。

當使用代理作為原型時,這隻會在指定名稱的自有屬性不存在時,才會觸發 has 陷阱函式。

5.將代理作為類的原型

代理物件不能直接作為類的原型,因為類的 prototype 屬性是不可寫入的。但是,可以使用繼承來實現:

//代理物件作為類的原型

function NoSuchProperty() {
    // empty
} 
NoSuchProperty.prototype = new Proxy({}, {
    get(trapTarget, key, receiver) {
        throw new ReferenceError(`${key} doesn't exist`);
    }
});
class Square extends NoSuchProperty {
    constructor(length, width) {
        super();
        this.length = length;
        this.width = width;
    }
} 
let shape = new Square(2, 6);
let area1 = shape.length * shape.width;
console.log(area1); // 12
// 由於 "wdth" 不存在而丟擲了錯誤
let area2 = shape.length * shape.wdth;

Square 類繼承了 NoSuchProperty 類,因此該代理就被加入了 Square 類的原型鏈。隨後shape 物件被建立為 Square 類的一個例項,讓它擁有兩個屬性: lengthwidth 。由於 get 陷阱函式永遠不會被呼叫,因此能夠成功讀取這兩個屬性的值。只有訪問 shape 上不存在的屬性時(例如這裡的 shape.wdth 拼寫錯誤) ,才觸發了 get 陷阱函式並導致錯誤被丟擲。

6. 總結

  1. 在 ES6 之前,特定物件(例如陣列) 會顯示出一些非常規的、無法被開發者複製的行為,而代理的出現改變了這種情況。代理允許你為一些 JS 底層操作自行定義非常規行為,因此你就可以通過代理陷阱來複制 JS 內建物件的所有行為。在各種不同操作發生時(例如對於 in運算子的使用) ,這些代理陷阱會在後臺被呼叫。

  2. 反射介面也是在 ES6 中引入的,允許開發者為每個代理陷阱實現預設的行為。每個代理陷阱在 Reflect 物件(ES6 的另一個新特性) 上都有一個同名的對應方法。將代理陷阱與反射介面方法結合使用,就可以在特定條件下讓一些操作有不同的表現,有別於預設的內建行為。

  3. 可被撤銷的代理是一種特殊的代理,可以使用 revoke() 函式去有效禁用。 revoke() 函式終結了代理的所有功能,因此在它被呼叫之後,所有與代理屬性互動的意圖都會導致丟擲錯誤。

  4. 儘管直接使用代理是最有力的使用方式,但你也可以把代理用作另一個物件的原型。但只有很少的代理陷阱能在作為原型的代理上被有效使用,包括 getsethas 這幾個,這讓這方面的用例變得十分有限

相關推薦

深入理解ES6--12.代理反射介面

主要知識點:代理和反射的定義、常用的陷阱函式、可被撤銷的代理、將代理物件作為原型使用、將代理作為類的原型 1. 代理和反射 代理是什麼? 通過呼叫 new Proxy() ,你可以建立一個代理用來替代另一個物件(被稱之為目目標物件)

深入理解ES6之——代理反射(proxy)

通過呼叫new proxy()你可以建立一個代理來替代另一個物件(被稱為目標),這個代理對目標物件進行了虛擬,因此該代理與該目標物件表面上可以被當做同一個物件來對待。 建立一個簡單的代理 當你使用Proxy構造器來建立一個代理時,需要傳遞兩個引數:目標物件以及一個處理器,後者是定義了一個或多個陷阱函式的物件。

深入理解ES6--迭代器、生成器、代理反射、Promise

迭代器(Iterator)和生成器(Generator) for-of迴圈及展開運算子…都是針對迭代器的!!! 不能使用箭頭函式來建立生成器;ES6函式的簡寫方式可以(只需在函式名前加星號) 可迭代物件具有Symbol.iterator屬性,ES6

深入理解ES6之迭代器生成器

迭代器 迭代器 iterator,在 Javascript 中,迭代器是一個物件(也可稱作為迭代器物件),它提供了一個 next() 方法,用來返回迭代序列中的下一項。 next 方法的定義,next 方法是一個函式,執行後返回一個物件包含兩個屬性:{ done: [boolean]

ES6】改變 JS 內置行為的代理反射

instance 什麽 del handle efault list sta clas 接受 代理(Proxy)可以攔截並改變 JS 引擎的底層操作,如數據讀取、屬性定義、函數構造等一系列操作。ES6 通過對這些底層內置對象的代理陷阱和反射函數,讓開發者能進一步接近 JS

深入理解ES6》之解構

nbsp span 對象 code true 上下文 div 嵌套對象 賦值 結構是一種打破數據解構,將其拆分為更小部分的過程。 對象解構   對象字面量的語法形式是在一個賦值操作符左邊放置一個對象字面量。 let node={ type:"indefi

深入理解ES6箭頭函數中的this

執行上下文 很多 pre 深入 上下 ber out pri 而不是 簡要介紹:箭頭函數中的this,指向與一般function定義的函數不同,箭頭函數this的定義:箭頭函數中的this是在定義函數的時候綁定,而不是在執行函數的時候綁定。 (1)一般函數this指向在執行

深入理解JDK動態代理

緩存 loader getprop manager 動態 java declared flush ont 1 package jdkproxy; 2 3 public interface Moveable { 4 void move(int i); 5 }

深入理解ES6 - var-let-const

sting fun ret 全局 key 聲明 mas 剛才 syn 知識點 var 聲明變量: 1、存在變量提升,實際上var無論在哪裏聲明,都會被當做當前的作用域頂部聲明變量。 2、可以重復聲明,後聲明的變量會覆蓋前聲明的變量。 let 聲明變量: 1、不存在變量提升。

深入理解NGINX 模組開發架構解析》之摘抄學習

1.基於Nginx框架開發程式有5個優勢:     (1).Nginx將網路、磁碟及定時器等非同步事件的驅動都做了非常好的封裝,基於它開發將可以忽略這些事件處理的細節;     (2).Nginx封裝了許多平臺無關的介面、容器,適用於跨平臺開發。

深入理解ES6》學習筆記二

5. 解構:使資料訪問更便捷 為何使用解構功能 簡化區域性變數的賦值 為物件和陣列都添加了解構功能 使用var、let或const解構宣告變數, 需提供初始化程式 不能指定為null或undefined

深入理解ES6》學習筆記一 (前四章)

1. 塊級作用域繫結 使用var定義變數時,Javascript引擎預編譯時會 將變數宣告移至程式頂部,具體的賦值操作在原宣告處 作用域外呼叫時值為 undefined 迴圈輸出時只輸出引用(可能全部相同) let定義變數

深入理解ES6 class

原文es6 class class基本宣告 在說class之前,想必大家肯定會想到constructor function. 看下面程式碼: function Foo(name) { this.name = name } class Bar { constructor

深入理解JavaScript原型鏈繼承

原型鏈 原型鏈一直都是一個在JS中比較讓人費解的知識點,但是在面試中經常會被問到,這裡我來做一個總結吧,首先引入一個關係圖: 一.要理解原型鏈,首先可以從上圖開始入手,圖中有三個概念: 1.建構函式: JS中所有函式都可以作為建構函式,前提是被new操作符操作; function P

深入理解JDK動態代理機制

本文是基於jdk1.8來對動態代理的底層機制進行探究的 Java中代理的實現一般分為三種:JDK靜態代理、JDK動態代理以及CGLIB動態代理。在Spring的AOP實現中,主要應用了JDK動態代理以及CGLIB動態代理。但是本文著重介紹JDK動態代理機制,CGLIB動態代理後面會接著探究。

深入理解CGLIB動態代理機制

本文是基於CGLIB 3.1進行探究的 cglib is a powerful, high performance and quality Code Generation Library, It is used to extend JAVA classes and impl

[Proxy] 深入理解jdk動態代理

[Proxy] 深入理解jdk動態代理 分析 jdk的proxy主要有三個類,Proxy,Proxy.ProxyClassFactory和ProxyGenerator。Proxy是一個面向使用者的Client,主要是管理proxy class cache,jdk的proxy必須是

深入理解ES6》——物件解構和陣列解構

為何使用解構功能     在ES5及早期版本中,開發者們為了從物件和陣列中獲取特定資料並賦值給變數,編寫了許多看起來同質化的程式碼,如下: let options = { repeat:true, save:false }; //從物件中取資料 let r

深入理解ES6》——Promise非同步程式設計

Promise的生命週期     每個promise都會經歷一個短暫的生命週期:先是處於進行中(pending)的狀態,此時操作尚未完成,所以它也是未處理(unsettled)的;一旦非同步操作執行結束,Promise則變為已處理(settled)的狀態。已處理的狀態又分為

深入理解ES6(至第三章-函式)

第一章 塊級作用域繫結 var 宣告初始化變數, 宣告可以提升,但初始化不可以提升。 一、塊級宣告: let const 預設使用,在某種程度上實現程式碼不可變,減少錯誤發生的機率 如果常量是物件,則物件中的值可以修改 不能重複宣告,宣告不會提升 二