1. 程式人生 > >ES6 代理和反射

ES6 代理和反射

此文出處

簡介

  • proxy
    proxy可以攔截目標(target)上的非內建的物件進行操作,使用trap攔截這些操作,trap是響應特定操作的方法。

  • reflection
    reflection是通過Reflect物件表示,他提供了一些方法集,為代理proxy提供預設行為。

下面是一些proxy trapReflect方法,每個proxy trap都有提供對應的Reflect方法,他們接收相同的引數

Proxy Trap Overrides the Behavior Of Default Behavior
get Reading a property value Reflect.get()
set Writing to a property Reflect.set()
has The in operator Reflect.has()
deleteProperty The delete operator Reflect.deleteProperty()
getPrototypeOf Object.getPrototypeOf() Reflect.getPrototypeOf()
setPrototypeOf Object.setPrototypeOf() Reflect.setPrototypeOf()
isExtensible Object.isExtensible() Reflect.isExtensible()
preventExtensions Object.preventExtensions() Reflect.preventExtensions()
getOwnPropertyDescriptor Object.getOwnPropertyDescriptor() Reflect.getOwnPropertyDescriptor()
defineProperty Object.defineProperty() Reflect.defineProperty
ownKeys Object.keys, Object.getOwnPropertyNames(), Object.getOwnPropertySymbols() Reflect.ownKeys()
apply Calling a function Reflect.apply()
construct Calling a function with new Reflect.construct()

這裡的每個trap都會覆蓋物件的內建行為,便於攔截和修改物件。如果你真的需要內建行為,可以使用相對應的Reflect方法。

開始的時候,ES6的規範有個enumerate trap,用於改變for..inObject.keys的列舉屬性,但是在實行的時候發現有些困難,於是在ES7中移除了。所以這裡不討論他。

建立一個簡單的代理

當你使用Proxy的建構函式去建立代理的時候,他接受兩個引數,一個是目標物件(target),另外一個是處理物件(handler)。這個handler定義了一個或者多個trap去處理代理,如果沒有定義trap,那麼就會使用預設的行為。

let target = {};

let proxy = new Proxy(target, {});

proxy.name = "proxy";
console.log(proxy.name);        // "proxy"
console.log(target.name);       // "proxy"

target.name = "target";
console.log(proxy.name);        // "target"
console.log(target.name);       // "target"

從上面這個例子可以發現,不管是proxy或者是target的屬性更改,都會影響到另外一個。其實這就是這兩個的關係: **proxy本身不儲存這個屬性,他只是把操作轉發到target。**上面的這個例子似乎沒啥意思,並沒有體現出核心trap的價值所在。

使用set trap驗證屬性

set trap是在設定屬性值時觸發。
set trap接收這幾個引數:

  1. trapTarget - 接收的屬性的物件,就是代理的目標。
  2. key - 要寫入的屬性的key(string || symbol)
  3. value - 寫入屬性的值
  4. receiver - 操作的物件,通常是代理

Reflect.setset trap相對應的方法。如果屬性被設定,那麼trap應該返回true,如果沒有被設定,那就返回falseReflect.set()會根據操作是否成功返回正確的值。

要驗證一個屬性的值,那就需要使用set trap來檢查這個值,看下面程式碼:

let target = {
    name: "target"
};

let proxy = new Proxy(target, {
    set(trapTarget, key, value, receiver) {
        console.log(`trapTarget is ${trapTarget}, key is ${key}, value is ${value}, receiver is ${receiver}`)
        // 忽視存在的屬性,以免產生影響
        if (!trapTarget.hasOwnProperty(key)) {
            if (isNaN(value)) {
                throw new TypeError("Property must be a number.");
            }
        }

        // 新增到屬性
        return Reflect.set(trapTarget, key, value, receiver);
    }
});

// 新增一個新的屬性
proxy.count = 1;
console.log(proxy.count);       // 1
console.log(target.count);      // 1

// 賦值給存在target上的屬性
proxy.name = "proxy";
console.log(proxy.name);        // "proxy"
console.log(target.name);       // "proxy"

