ES6中defineProperty與proxy的詳細介紹(程式碼範例)
本篇文章帶給大家的內容是關於ES6中defineProperty與proxy的詳細介紹(程式碼範例),有一定的參考價值,有需要的朋友可以參考一下,希望對你有幫助。
我們或多或少都聽過「資料綁定」這個詞,「資料綁定」的關鍵在於監聽資料的變化,可是對於這樣一個物件:var obj = {value: 1}
,我們該怎麼知道obj 發生了改變呢?
definePropety
ES5 提供了 Object.defineProperty 方法,該方法可以在一個物件上定義一個新屬性,或修改一個物件的現有屬性,並傳回這個物件。
語法
Object.defineProperty(obj, prop, descriptor)
參數
obj: 要在其上定义属性的对象。 prop: 要定义或修改的属性的名称。 descriptor: 将被定义或修改的属性的描述符。
舉例:
var obj = {}; Object.defineProperty(obj, "num", { value : 1, writable : true, enumerable : true, configurable : true }); // 对象 obj 拥有属性 num,值为 1
雖然我們可以直接加入屬性和值,但是使用這種方式,我們可以進行更多的配置。
函數的第三個參數 descriptor 所表示的屬性描述符有兩種形式:資料描述子和存取描述子。
兩者皆有下列兩種鍵值:
configurable
當且僅當該屬性的 configurable 為true 時,此屬性描述子才能夠被改變,也能夠被刪除。預設為 false。
enumerable
當且僅當此屬性的 enumerable 為 true 時,此屬性才能夠出現在物件的列舉屬性中。預設為 false。
資料描述子同時具有以下可選鍵值:
#value
此屬性對應的值。可以是任何有效的 JavaScript 值(數值,對象,函數等)。預設為 undefined。
writable
當且僅當該屬性的 writable 為 true 時,此屬性才能被賦值運算子改變。預設為 false。
訪問描述子同時具有以下可選鍵值:
#get
一個給屬性提供getter 的方法,如果沒有 getter 則為 undefined。此方法傳回值被用作屬性值。預設為 undefined。
set
一個提供屬性 setter 的方法,如果沒有 setter 則為 undefined。此方法將接受唯一參數,並將該參數的新值指派給該屬性。預設為 undefined。
值得注意的是:
屬性描述符必須是資料描述符或存取描述符兩種形式之一,不能同時是兩者。 這意味著你可以:
Object.defineProperty({}, "num", { value: 1, writable: true, enumerable: true, configurable: true });
也可以:
var value = 1; Object.defineProperty({}, "num", { get : function(){ return value; }, set : function(newValue){ value = newValue; }, enumerable : true, configurable : true });
但是不可以:
// 报错 Object.defineProperty({}, "num", { value: 1, get: function() { return 1; } });
此外,所有的屬性描述子都是非必須的,但是descriptor 這個欄位是必須的,如果不進行任何配置,你可以這樣:
var obj = Object.defineProperty({}, "num", {}); console.log(obj.num); // undefined
Setters 和Getters
之所以講到defineProperty,是因為我們要使用存取描述符中的get 和set,這兩個方法又被稱為getter 和setter。由 getter 和 setter 定義的屬性稱做」存取器屬性「。
當程式查詢存取器屬性的值時,JavaScript 呼叫 getter方法。這個方法的回傳值就是屬性存取表達式的值。當程式設定一個存取器屬性的值時,JavaScript 呼叫 setter 方法,將賦值表達式右邊的值當作參數傳入 setter。從某種意義上講,這個方法負責「設定」屬性值。可以忽略 setter 方法的回傳值。
舉個例子:
var obj = {}, value = null; Object.defineProperty(obj, "num", { get: function(){ console.log('执行了 get 操作') return value; }, set: function(newValue) { console.log('执行了 set 操作') value = newValue; } }) obj.value = 1 // 执行了 set 操作 console.log(obj.value); // 执行了 get 操作 // 1
這不就是我們要的監控資料改變的方法嗎?我們再來封裝一下:
function Archiver() { var value = null; // archive n. 档案 var archive = []; Object.defineProperty(this, 'num', { get: function() { console.log('执行了 get 操作') return value; }, set: function(value) { console.log('执行了 set 操作') value = value; archive.push({ val: value }); } }); this.getArchive = function() { return archive; }; } var arc = new Archiver(); arc.num; // 执行了 get 操作 arc.num = 11; // 执行了 set 操作 arc.num = 13; // 执行了 set 操作 console.log(arc.getArchive()); // [{ val: 11 }, { val: 13 }]
watch API
既然可以監控資料的改變,那我可以這樣設想,也就是當資料改變的時候,自動進行渲染工作。舉個例子:
HTML 中有一個 span 標籤和 button 標籤
<span id="container">1</span> <button id="button">点击加 1</button>
當點擊按鈕的時候,span 標籤裡的值加 1。
傳統的做法是:
document.getElementById('button').addEventListener("click", function(){ var container = document.getElementById("container"); container.innerHTML = Number(container.innerHTML) + 1; });
如果使用了defineProperty:
var obj = { value: 1 } // 储存 obj.value 的值 var value = 1; Object.defineProperty(obj, "value", { get: function() { return value; }, set: function(newValue) { value = newValue; document.getElementById('container').innerHTML = newValue; } }); document.getElementById('button').addEventListener("click", function() { obj.value += 1; });
程式碼看似增多了,但是當我們需要改變span 標籤裡的值的時候,直接修改obj.value 的值就可以了。
然而,現在的寫法,我們還需要單獨宣告一個變數儲存obj.value 的值,因為如果你在set 中直接 obj.value = newValue
就會陷入無限的循環中。另外,我們可能需要監控很多屬性值的改變,要是一個一個寫,也很累吶,所以我們簡單寫個 watch 函數。使用效果如下:
var obj = { value: 1 } watch(obj, "num", function(newvalue){ document.getElementById('container').innerHTML = newvalue; }) document.getElementById('button').addEventListener("click", function(){ obj.value += 1 });
我們來寫下這個watch 函數:
(function(){ var root = this; function watch(obj, name, func){ var value = obj[name]; Object.defineProperty(obj, name, { get: function() { return value; }, set: function(newValue) { value = newValue; func(value) } }); if (value) obj[name] = value } this.watch = watch; })()
現在我們已經可以監控物件屬性值的改變,並且可以根據屬性值的改變,添加回呼函數,棒棒噠~
proxy
使用defineProperty 只能重定義屬性的讀取(get)和設定(set)行為,到了ES6,提供了Proxy,可以重定義更多的行為,例如in、delete、函數呼叫等更多行為。
Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,ES6 原生提供 Proxy 构造函数,用来生成 Proxy 实例。我们来看看它的语法:
var proxy = new Proxy(target, handler);
proxy 对象的所有用法,都是上面这种形式,不同的只是handler参数的写法。其中,new Proxy()表示生成一个Proxy实例,target参数表示所要拦截的目标对象,handler参数也是一个对象,用来定制拦截行为。
var proxy = new Proxy({}, { get: function(obj, prop) { console.log('设置 get 操作') return obj[prop]; }, set: function(obj, prop, value) { console.log('设置 set 操作') obj[prop] = value; } }); proxy.time = 35; // 设置 set 操作 console.log(proxy.time); // 设置 get 操作 // 35
除了 get 和 set 之外,proxy 可以拦截多达 13 种操作,比如 has(target, propKey),可以拦截 propKey in proxy 的操作,返回一个布尔值。
// 使用 has 方法隐藏某些属性,不被 in 运算符发现 var handler = { has (target, key) { if (key[0] === '_') { return false; } return key in target; } }; var target = { _prop: 'foo', prop: 'foo' }; var proxy = new Proxy(target, handler); console.log('_prop' in proxy); // false
又比如说 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"
又比如说 ownKeys 方法可以拦截对象自身属性的读取操作。具体来说,拦截以下操作:
Object.getOwnPropertyNames()
Object.getOwnPropertySymbols()
Object.keys()
下面的例子是拦截第一个字符为下划线的属性名,不让它被 for of 遍历到。
let target = { _bar: 'foo', _prop: 'bar', prop: 'baz' }; let handler = { ownKeys (target) { return Reflect.ownKeys(target).filter(key => key[0] !== '_'); } }; let proxy = new Proxy(target, handler); for (let key of Object.keys(proxy)) { console.log(target[key]); } // "baz"
更多的拦截行为可以查看阮一峰老师的 《ECMAScript 6 入门》
值得注意的是,proxy 的最大问题在于浏览器支持度不够,而且很多效果无法使用 poilyfill 来弥补。
watch API 优化
我们使用 proxy 再来写一下 watch 函数。使用效果如下:
(function() { var root = this; function watch(target, func) { var proxy = new Proxy(target, { get: function(target, prop) { return target[prop]; }, set: function(target, prop, value) { target[prop] = value; func(prop, value); } }); if(target[name]) proxy[name] = value; return proxy; } this.watch = watch; })() var obj = { value: 1 } var newObj = watch(obj, function(key, newvalue) { if (key == 'value') document.getElementById('container').innerHTML = newvalue; }) document.getElementById('button').addEventListener("click", function() { newObj.value += 1 });
我们也可以发现,使用 defineProperty 和 proxy 的区别,当使用 defineProperty,我们修改原来的 obj 对象就可以触发拦截,而使用 proxy,就必须修改代理对象,即 Proxy 的实例才可以触发拦截。
以上是ES6中defineProperty與proxy的詳細介紹(程式碼範例)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

Go語言提供了兩種動態函數創建技術:closures和反射。 closures允許存取閉包作用域內的變量,而反射可使用FuncOf函數建立新函數。這些技術在自訂HTTP路由器、實現高度可自訂的系統和建置可插拔的元件方面非常有用。

在C++函數命名中,考慮參數順序至關重要,可提高可讀性、減少錯誤並促進重構。常見的參數順序約定包括:動作-物件、物件-動作、語意意義和遵循標準函式庫。最佳順序取決於函數目的、參數類型、潛在混淆和語言慣例。

1. SUM函數,用於對一列或一組單元格中的數字進行求和,例如:=SUM(A1:J10)。 2、AVERAGE函數,用於計算一列或一組儲存格中的數字的平均值,例如:=AVERAGE(A1:A10)。 3.COUNT函數,用於計算一列或一組單元格中的數字或文字的數量,例如:=COUNT(A1:A10)4、IF函數,用於根據指定的條件進行邏輯判斷,並返回相應的結果。

C++函數中預設參數的優點包括簡化呼叫、增強可讀性、避免錯誤。缺點是限制靈活性、命名限制。可變參數的優點包括無限彈性、動態綁定。缺點包括複雜性更高、隱式型別轉換、除錯困難。

C++中的函數傳回參考類型的好處包括:效能提升:引用傳遞避免了物件複製,從而節省了記憶體和時間。直接修改:呼叫方可以直接修改傳回的參考對象,而無需重新賦值。程式碼簡潔:引用傳遞簡化了程式碼,無需額外的賦值操作。

自訂PHP函數與預定義函數的差異在於:作用域:自訂函數僅限於其定義範圍,而預定義函數可在整個腳本中存取。定義方式:自訂函數使用function關鍵字定義,而預先定義函數則由PHP核心定義。參數傳遞:自訂函數接收參數,而預先定義函數可能不需要參數。擴充性:自訂函數可以根據需要創建,而預定義函數是內建的且無法修改。

C++中的異常處理可透過自訂異常類別增強,提供特定錯誤訊息、上下文資訊以及根據錯誤類型執行自訂操作。定義繼承自std::exception的異常類,提供特定的錯誤訊息。使用throw關鍵字拋出自訂異常。在try-catch區塊中使用dynamic_cast將捕獲到的異常轉換為自訂異常類型。在實戰案例中,open_file函數會拋出FileNotFoundException異常,捕捉並處理該異常可提供更具體的錯誤訊息。
