#ECMASCRIPT6筆記
ECMASCRIPT6筆記
來源於http://es6.ruanyifeng.com/#docs/proxy
是我在閱讀時做下的筆記,方便以後查閱
Symbol
ES5 的物件屬性名都是字串,這容易造成屬性名的衝突。比如,你使用了一個他人提供的物件,但又想為這個物件新增新的方法(mixin 模式),新方法的名字就有可能與現有方法產生衝突。如果有一種機制,保證每個屬性的名字都是獨一無二的就好了,這樣就從根本上防止屬性名的衝突。這就是 ES6 引入Symbol的原因。
ES6 引入了一種新的原始資料型別Symbol
Symbol 值透過Symbol函式生成。這就是說,物件的屬性名現在可以有兩種型別,一種是原來就有的字串,另一種就是新增的 Symbol 型別。凡是屬性名屬於 Symbol 型別,就都是獨一無二的,可以保證不會與其他屬性名產生衝突。
由於每一個 Symbol 值都是不相等的,這意味著 Symbol 值可以作為識別符號,用於物件的屬性名,就能保證不會出現同名的屬性。這對於一個物件由多個模組構成的情況非常有用,能防止某一個鍵被不小心改寫或覆蓋。
var mySymbol = Symbol();
// 第一種寫法
var a = {};
a[mySymbol] = 'Hello!';
// 第二種寫法
var a = {
[mySymbol]: 'Hello!'//用[]圍起來定義,否則會被認為是字串
};
// 第三種寫法
var a = {};
Object.defineProperty(a, mySymbol, { value: 'Hello!' });
// 以上寫法都得到同樣結果
a[mySymbol] // "Hello!"
注意,Symbol 值作為物件屬性名時,不能用點運算子。
var mySymbol = Symbol();
var a = {};
a.mySymbol = 'Hello!';
a[mySymbol] // undefined
a['mySymbol'] // "Hello!"
Symbol 作為屬性名,該屬性不會出現在for...in、for...of迴圈中,也不會被Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()返回。但是,它也不是私有屬性,有一個Object.getOwnPropertySymbols方法,可以獲取指定物件的所有 Symbol 屬性名。
Symbol.for()
有時,我們希望重新使用同一個Symbol值,Symbol.for方法可以做到這一點。它接受一個字串作為引數,然後搜尋有沒有以該引數作為名稱的Symbol值。如果有,就返回這個Symbol值,否則就新建並返回一個以該字串為名稱的Symbol值。
var s1 = Symbol.for('foo');
var s2 = Symbol.for('foo');
s1 === s2 // true
Symbol.for("bar") === Symbol.for("bar")
// true
Symbol("bar") === Symbol("bar")
// false
上面程式碼中,由於Symbol()寫法沒有登記機制,所以每次呼叫都會返回一個不同的值。
需要注意的是,Symbol.for為Symbol值登記的名字,是全域性環境的,可以在不同的 iframe 或 service worker 中取到同一個值。
iframe = document.createElement('iframe');
iframe.src = String(window.location);
document.body.appendChild(iframe);
iframe.contentWindow.Symbol.for('foo') === Symbol.for('foo')
// true
上面程式碼中,iframe 視窗生成的 Symbol 值,可以在主頁面得到。
Symbol.keyFor
Symbol.keyFor方法返回一個已登記的 Symbol 型別值的key。
var s1 = Symbol.for("foo");
Symbol.keyFor(s1) // "foo"
var s2 = Symbol("foo");
Symbol.keyFor(s2) // undefined
上面程式碼中,變數s2屬於未登記的Symbol值,所以返回undefined。
Set
新的資料結構 Set,它類似於陣列,但是成員的值都是唯一的,沒有重複的值。
const s = new Set();
[2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x));
for (let i of s) {
console.log(i);
}
// 2 3 5 4 可以看到沒有重複值
Set 結構的例項有以下屬性。
Set.prototype.constructor:建構函式,預設就是Set函式。
Set.prototype.size:返回Set例項的成員總數。
Set 例項的方法分為兩大類:操作方法(用於運算元據)和遍歷方法(用於遍歷成員)。下面先介紹四個操作方法。
add(value):新增某個值,返回Set結構本身。
delete(value):刪除某個值,返回一個布林值,表示刪除是否成功。
has(value):返回一個布林值,表示該值是否為Set的成員。
clear():清除所有成員,沒有返回值。
s.add(1).add(2).add(2);
// 注意2被加入了兩次
s.size // 2
s.has(1) // true
s.has(2) // true
s.has(3) // false
s.delete(2);
s.has(2) // false
Set 結構的例項有四個遍歷方法,可以用於遍歷成員。
keys():返回鍵名的遍歷器 values():返回鍵值的遍歷器 entries():返回鍵值對的遍歷器 forEach():使用回撥函式遍歷每個成員
let set = new Set(['red', 'green', 'blue']);
for (let item of set.keys()) {
console.log(item);
}
// red
// green
// blue
for (let item of set.values()) {
console.log(item);
}
// red
// green
// blue
for (let item of set.entries()) {
console.log(item);
}
// ["red", "red"]
// ["green", "green"]
// ["blue", "blue"]
Set 結構的例項預設可遍歷,它的預設遍歷器生成函式就是它的values方法。
Set.prototype[Symbol.iterator] === Set.prototype.values
// true
這意味著,可以省略values方法,直接用for...of迴圈遍歷 Set。
let set = new Set(['red', 'green', 'blue']);
for (let x of set) {
console.log(x);
}
// red
// green
// blue
for...of遍歷陣列之類的結構
for(var a of array)
{
console.log(a);
}
forEach()
let set = new Set([1, 2, 3]);
set.forEach((value, key) => console.log(value * 2) )
// 2
// 4
// 6
上面程式碼說明,forEach方法的引數就是一個處理函式。該函式的引數依次為鍵值、鍵名、集合本身(上例省略了該引數)。另外,forEach方法還可以有第二個引數,表示繫結的this物件。
WeakSet
WeakSet 結構與 Set 類似,也是不重複的值的集合。但是,它與 Set 有兩個區別。
首先,WeakSet 的成員只能是物件,而不能是其他型別的值。
const ws = new WeakSet();
ws.add(1)
// TypeError: Invalid value used in weak set
ws.add(Symbol())
// TypeError: invalid value used in weak set
其次,WeakSet 中的物件都是弱引用,即垃圾回收機制不考慮 WeakSet 對該物件的引用,也就是說,如果其他物件都不再引用該物件,那麼垃圾回收機制會自動回收該物件所佔用的記憶體,不考慮該物件還存在於 WeakSet 之中。
WeakSet 是一個建構函式,可以使用new命令,建立 WeakSet 資料結構。
const ws = new WeakSet();
作為建構函式,WeakSet 可以接受一個陣列或類似陣列的物件作為引數。(實際上,任何具有 Iterable 介面的物件,都可以作為 WeakSet 的引數。)該陣列的所有成員,都會自動成為 WeakSet 例項物件的成員。
const a = [[1, 2], [3, 4]];
const ws = new WeakSet(a);
// WeakSet {[1, 2], [3, 4]}
注意,是a陣列的成員成為 WeakSet 的成員,而不是a陣列本身。這意味著,陣列的成員只能是物件。
Proxy
Proxy 用於修改某些操作的預設行為,等同於在語言層面做出修改
Proxy 可以理解成,在目標物件之前架設一層“攔截”,外界對該物件的訪問,都必須先透過這層攔截,因此提供了一種機制,可以對外界的訪問進行過濾和改寫。
var obj = new Proxy({}, {
get: function (target, key, receiver) {
console.log(`getting ${key}!`);
return Reflect.get(target, key, receiver);
},
set: function (target, key, value, receiver) {
console.log(`setting ${key}!`);
return Reflect.set(target, key, value, receiver);
}
});
//set和get是自有的屬性
對一個空物件架設了一層攔截,重定義了屬性的讀取(get)和設定(set)行為。
obj.count = 1
// setting count!
++obj.count
// getting count!
// setting count!
// 2
上面程式碼說明,Proxy 實際上過載(overload)了點運算子,即用自己的定義覆蓋了語言的原始定義。
var proxy = new Proxy(target, handler);
target參數列示所要攔截的目標物件,handler引數也是一個物件,用來定製攔截行為。
var proxy = new Proxy({}, {
get: function(target, property) {
return 35;
}
});
proxy.time // 35
proxy.name // 35
proxy.title // 35
比如,上面程式碼中,配置物件有一個get方法,用來攔截對目標物件屬性的訪問請求。get方法的兩個引數分別是目標物件和所要訪問的屬性。可以看到,由於攔截函式總是返回35,所以訪問任何屬性都得到35。
注意,要使得Proxy起作用,必須針對Proxy例項(上例是proxy物件)進行操作,而不是針對目標物件(上例是空物件)進行操作。
var target = {};
var handler = {};
var proxy = new Proxy(target, handler);
proxy.a = 'b';
target.a // "b"
如果handler沒有設定任何攔截,那就等同於直接通向原物件。
將 Proxy 物件,設定到object.proxy屬性,從而可以在object物件上呼叫。
var object = { proxy: new Proxy(target, handler) };
Proxy 例項也可以作為其他物件的原型物件。
var proxy = new Proxy({}, {
get: function(target, property) {
return 35;
}
});
let obj = Object.create(proxy);
obj.time // 35
上面程式碼中,proxy物件是obj物件的原型,obj物件本身並沒有time屬性,所以根據原型鏈,會在proxy物件上讀取該屬性,導致被攔截。
下面是 Proxy 支援的攔截操作一覽。
對於可以設定、但沒有設定攔截的操作,則直接落在目標物件上,按照原先的方式產生結果。
(1)get(target, propKey, receiver)
攔截物件屬性的讀取,比如proxy.foo和proxy['foo']。
最後一個引數receiver是一個物件,可選,參見下面Reflect.get的部分。
(2)set(target, propKey, value, receiver)
攔截物件屬性的設定,比如proxy.foo = v或proxy['foo'] = v,返回一個布林值。
(3)has(target, propKey)
攔截propKey in proxy的操作,返回一個布林值。
(4)deleteProperty(target, propKey)
攔截delete proxy[propKey]的操作,返回一個布林值。
(5)ownKeys(target)
攔截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy),返回一個陣列。該方法返回目標物件所有自身的屬性的屬性名,而Object.keys()的返回結果僅包括目標物件自身的可遍歷屬性。
(6)getOwnPropertyDescriptor(target, propKey)
攔截Object.getOwnPropertyDescriptor(proxy, propKey),返回屬性的描述物件。
(7)defineProperty(target, propKey, propDesc)
攔截Object.defineProperty(proxy, propKey,
propDesc)、Object.defineProperties(proxy, propDescs),返回一個布林值。(8)preventExtensions(target)
攔截Object.preventExtensions(proxy),返回一個布林值。
(9)getPrototypeOf(target)
攔截Object.getPrototypeOf(proxy),返回一個物件。
(10)isExtensible(target)
攔截Object.isExtensible(proxy),返回一個布林值。
(11)setPrototypeOf(target, proto)
,返回一個布林值。
如果目標物件是函式,那麼還有兩種額外操作可以攔截。
(12)apply(target, object, args)
攔截 Proxy 例項作為函式呼叫的操作,比如proxy(...args)、proxy.call(object,
...args)、proxy.apply(...)。(13)construct(target, args)
攔截 Proxy 例項作為建構函式呼叫的操作,比如new proxy(...args)。
Reflect
Reflect物件與Proxy物件一樣,也是 ES6 為了操作物件而提供的新 API。Reflect物件的設計目的有這樣幾個。
(1) 將Object物件的一些明顯屬於語言內部的方法(比如Object.defineProperty),放到Reflect物件上。現階段,某些方法同時在Object和Reflect物件上部署,未來的新方法將只部署在Reflect物件上。也就是說,從Reflect物件上可以拿到語言內部的方法。
(2) 修改某些Object方法的返回結果,讓其變得更合理。比如,Object.defineProperty(obj, name, desc)在無法定義屬性時,會丟擲一個錯誤,而Reflect.defineProperty(obj, name, desc)則會返回false。
// 老寫法
try {
Object.defineProperty(target, property, attributes);
// success
} catch (e) {
// failure
}
// 新寫法
if (Reflect.defineProperty(target, property, attributes)) {
// success
} else {
// failure
}
(3) 讓Object操作都變成函式行為。某些Object操作是命令式,比如name in obj和delete obj[name],而Reflect.has(obj, name)和Reflect.deleteProperty(obj, name)讓它們變成了函式行為。
// 老寫法
'assign' in Object // true
// 新寫法
Reflect.has(Object, 'assign') // true
(4)Reflect物件的方法與Proxy物件的方法一一對應,只要是Proxy物件的方法,就能在Reflect物件上找到對應的方法。這就讓Proxy物件可以方便地呼叫對應的Reflect方法,完成預設行為,作為修改行為的基礎。也就是說,不管Proxy怎麼修改預設行為,你總可以在Reflect上獲取預設行為。
Reflect物件一共有13個靜態方法。
Reflect.apply(target,thisArg,args) Reflect.construct(target,args) Reflect.get(target,name,receiver) Reflect.set(target,name,value,receiver) Reflect.defineProperty(target,name,desc) Reflect.deleteProperty(target,name) Reflect.has(target,name) Reflect.ownKeys(target) Reflect.isExtensible(target) Reflect.preventExtensions(target) Reflect.getOwnPropertyDescriptor(target, name) Reflect.getPrototypeOf(target) Reflect.setPrototypeOf(target, prototype)
上面這些方法的作用,大部分與Object物件的同名方法的作用都是相同的,而且它與Proxy物件的方法是一一對應的。下面是對它們的解釋。
相關文章
- ECMAScript6 實用筆記2018-01-08筆記
- ECMAScript6簡介2017-12-18
- ECMAScript6 教程(一)2015-07-23
- ECMAScript6變數的解構賦值2017-12-19變數賦值
- 印象筆記 --- 方法分享筆記2018-11-22筆記
- 筆記2020-12-28筆記
- CUUG筆記 ORACLE索引學習筆記2014-01-07筆記Oracle索引
- 主動筆記與被動筆記2015-04-12筆記
- 淘寶記錄筆記2024-04-18筆記
- 心情筆記2020-04-04筆記
- 命令筆記2019-12-02筆記
- 筆記:Docker2019-02-26筆記Docker
- Meteor筆記2019-03-01筆記
- ES筆記2019-04-03筆記
- AbstractQueuedSynchronizer筆記2019-04-02筆記
- new筆記2019-07-14筆記
- vio筆記2019-04-21筆記
- Liunx筆記2019-04-10筆記
- Nacos 筆記2021-08-12筆記
- oracle筆記2021-08-30Oracle筆記
- html 筆記2019-02-16HTML筆記
- Cookie筆記2018-08-18Cookie筆記
- jQuery筆記2018-08-13jQuery筆記
- Restful 筆記2018-09-25REST筆記
- kafka 筆記2019-01-09Kafka筆記
- 路由筆記2019-01-07路由筆記
- webSocket筆記2019-01-21Web筆記
- 筆記12018-12-09筆記
- 筆記-FMDB2018-12-08筆記
- canvas筆記2018-07-26Canvas筆記
- 小馬筆記2018-08-06筆記
- 隨筆記2018-08-06筆記
- spark筆記2020-11-20Spark筆記
- 筆記:JVM2020-11-10筆記JVM
- Servlet筆記2020-11-17Servlet筆記
- 夢筆記2020-12-03筆記
- GIF筆記2020-11-29筆記
- shell 筆記2020-11-29筆記