// 新的屬性值不是數字會丟擲異常
proxy.anotherName = "proxy";

可以發現,每次設定屬性值的時候都會進行攔截判斷,所以,相對的,你在獲取的時候,可以使用get進行攔截判斷。

使用get trap驗證

js一個有趣又令人困惑的地方就是獲取一個不存在的屬性的時候,不會丟擲異常,只會返回一個undefined。不像其他的大多數語言,都會丟擲一個錯誤,可能你寫了大量的程式碼,你可能會意識到這是一個嚴重的問題,比如拼寫錯誤的這些問題,代理可以為你處理這些。

**get是在讀取物件屬性的時候用到的trap。**他接收三個引數:

  1. trapTarget - 從哪個物件讀取的屬性,就是target.
  2. key - 讀取的key
  3. receiver - 操作的物件,通常是代理(proxy)
    可以發現這個和上面的set差不多,就是少了一個設定的value引數。相對的,Reflect.get方法接受與get trap相同的三個引數,並返回屬性的預設值。
var proxy = new Proxy({}, {
        get(trapTarget, key, receiver) {
            if (!(key in receiver)) {
                throw new TypeError("Property " + key + " doesn't exist.");
            }

            return Reflect.get(trapTarget, key, receiver);
        }
    });

proxy.name = "proxy";
console.log(proxy.name);            // "proxy"

// 不存在這個屬性,丟擲錯誤
console.log(proxy.age);             // throws error

不知道你有沒有發現,我們在這裡使用receiver代替trapTarget配合in一起使用,我們將在下面介紹。

使用has trap隱藏屬性的存在

in這個操作想來大家比較熟悉的,是確定屬性是否存在物件及原型鏈上。

var target = {
    value: 42;
}

console.log("value" in target);     // true
console.log("toString" in target);  // true

代理允許你使用has這個trap去返回不同的值。
這個has trap是在使用in操作時觸發。has trap接收兩個引數:

  1. trapTarget
  2. key
    Reflect.has方法接受這些相同的引數並返回in運算子的預設響應。使用has trapReflect.has可以改變某些屬性的in行為,同時又回退到其他屬性的預設行為。例如你只想隱藏value屬性:
var target = {
    name: "target",
    value: 42
};

var proxy = new Proxy(target, {
    has(trapTarget, key) {

        if (key === "value") {
            return false;
        } else {
            return Reflect.has(trapTarget, key);
        }
    }
});


console.log("value" in proxy);      // false
console.log("name" in proxy);       // true
console.log("toString" in proxy);   // true

可以發現上例直接判斷,如果不是value key,就使用Reflect去返回其預設行為。

使用deleteProperty trap對刪除進行操作

通過屬性描述那部分我們知道,delete是通過configurable來控制的,非嚴格模式下刪除會返回false,嚴格模式下會報錯。但是我們可以使用代理deleteProperty trap去操作他這個行為。

下面我們再來看看deleteProperty這個trap。他也是接受兩個引數:

  1. trapTarget
  2. key
    Reflect.deleteProperty方法提供了deleteProperty trap相對的行為去實現。所以我們可以使用這兩個去改變delete的預設行為。
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);
        }
    }
});

// Attempt to delete proxy.value

console.log("value" in proxy);      // true

let result1 = delete proxy.value;
console.log(result1);               // false

console.log("value" in proxy);      // true

// Attempt to delete proxy.name

console.log("name" in proxy);       // true

let result2 = delete proxy.name;
console.log(result2);               // true

console.log("name" in proxy);       // false

這樣可以攔截操作,好奇的你可能會想去操作nonconfigurable的時候,也可以刪除,你可以嘗試一下。這個方法在受保護的屬性被刪除的時候,非嚴格模式下會拋錯。

原型的代理trap

