摘要:理解元編程和是屬于元編程范疇的,能介入的對(duì)象底層操作進(jìn)行的過(guò)程中,并加以影響。元編程中的元的概念可以理解為程序本身。中,便是兩個(gè)可以用來(lái)進(jìn)行元編程的特性。在之后,標(biāo)準(zhǔn)引入了,從而提供比較完善的元編程能力。
導(dǎo)讀
幾年前 ES6 剛出來(lái)的時(shí)候接觸過(guò) 元編程(Metaprogramming)的概念,不過(guò)當(dāng)時(shí)還沒(méi)有深究。今天在應(yīng)用和學(xué)習(xí)中不斷接觸到這概念,比如 mobx 5 中就用到了 Proxy 重寫(xiě)了 Observable 對(duì)象,覺(jué)得有必要梳理總結(jié)一下。
本文不生產(chǎn)代碼,只當(dāng)代碼、文檔的搬運(yùn)工。所以本文并非是一篇傳統(tǒng)意義上的教程,更類似于 github awesome 這樣列表文章。
1、理解元編程Symbol、Reflect 和 Proxy 是屬于 ES6 元編程范疇的,能“介入”的對(duì)象底層操作進(jìn)行的過(guò)程中,并加以影響。元編程中的 元 的概念可以理解為 程序 本身。
”元編程能讓你擁有可以擴(kuò)展程序自身能力“。這句話還是很抽象,初學(xué)者該怎么理解呢?
我也理解了半天,想到了下面的例子:
就好比你原本是公司的部門的大主管,雖然你能力很強(qiáng),但也必須按照規(guī)章制度做事,比如早上 8 點(diǎn)必須到公司,否則你就要扣績(jī)效;而后來(lái)公司基本規(guī)定靈活了,每個(gè)部門可以自己制定打卡制度,此時(shí)身為主管的你,依據(jù)公司該基本規(guī)定,制定自己部門的考勤制度,本部門的職工可以 9 點(diǎn)來(lái)公司,還可以不打卡!(當(dāng)然還可以制定其他規(guī)定)
在這個(gè)例子中:
”整個(gè)公司“就相當(dāng)于 JS 引擎
”公司的基本規(guī)章制度“就相當(dāng)于 JS 運(yùn)行機(jī)制和語(yǔ)法,員工辦事最低要求就是遵照公司的規(guī)章制度
”在此基礎(chǔ)上,你擁有管理部門的權(quán)力,負(fù)責(zé)開(kāi)發(fā)并維護(hù)一些產(chǎn)品“,這種行為就相當(dāng)于平時(shí)普通的編程;
公司的基本規(guī)定變靈活之后,你除了擁有以前管理員工的權(quán)力之外,”還擁有更改制度(針對(duì)該部門)的能力,這樣就可以從制度層面影響員工的最低要求行為“,這里更改規(guī)章制度就相當(dāng)于 元編程 了;
這里的例子不一定準(zhǔn)確,是我個(gè)人的理解,權(quán)做參考,也可以去看看知乎上 怎么理解元編程? 的問(wèn)答。
借助這個(gè)例子理解元編程,我們能感知在沒(méi)有元編程能力的時(shí)候,就算你編程能力很厲害,但終究“孫悟空翻不出五指山”;而掌握了元編程能力之后,就差上天了,“給你一個(gè)支點(diǎn),你就能撬動(dòng)地球”,能力大大擴(kuò)增。
簡(jiǎn)言之,元編程讓你具備一定程度上改變現(xiàn)有的程序規(guī)則層面的能力?;蛘哒f(shuō),元編程可以讓你以某種形式去影響或更改程序運(yùn)行所依賴的基礎(chǔ)功能,以此獲得一些維護(hù)性、效率上的好處。
Javascript 中,eval、new Function()便是兩個(gè)可以用來(lái)進(jìn)行元編程的特性。不過(guò)因?yàn)樾阅芎涂删S護(hù)的角度上,這兩個(gè)特性還是不要用為妙。
在 ES6 之后,標(biāo)準(zhǔn)引入了 Proxy & Reflect & Symbols,從而提供比較完善的元編程能力。
2、學(xué)習(xí) ES6 元編程的資源我原本也想仔細(xì)講講 ES6 中 Symbol、Proxy 和 Reflect 的基本概念和使用的,但網(wǎng)上這方面的文章不要太多,覺(jué)得重復(fù)碼字也沒(méi)有太必要。這里著重推薦幾篇,分為教程類和手冊(cè)類,通讀完之后應(yīng)該就掌握差不多了。
元編程在 ES6 體現(xiàn)最為突出的是 Proxy 的應(yīng)用,目前我所找的文章也多偏向 Proxy。
原理教程類:
深入淺出ES6(十二):代理 Proxies:ES6 深入淺出系列,個(gè)人推薦認(rèn)真讀完該文章。本文的作者實(shí)現(xiàn)了 ES6 的 Reflect 特性,所以他對(duì) ES6 這兩個(gè)特性理解是最為深刻的,他的文章自然要深度閱讀。
ES6 Proxies in Depth:和其他教程相比,該文章篇幅稍微短一些,能較為快速得掌握概念和一些實(shí)際應(yīng)用。
Metaprogramming with proxies:來(lái)自 《Exploring ES6》書(shū)籍摘選,基礎(chǔ)入門。
Chapter 7: Meta Programming:經(jīng)典的 《You Don"t Know JS》系列文章,深入淺出,文章夠長(zhǎng),需要一些耐心。
Metaprogramming in ES6: Symbols and why they"re awesome:本篇就是基于 Symbols、Reflect、Proxy 等實(shí)現(xiàn)元編程的教程系列教程,內(nèi)容也足夠詳實(shí)。
ES6學(xué)習(xí)筆記: 代理和反射:非常詳實(shí)地整理了 Proxy 和 Reflect 相關(guān)的知識(shí)點(diǎn),只是閱讀起來(lái)略微枯燥。
應(yīng)用教程類:
ES6 Features - 10 Use Cases for Proxy:收集了 10 個(gè) proxy 的具體應(yīng)用場(chǎng)景,具體的代碼放在 jsProxy 倉(cāng)庫(kù)中
從ES6重新認(rèn)識(shí)JavaScript設(shè)計(jì)模式(五): 代理模式和Proxy:本文從設(shè)計(jì)模式上去理解 Proxy 的應(yīng)用
使用 Javascript 原生的 Proxy 優(yōu)化應(yīng)用 :文章涉及到 Proxy 的基本用法、如何使用 Proxy 創(chuàng)建代理模式,以及如何對(duì)應(yīng)用進(jìn)行優(yōu)化。
手冊(cè)類:
MDN - Proxy:MDN 上的 Proxy 官方文檔
MDN - Reflect:MDN 上的 Reflect 官方文檔
MDN - 元編程:MDN 官方文檔教程,介紹了元編程的概念,應(yīng)該算是比較抽象的,當(dāng)手冊(cè)翻翻不錯(cuò);
ECMAScript 6 入門 - Proxy:阮一峰翻譯的 《ECMAScript 6 入門》 教程
ES6 自定義JavaScript語(yǔ)言行為的 Proxy 對(duì)象:算是簡(jiǎn)明版的中文版的 API 手冊(cè)
在沒(méi)充分理解元編程之前翻手冊(cè)還是挺枯燥的,建議平時(shí)使用的時(shí)候再?gòu)倪@里補(bǔ)漏
隨著時(shí)間的推移,上面收集的文章可能會(huì)顯得陳舊,又有可能出現(xiàn)新的好文章,推薦在搜索引擎中使用 js Metaprogramming 或者 es6 proxy 進(jìn)行搜索相關(guān)文章;
3、代碼片段下面摘抄一些代碼片段,方便自己后續(xù)在應(yīng)用 JS 元編程的時(shí)候快速 "借鑒"。你們?nèi)绻灿杏X(jué)得不錯(cuò)的代碼片段,歡迎在 issue 中回復(fù),我將不定期更新到這兒。
目錄Schema 校驗(yàn)
自動(dòng)填充對(duì)象
進(jìn)制轉(zhuǎn)換
緩存代理
實(shí)現(xiàn)私有屬性
函數(shù)節(jié)流
圖片懶加載
監(jiān)聽(tīng)屬性更改
實(shí)現(xiàn)單例模式
Python 那樣截取數(shù)組
Schema 校驗(yàn) ↑示例來(lái)自 ES6 Proxies in Depth
場(chǎng)景:person 是一個(gè)普通對(duì)象,包含一個(gè) age 屬性,當(dāng)我們給它賦值的時(shí)候確保是大于零的數(shù)值,否則賦值失敗并拋出異常。
var person = { age: 27 };
思路:通過(guò)設(shè)置 set trap,其中包含了對(duì) age 字段的校驗(yàn)邏輯。
代碼:
var validator = { set (target, key, value) { if (key === "age") { if (typeof value !== "number" || Number.isNaN(value)) { throw new TypeError("Age must be a number") } if (value <= 0) { throw new TypeError("Age must be a positive number") } } return true } } var proxy = new Proxy(person, validator) proxy.age = "foo" // <- TypeError: Age must be a number proxy.age = NaN // <- TypeError: Age must be a number proxy.age = 0 // <- TypeError: Age must be a positive number proxy.age = 28 console.log(person.age) // <- 28自動(dòng)填充對(duì)象 ↑
示例來(lái)自 深入淺出ES6(十二):代理 Proxies
場(chǎng)景:創(chuàng)建一個(gè)Tree()函數(shù)來(lái)實(shí)現(xiàn)以下特性,當(dāng)我們需要時(shí),所有中間對(duì)象 branch1、branch2 和 branch3 都可以自動(dòng)創(chuàng)建。
var tree = Tree(); tree // { } tree.branch1.branch2.twig = "green"; // { branch1: { branch2: { twig: "green" } } } tree.branch1.branch3.twig = "yellow"; // { branch1: { branch2: { twig: "green" }, // branch3: { twig: "yellow" }}}
思路:Tree 返回的就是一個(gè) proxy 實(shí)例,通過(guò) get trap ,當(dāng)不存在屬性的時(shí)候自動(dòng)創(chuàng)建一個(gè)子樹(shù)。
代碼:
function Tree() { return new Proxy({}, handler); } var handler = { get: function (target, key, receiver) { if (!(key in target)) { target[key] = Tree(); // 自動(dòng)創(chuàng)建一個(gè)子樹(shù) } return Reflect.get(target, key, receiver); } };進(jìn)制轉(zhuǎn)換 ↑
示例來(lái)自 深入淺出ES6(十二):代理 Proxies
場(chǎng)景:比如將 2 進(jìn)制轉(zhuǎn)換成 16 進(jìn)制或者 8 進(jìn)制,反之也能轉(zhuǎn)換。
思路:由于大部分的功能是相同的,我們通過(guò)函數(shù)名字將變量提取出來(lái),然后通過(guò) get trap 完成進(jìn)制轉(zhuǎn)換。
代碼:
const baseConvertor = new Proxy({}, { get: function baseConvert(object, methodName) { var methodParts = methodName.match(/base(d+)toBase(d+)/); var fromBase = methodParts && methodParts[1]; var toBase = methodParts && methodParts[2]; if (!methodParts || fromBase > 36 || toBase > 36 || fromBase < 2 || toBase < 2) { throw new Error("TypeError: baseConvertor" + methodName + " is not a function"); } return function (fromString) { return parseInt(fromString, fromBase).toString(toBase); } } }); baseConvertor.base16toBase2("deadbeef") === "11011110101011011011111011101111"; baseConvertor.base2toBase16("11011110101011011011111011101111") === "deadbeef";緩存代理 ↑
示例來(lái)自 從ES6重新認(rèn)識(shí)JavaScript設(shè)計(jì)模式(五): 代理模式和Proxy
場(chǎng)景:以沒(méi)有經(jīng)過(guò)任何優(yōu)化的計(jì)算斐波那契數(shù)列的函數(shù)來(lái)假設(shè)為開(kāi)銷很大的方法,這種遞歸調(diào)用在計(jì)算 40 以上的斐波那契項(xiàng)時(shí)就能明顯的感到延遲感。希望通過(guò)緩存來(lái)改善。
const getFib = (number) => { if (number <= 2) { return 1; } else { return getFib(number - 1) + getFib(number - 2); } }
注:這只是演示緩存的寫(xiě)法,遞歸調(diào)用本身就有問(wèn)題,容易導(dǎo)致內(nèi)存泄露,在實(shí)際應(yīng)用中需要改寫(xiě)上述的 getFib 函數(shù)。
思路:因?yàn)槭呛瘮?shù)調(diào)用,所以需使用 apply trap,利用 Map 或者普通對(duì)象存儲(chǔ)每次計(jì)算的結(jié)果,在執(zhí)行運(yùn)算前先去 Map 查詢計(jì)算值是否被緩存。(相當(dāng)于以空間換時(shí)間,獲得性能提升)
代碼:
const getCacheProxy = (fn, cache = new Map()) => { return new Proxy(fn, { apply(target, context, args) { const argsString = args.join(" "); if (cache.has(argsString)) { // 如果有緩存,直接返回緩存數(shù)據(jù) console.log(`輸出${args}的緩存結(jié)果: ${cache.get(argsString)}`); return cache.get(argsString); } const result = Reflect.apply(target, undefined, args); cache.set(argsString, result); return result; } }) } const getFibProxy = getCacheProxy(getFib); getFibProxy(40); // 102334155 getFibProxy(40); // 輸出40的緩存結(jié)果: 102334155
在實(shí)際應(yīng)用中數(shù)據(jù)量越大、計(jì)算過(guò)程越復(fù)雜,優(yōu)化效果越好,否則有可能會(huì)得不償失。
實(shí)現(xiàn)私有屬性 ↑示例來(lái)自 從ES6重新認(rèn)識(shí)JavaScript設(shè)計(jì)模式(五): 代理模式和Proxy
場(chǎng)景:眾所周知,JavaScript是沒(méi)有私有屬性這一個(gè)概念的,私有屬性一般是以 _ 下劃線開(kāi)頭,請(qǐng)通過(guò) Proxy 限制以 _ 開(kāi)頭的屬性的訪問(wèn)。
const myObj = { public: "hello", _private: "secret", method: function () { console.log(this._private); } },
思路:看上去比較簡(jiǎn)單,貌似使用 get、set 這兩個(gè) trap 就可以,但實(shí)際上并不是。實(shí)際上還需要實(shí)現(xiàn) has, ownKeys , getOwnPropertyDescriptor 這些 trap,這樣就能最大限度的限制私有屬性的訪問(wèn)。
代碼:
function getPrivateProps(obj, filterFunc) { return new Proxy(obj, { get(obj, prop) { if (!filterFunc(prop)) { let value = Reflect.get(obj, prop); // 如果是方法, 將this指向修改原對(duì)象 if (typeof value === "function") { value = value.bind(obj); } return value; } }, set(obj, prop, value) { if (filterFunc(prop)) { throw new TypeError(`Can"t set property "${prop}"`); } return Reflect.set(obj, prop, value); }, has(obj, prop) { return filterFunc(prop) ? false : Reflect.has(obj, prop); }, ownKeys(obj) { return Reflect.ownKeys(obj).filter(prop => !filterFunc(prop)); }, getOwnPropertyDescriptor(obj, prop) { return filterFunc(prop) ? undefined : Reflect.getOwnPropertyDescriptor(obj, prop); } }); } function propFilter(prop) { return prop.indexOf("_") === 0; } myProxy = getPrivateProps(myObj, propFilter); console.log(JSON.stringify(myProxy)); // {"public":"hello"} console.log(myProxy._private); // undefined console.log("_private" in myProxy); // false console.log(Object.keys(myProxy)); // ["public", "method"] for (let prop in myProxy) { console.log(prop); } // public method myProxy._private = 1; // Uncaught TypeError: Can"t set property "_private"
注意:其中在 get 方法的內(nèi)部,我們有個(gè)判斷,如果訪問(wèn)的是對(duì)象方法使將 this 指向被代理對(duì)象,這是在使用 Proxy 需要十分注意的,如果不這么做方法內(nèi)部的 this 會(huì)指向 Proxy 代理。
一般來(lái)講,set trap 都會(huì)默認(rèn)觸發(fā) getOwnPropertyDescriptor 和 defineProperty函數(shù)節(jié)流 ↑
示例來(lái)自 使用 Javascript 原生的 Proxy 優(yōu)化應(yīng)用
場(chǎng)景:控制函數(shù)調(diào)用的頻率.
const handler = () => console.log("Do something..."); document.addEventListener("scroll", handler);
思路:涉及到函數(shù)的調(diào)用,所以使用 apply trap 即可。
代碼:
const createThrottleProxy = (fn, rate) => { let lastClick = Date.now() - rate; return new Proxy(fn, { apply(target, context, args) { if (Date.now() - lastClick >= rate) { fn.bind(target)(args); lastClick = Date.now(); } } }); }; const handler = () => console.log("Do something..."); const handlerProxy = createThrottleProxy(handler, 1000); document.addEventListener("scroll", handlerProxy);
同樣需要注意使用 bind 綁定上下文,不過(guò)這里的示例使用了箭頭函數(shù),不用 bind 也沒(méi)啥問(wèn)題。
圖片懶加載 ↑示例來(lái)自 使用 Javascript 原生的 Proxy 優(yōu)化應(yīng)用
場(chǎng)景:為了更好的用戶體驗(yàn),在加載圖片的時(shí)候,使用 loading 占位圖,等真正圖片加載完畢之后再顯示出來(lái)。原始的寫(xiě)法如下:
const img = new Image(); img.src = "/some/big/size/image.jpg"; document.body.appendChild(img);
思路:加載圖片的時(shí)候,會(huì)讀取 img.src 屬性,我們使用 constructor trap 控制在創(chuàng)建的時(shí)候默認(rèn)使用 loading 圖,等加載完畢再將真實(shí)地址賦給 img;
代碼:
const IMG_LOAD = "https://img.alicdn.com/tfs/TB11rDdclLoK1RjSZFuXXXn0XXa-300-300.png"; const imageProxy = (loadingImg) => { return new Proxy(Image, { construct(target, args){ const instance = Reflect.construct(target, args); instance.src = loadingImg; return instance; } }); }; const ImageProxy = imageProxy(IMG_LOAD); const createImageProxy = (realImg) =>{ const img = new ImageProxy(); const virtualImg = new Image(); virtualImg.src = realImg; virtualImg.onload = () => { hasLoaded = true; img.src = realImg; }; return img; } var img = createImageProxy("https://cdn.dribbble.com/users/329207/screenshots/5289734/bemocs_db_dribbble_03_gold_leaf.jpg"); document.body.appendChild(img);監(jiān)聽(tīng)屬性更改 ↑
示例來(lái)自 ES6 Features - 10 Use Cases for Proxy
場(chǎng)景:當(dāng)普通對(duì)象屬性更改后,觸發(fā)所綁定的 onChange 回調(diào);
思路:能更改屬性的有 set 和 deleteProperty 這兩個(gè) trap,在其中調(diào)用 onChange 方法即可
function trackChange(obj, onChange) { const handler = { set (obj, prop, value) { const oldVal = obj[prop]; Reflect.set(obj, prop, value); onChange(obj, prop, oldVal, value); }, deleteProperty (obj, prop) { const oldVal = obj[prop]; Reflect.deleteProperty(obj, prop); onChange(obj, prop, oldVal, undefined); } }; return new Proxy(obj, handler); } // 應(yīng)用在對(duì)象上 let myObj = trackChange({a: 1, b: 2}, function (obj, prop, oldVal, newVal) { console.log(`myObj.${prop} changed from ${oldVal} to ${newVal}`); }); myObj.a = 5; // myObj.a changed from 1 to 5 delete myObj.b; // myObj.b changed from 2 to undefined myObj.c = 6; // myObj.c changed from undefined to 6 // 應(yīng)用在數(shù)組上 let myArr = trackChange([1,2,3], function (obj, prop, oldVal, newVal) { let propFormat = isNaN(parseInt(prop)) ? `.${prop}` : `[${prop}]`, arraySum = myArr.reduce((a,b) => a + b); console.log(`myArr${propFormat} changed from ${oldVal} to ${newVal}`); console.log(` sum [${myArr}] = ${arraySum}`); }); myArr[0] = 4; // myArr[0] changed from 1 to 4 // sum [4,2,3] = 9 delete myArr[2]; // myArr[2] changed from 3 to undefined // sum [4,2,] = 6 myArr.length = 1; // myArr.length changed from 3 to 1 // sum [4] = 4實(shí)現(xiàn)單例模式 ↑
示例來(lái)自 ES6 Features - 10 Use Cases for Proxy
場(chǎng)景:實(shí)現(xiàn)單例設(shè)計(jì)模式;
思路:和創(chuàng)建有關(guān)的,是 construct 這個(gè) trap,每次我們返回相同的實(shí)例即可。
代碼:
// makes a singleton proxy for a constructor function function makeSingleton(func) { let instance, handler = { construct: function (target, args) { if (!instance) { instance = new func(); } return instance; } }; return new Proxy(func, handler); } // 以這個(gè)為 constructor 為例 function Test() { this.value = 0; } // 普通創(chuàng)建實(shí)例 const t1 = new Test(), t2 = new Test(); t1.value = 123; console.log("Normal:", t2.value); // 0 - 因?yàn)?t1、t2 是不同的實(shí)例 // 使用 Proxy 來(lái) trap 構(gòu)造函數(shù), 完成單例模式 const TestSingleton = makeSingleton(Test), s1 = new TestSingleton(), s2 = new TestSingleton(); s1.value = 123; console.log("Singleton:", s2.value); // 123 - 現(xiàn)在 s1、s2 是相同的實(shí)例。像 Python 那樣截取數(shù)組 ↑
示例來(lái)自 ES6 Features - 10 Use Cases for Proxy
場(chǎng)景:在 python 中,你可以使用 list[10:20:3] 來(lái)獲取 10 到 20 索性中每隔 3 個(gè)的元素組成的數(shù)組(也支持負(fù)數(shù)索引)。
思路:由于在 JS 中,數(shù)組方括號(hào)語(yǔ)法中不支持冒號(hào),只能曲線救國(guó),使用這樣 list["10:20:3"] 的形式。只需要實(shí)現(xiàn) get trap 即可。
// Python-like array slicing function pythonIndex(array) { function parse(value, defaultValue, resolveNegative) { if (value === undefined || isNaN(value)) { value = defaultValue; } else if (resolveNegative && value < 0) { value += array.length; } return value; } function slice(prop) { if (typeof prop === "string" && prop.match(/^[+-d:]+$/)) { // no ":", return a single item if (prop.indexOf(":") === -1) { let index = parse(parseInt(prop, 10), 0, true); console.log(prop, " ", array[index]); return array[index]; } // otherwise: parse the slice string let [start, end, step] = prop.split(":").map(part => parseInt(part, 10)); step = parse(step, 1, false); if (step === 0) { throw new RangeError("Step can"t be zero"); } if (step > 0) { start = parse(start, 0, true); end = parse(end, array.length, true); } else { start = parse(start, array.length - 1, true); end = parse(end, -1, true); } // slicing let result = []; for (let i = start; start <= end ? i < end : i > end; i += step) { result.push(array[i]); } console.log(prop, " ", JSON.stringify(result)); return result; } } const handler = { get (arr, prop) { return slice(prop) || Reflect.get(array, prop); } }; return new Proxy(array, handler); } // try it out let values = [0,1,2,3,4,5,6,7,8,9], pyValues = pythonIndex(values); console.log(JSON.stringify(values)); pyValues["-1"]; // 9 pyValues["0:3"]; // [0,1,2] pyValues["8:5:-1"]; // [8,7,6] pyValues["-8::-1"]; // [2,1,0] pyValues["::-1"]; // [9,8,7,6,5,4,3,2,1,0] pyValues["4::2"]; // [4,6,8] // 不影響正常的索引 pyValues[3]; // 3小結(jié)
本文總結(jié)了自己學(xué)習(xí) ES6 元編程相關(guān)知識(shí)(Symbols & Proxy & Reflect)的理解、教程文檔 和 代碼片段。
由于教程文檔和代碼片段將隨著學(xué)習(xí)的進(jìn)行將增多,所以后續(xù)還會(huì)不定期更新。如果你也有好的資源,歡迎到 issue 中回復(fù)共享。
Changelog2018.09.22 更新 圖片懶加載 代碼片段,改用 construct trap 實(shí)現(xiàn);更新原因:bugfix,原來(lái)的代碼所創(chuàng)建的 img 是 proxy 對(duì)象,執(zhí)行 document.body.appendChild(img) 將報(bào)錯(cuò)。
下面的是我的公眾號(hào)二維碼圖片,歡迎關(guān)注。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/97011.html
摘要:的出現(xiàn),使用內(nèi)建對(duì)象的繼承得以實(shí)現(xiàn)。屬性不存在拋出異常是取值操作,而就是賦值操作,可以對(duì)屬性值進(jìn)行驗(yàn)證。屬性必須為數(shù)字拋出異常接受兩個(gè)參數(shù)被讀取屬性的原對(duì)象,即代理的目標(biāo)。這個(gè)可以攔截內(nèi)部方法,通過(guò)返回?cái)?shù)組的值可以覆寫(xiě)其行為。 Proxy & Reflect extends的出現(xiàn),使用內(nèi)建對(duì)象的繼承得以實(shí)現(xiàn)。Proxy可以攔截JS引擎內(nèi)部目標(biāo)的底層對(duì)象操作,這些底層操作被攔截后會(huì)觸發(fā)響...
摘要:前言新增了兩種基本的原生數(shù)據(jù)集合和加上和現(xiàn)在共有四種,以及由兩者衍生出的弱引用集合和。其本身是生成實(shí)例數(shù)據(jù)集合的構(gòu)造函數(shù),可以接受一個(gè)數(shù)組或具有接口的數(shù)據(jù)結(jié)構(gòu)作為參數(shù)用來(lái)初始化。返回鍵值對(duì)的遍歷器對(duì)象,鍵值對(duì)為鍵名鍵值。 前言 ES6新增了兩種基本的原生數(shù)據(jù)集合:Set和Map(加上Array和Object現(xiàn)在共有四種),以及由兩者衍生出的弱引用集合:WeakSet和WeakMap。從...
摘要:凡是部署了屬性的數(shù)據(jù)結(jié)構(gòu),就稱為部署了遍歷器接口。調(diào)用這個(gè)接口,就會(huì)返回一個(gè)遍歷器對(duì)象。 ES6在2015年6月就得以批準(zhǔn),至今已兩年了。近一年多以來(lái)陸續(xù)看過(guò)很多ES6的資料,工作項(xiàng)目中也逐步的用上了很多ES6的特性(let,const,promise,Template strings,Class,箭頭函數(shù)等等),不得不說(shuō),這些特性給開(kāi)發(fā)帶來(lái)了非常多的便利。但是做決定我的ES6知識(shí)其...
摘要:方法返回一個(gè)布爾值,表示某個(gè)數(shù)組是否包含給定的值,與字符串的方法類似。不可以當(dāng)作構(gòu)造函數(shù),也就是說(shuō),不可以使用命令,否則會(huì)拋出一個(gè)錯(cuò)誤。本身是一個(gè)構(gòu)造函數(shù),用來(lái)生成數(shù)據(jù)結(jié)構(gòu)。返回一個(gè)布爾值,表示該值是否為的成員。清除所有成員,沒(méi)有返回值。 在學(xué)習(xí)es6的過(guò)程中,為了方便自己復(fù)習(xí),以及查看,對(duì)api做了一個(gè)極簡(jiǎn)用例介紹。如有錯(cuò)誤多多指正。 一 let和const 1.let (1)一個(gè)大...
摘要:返回布爾值標(biāo)簽?zāi)0蹇梢跃o跟一個(gè)函數(shù)名后邊,該函數(shù)將被調(diào)用來(lái)處理這個(gè)模板字符串。其它情況下返回值為在內(nèi)部,整數(shù)和浮點(diǎn)數(shù)使用同樣的存儲(chǔ)方法,所以和被視為同一個(gè)值。 簡(jiǎn)介 ES6目標(biāo),讓JavaScript變成一個(gè)企業(yè)級(jí)的開(kāi)發(fā)語(yǔ)言,不僅僅限制與前端頁(yè)面的腳本語(yǔ)言。 標(biāo)準(zhǔn)(Standard): 用于定義與其他事物區(qū)別的一套規(guī)則 實(shí)現(xiàn)(Implementation): 某個(gè)標(biāo)準(zhǔn)的具體實(shí)施/真實(shí)實(shí)...
閱讀 2129·2023-04-26 00:09
閱讀 3162·2021-09-26 10:12
閱讀 3525·2019-08-30 15:44
閱讀 2887·2019-08-30 13:47
閱讀 949·2019-08-23 17:56
閱讀 3259·2019-08-23 15:31
閱讀 501·2019-08-23 13:47
閱讀 2560·2019-08-23 11:56