這個章節裡介紹了setPrototypeOfgetPrototypeOf。代理也為這兩種情況添加了相應的trap。針對這兩個代理的trap,都有不同的引數:

  • setPrototypeOf
    1. trapTarget
    2. proto 這個用作原型的物件
      他和Reflect.setPrototypeOf接收的引數相同,去做相對應的操作。另一方面,getPrototypeOf只接收一個引數trapTarget,相應的也存在Reflect.getPrototypeOf方法.
原型代理是如何工作的

他們有一些限制。首先,getPrototypeOf只可以返回物件或者null,返回其他的,在執行的時候會報錯。同樣的,setPrototypeOf trap如果失敗,必須返回false,並且Object.setPrototypeOf會拋錯, 如果返回其他的值,那就是認為操作成功。
下面來看一個例子:

var target = {};
var proxy = new Proxy(target, {
    getPrototypeOf(trapTarget) {
        return null;
    },
    setPrototypeOf(trapTarget, proto) {
        return false;
    }
});

var targetProto = Object.getPrototypeOf(target);
var proxyProto = Object.getPrototypeOf(proxy);

console.log(targetProto === Object.prototype);      // true
console.log(proxyProto === Object.prototype);       // false
console.log(proxyProto);                            // null

// succeeds
Object.setPrototypeOf(target, {});

// throws error
Object.setPrototypeOf(proxy, {});

從上面可以發現,對於proxy進行了攔截,以至於原型不同。然後對proxy進行setPrototypeOf會丟擲異常,就是上面提到的,setPrototypeOf返回false,那麼Object.setPrototypeOf會丟擲錯誤。
當然,如果你想要使用它的預設行為,那就需要使用Reflect物件的方法來操作。

為什麼會有兩套方法

讓人感到困惑的是, setPrototypeOf trapgetPrototypeOf trap看起來和Object.getPrototypeOf() or Object.setPrototypeOf()看起來類似,為什麼還要這兩套方法。其實他們看起來是類似,但是還有很大的差異:
首先,Object.getPrototype||Object.setPrototypeOf在一開始就是為開發人員建立的高級別的操作。然而Reflect.getPrototypeOf || Reflect.setPrototypeOf是提供了操作以前僅僅用於內部的[[GetPrototypeOf]] || [[SetPrototypeOf]]的底層屬性。Reflect.getPrototypeOf方法是內部[[GetPrototypeOf]]操作的包裝器(帶有一些輸入驗證)。Reflect.setPrototypeOf方法和[[SetPrototypeOf]]具有相同的關係。Object上的相應方法也呼叫[[GetPrototypeOf]][[SetPrototypeOf]],但在呼叫之前執行幾個步驟並檢查返回值以確定如何操作。

上面說的比較泛泛,下面來詳細說下:
如果Reflect.getPrototypeOf方法的引數不是物件或者null,則丟擲錯誤;而Object.getPrototypeOf在執行操作之前首先將值強制轉換為物件。

var result1 = Object.getPrototypeOf(1);
console.log(result1 === Number.prototype);  // true

// throws an error
Reflect.getPrototypeOf(1);

很明顯,Object上的可以工作,他把數字1轉換成了物件,Reflect上的不會進行轉換,所以丟擲異常。

setPrototypeOf也有一些不同,Reflect.setPrototypeOf會返回一個布林來確定是否成功,false就是失敗。然而Object.setPrototypeOf如果失敗,會丟擲錯誤。

物件 Extensibility trap

ECMAScript 5通過Object.preventExtensionsObject.isExtensible方法添加了物件可擴充套件性的操作,因此ES6在此基礎上對這兩個方法添加了代理。並且這兩個代理方法都只接收一個引數trapTarget.isExtensible trap必須返回布林值來確定是否是可擴充套件的,preventExtensions trap返回布林值確定是否成功。
Reflect物件裡的這兩個方法都會返回布林值,所以這兩個是可以作為相對應的方法去使用實現預設行為。

兩個簡單的例子
var target = {};
var proxy = new Proxy(target, {
    isExtensible(trapTarget) {
        return Reflect.isExtensible(trapTarget);
    },
    preventExtensions(trapTarget) {
        return Reflect.preventExtensions(trapTarget);
    }
});


console.log(Object.isExtensible(target));       // true
console.log(Object.isExtensible(proxy));        // true

Object.preventExtensions(proxy);

console.log(Object.isExtensible(target));       // false
console.log(Object.isExtensible(proxy));        // false

這個例子就是使用代理攔截並返回他的預設行為,等於預設的情況。所以經過Object屬性操作之後,就是返回預設的行為。

如果我們不想他拓展,我們可以這樣去處理:

var target = {};
var 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

這裡他不會成功,因為返回了false,沒有使用對應的Reflect去做相對的預設行為處理,所以操作不會轉發到操作的目標。

Duplicate Extensibility Methods

如果傳遞物件值作為引數,方法Object.isExtensibleReflect.isExtensible類似。如果不是物件作為引數傳遞,Object.isExtensible始終返回false,而Reflect.isExtensible則丟擲錯誤。

let result1 = Object.isExtensible(2);
console.log(result1);                       // false

// throws error, Reflect.isExtensible called on non-object
let result2 = Reflect.isExtensible(2);

這個限制類似於Object.getPrototypeOfReflect.getPrototypeOf方法之間的差異,因為具有較低級別功能的方法具有比其更高級別對應方更嚴格的錯誤檢查。

Object.preventExtensionsReflect.preventExtensions方法也非常相似。 Object.preventExtensions方法始終返回作為引數傳遞給它的值,即使該值不是物件也是如此。然而另一方面,如果引數不是物件,那麼Reflect.preventExtensions方法會丟擲錯誤;如果引數是一個物件,那麼Reflect.preventExtensions在操作成功時返回true,否則返回false

var result1 = Object.preventExtensions(2);
console.log(result1);                               // 2

var target = {};
var result2 = Reflect.preventExtensions(target);
console.log(result2);                               // true

// throws error
var result3 = Reflect.preventExtensions(2);

這個例子就是對上面的總結。

Property Descriptor Traps

ECMAScript 5最重要的功能之一是使用Object.defineProperty方法定義屬性具體屬性的能力。在以前的JavaScript版本中,無法定義訪問者屬性,使屬性成為只讀,或使屬性不可數。具體參考這裡

代理允許分別使用defineProperty trapgetOwnPropertyDescriptor trap攔截對Object.definePropertyObject.getOwnPropertyDescriptor的呼叫。 defineProperty trap接收以下引數:

  1. trapTarget - 被定義屬性的物件(代理的目標)
  2. key
  3. descriptor
    defineProperty trap返回布林值。getOwnPropertyDescriptor trap只接收trapTargetkey,並且返回描述資訊。相應的Reflect.definePropertyReflect.getOwnPropertyDescriptor方法接受與其代理trap對應方相同的引數。
    例如:
var proxy = new Proxy({}, {
    defineProperty(trapTarget, key, descriptor) {
        return Reflect.defineProperty(trapTarget, key, descriptor);
    },
    getOwnPropertyDescriptor(trapTarget, key) {
        return Reflect.getOwnPropertyDescriptor(trapTarget, key);
    }
});


Object.defineProperty(proxy, "name", {
    value: "proxy"
});

console.log(proxy.name);            // "proxy"

var descriptor = Object.getOwnPropertyDescriptor(proxy, "name");

console.log(descriptor.value);      // "proxy"

很簡單的一個例子,基本沒有在攔截上做任何操作,只是返回他的預設行為。

Blocking Object.defineProperty()

trap返回true時,Object.defineProperty表示成功;
trap返回false時,Object.defineProperty會丟擲錯誤。
可以使用這個功能來限制Object.defineProperty方法可以定義的屬性型別.如下:

var proxy = new Proxy({}, {
    defineProperty(trapTarget, key, descriptor) {
        if (typeof key === "symbol") {
            return false;
        }

        return Reflect.defineProperty(trapTarget, key, descriptor);
    }
});


Object.defineProperty(proxy, "name", {
    value: "proxy"
});

console.log(proxy.name);                    // "proxy"

var nameSymbol = Symbol("name");

// throws error
Object.defineProperty(proxy, nameSymbol, {
    value: "proxy"
});

這裡我們檢測key的型別,如果是symbol就返回false.對於Object.defineProperty,返回false會丟擲異常。

當然可以通過返回true而不呼叫Reflect.defineProperty方法使Object.defineProperty預設是失敗的,這就避免錯誤的丟擲。

Descriptor Object Restrictions

為了確保在使用Object.definePropertyObject.getOwnPropertyDescriptor方法時的一致行為,傳遞給defineProperty trap的描述符物件被規範化。從getOwnPropertyDescriptor trap返回的物件總是出於同樣的原因進行驗證。

不管哪個引數作為第三個引數傳遞給Object.defineProperty方法,都只能是下面這幾種:enumerable, configurable, value, writable, get, set 這些將被作為descriptor傳遞。例如:

var proxy = new Proxy({}, {
    defineProperty(trapTarget, key, descriptor) {
        console.log(descriptor.value);              // "proxy"
        console.log(descriptor.name);               // undefined
        console.log(descriptor.writable)          // undefined
        return Reflect.defineProperty(trapTarget, key, descriptor);
    }
});


Object.defineProperty(proxy, "name", {
    value: "proxy",
    name: "custom"
});

可以發現,name不存在那幾個descriptor裡,所以傳遞不進去,不接收。並且這個和Object.defineProperty不同,沒有進行一些包裝,不存在預設的writable, configurable這些…。但是按理來說,你傳遞一個物件進行,他就應該接收啊,為啥這裡會是undefined呢?這是因為**descriptor實際上不是對傳遞給Object.defineProperty方法的第三個引數的引用,而是一個僅包含允許屬性的新物件。Reflect.defineProperty方法還會忽略描述符上的任何非標準屬性**

getOwnPropertyDescriptor稍微有些不同,他會返回null, undefined,object.如果返回的是物件,那麼物件只會包含上面可能出現的descriptor的這幾種情況。

如果返回具有不允許的屬性的物件,會導致錯誤,如下程式碼:

var proxy = new Proxy({}, {
    getOwnPropertyDescriptor(trapTarget, key) {
        return {
            name: "proxy"
        };
    }
});

// throws error
var descriptor = Object.getOwnPropertyDescriptor(proxy, "name");

因為name不屬於descriptor接受的範圍,所以引發了錯誤。這個限制可確保Object.getOwnPropertyDescriptor返回的值始終具有可靠的結構,無論代理使用什麼。

Duplicate Descriptor Methods

和上面的一些trap類似,這個也有一些讓人為之困惑的類似的方法。這裡的是Object.defineProperty&Object. getOwnPropertyDescriptorReflect. defineProperty&Reflect.getOwnPropertyDescriptor.

defineProperty() Methods

看看這個方法的異同.
Object.definePropertyReflect.defineProperty方法完全相同,只是它們的返回值有所不同。

            
           

相關推薦

ES6 代理反射

此文出處 簡介 proxy proxy可以攔截目標(target)上的非內建的物件進行操作,使用trap攔截這些操作,trap是響應特定操作的方法。 reflection reflection是通過Reflect物件表示,他提供了一些方法集,為代理proxy提

ES6躬行記(24)——代理反射

  代理和反射是ES6新增的兩個特性,兩者之間是協調合作的關係,它們的具體功能將在接下來的章節中分別講解。 一、代理   ES6引入代理(Proxy)地目的是攔截物件的內建操作,注入自定義的邏輯,改變物件的預設行為。也就是說,將某些JavaScript內部的操作暴露了出來,給予開發人員更多的許可權。這其實

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

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

java中的動態代理反射

java的動態代理是用反射實現的。 什麼是反射? java的反射機制,是說在執行時刻,對於任何一個類,都能夠知道它的所有屬性和方法;對任意一個物件,都能夠呼叫它的任意方法和屬性;這種動態獲取資訊以及動態呼叫方法的功能稱為java的反射機制。 java通過j

Java高階特性(動態代理反射

目錄第4天 java高階特性增強今天內容安排:1、掌握多執行緒2、掌握併發包下的佇列3、瞭解JMS4、掌握JVM技術5、掌握反射和動態代理Øjava多執行緒增強通俗來講:應用程式就是一個程序。不管是我們開發的應用程式,還是我們執行的其他的應用程式,都需要先把程式安裝在本地的硬

類載入器(Mapper動態代理)反射

類的載入:        當程式要使用某個類時,如果該類還未被載入到記憶體中,則系統會通過載入,連線,初始化三步來實現對這個類進行初始化。    一:載入             就是指將class檔案讀入記憶體,併為之建立一個Class物件。            任何類被

ES6代理Proxy反射Reflect

一、Proxy Proxy(代理)是 ES6 中新增的一個特性。Proxy 讓我們能夠以簡潔易懂的方式控制外部對物件的訪問。其功能非常類似於設計模式中的代理模式。 使用 Proxy 的好處是:物件只需關注於核心邏輯,一些非核心的邏輯(如:讀取或設定物件的某些屬性前記錄日誌;設定物件的

類的加載機制反射——五、使用反射生成JDK動態代理

復用 他也 new mil ont throwable logs object load 使用反射生成JDK動態代理 1.使用Proxy和InvocationHandler創建動態代理 (1)Proxy提供了用於創建動態代理類和動態代理對象的靜態方法,他也是所有動態代理類的

ES6語法知識點:代理proxy

Proxy 可以理解成,在目標物件之前架設一層“攔截”,外界對該物件的訪問,都必須先通過這層攔截,因此提供了一種機制,可以對外界的訪問進行過濾和改寫。 var obj = new Proxy({}, { get: function (target, key, recei

nginx+tomcat實現反射代理域名繫結專案

Nginx反向代理設定如下: upstream admin { server 127.0.0.1:8080;(將請求轉發到的實際容器tomcat埠) } server { listen 80; server_name  _;(這個地方設定需要反向代理的域名,如www.

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

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

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

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

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

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

JAVA的回撥函式反射機制(原理不說直接看程式碼),補充動靜態代理

程式碼都是轉載或抄錄這個地址: http://blog.csdn.net/zhandoushi1982/article/details/8567709 http://blog.csdn.net/xiaanming/article/details/8703708/ 反射機制

使用閉包代理Segue進行反向傳值

closure create 賦值 返回 protocol alt 類型 del uiview import UIKit class FirstViewController: UIViewController, SecondViewControllerDelegate

侃侃正向代理反向代理

nginx 是我 應用 技術 關系 str 它的 技術分享 服務器 正向代理 比如你現在缺錢,想找馬雲爸爸去借錢,可想而知人家可能鳥都不鳥你,到最後碰一鼻子灰借不到錢。不過你認識你家隔壁老王,而老王認識馬雲同誌,而且關系還很好。這時候你托老王去找馬雲借錢,當然這事最後成了,

es6--letconst

標簽 cau ria ble ready 復合 iss 模式 ren 參考資料: http://es6.ruanyifeng.com/#docs/let 測試環境(本文的代碼均是在chrome下運行)   在<script>標簽中添加‘use strict

es6 letconst

ons defined ria ole tar iss 聲明 htm love 一、let 1、let塊作用域 if(true){ var a=1; let b=2; } con

Nginx反向代理負載均衡部署指南

命令 eva http ddr cli 僅支持 新版 ive app nginx不單能夠作為強大的webserver,也能夠作為一個反向代理server,並且nginx還能夠依照調度規則實現動態、靜態頁面的分離。能夠依照輪詢、ip哈希、URL哈希、權重等多種

es6 map()filter()詳解【轉】

低版本 window get 簡約 push foreach 沒有 數值 length 原文地址:http://www.zhangxinxu.com/wordpress/2013/04/es5%e6%96%b0%e5%a2%9e%e6%95%b0%e7%bb%84%e