es5
的Object.defineProperty
是關鍵,一個已經定義好的對象,咱們能夠利用Object.defineProperty
方法重寫它的屬性,添加get
,set
方法。數組
var obj = { name:"小明", age:18, say(){ return "hello"; } } for(let key in obj){ let value = obj[key]; //利用閉包,存值 Object.defineProperty(obj,key,{ get(){ return value; }, set(val){ console.log('改變了'); value = val; }, }) }
get
,set
回調裏能夠塞入咱們想要的操做。固然,上述代碼若是要嚴謹點的話,還應當區分是否監聽原型鏈上的屬性。能夠用hasOwnProperty
方法或者Object.keys()
方法排除原型鏈上的屬性。閉包
由於種種歷史緣由,數組類型是無法用上面的方法進行監聽的,並且由於種種歷史緣由和實現方式的緣故,數組裏的元素改變是無法直接監聽到的,(好比 arr[3] = 9
),因此只能退而求其次監聽數組的方法。
實現思路很簡單,在Array
和須要監聽的數組之間多構造一個數組對象,將監聽數組的原型__proto__
指向構造的數組對象便可,構造的數組對象天然能夠覆寫數組方法,並添加本身的操做。app
var overRideArr = []; //構造的數組對象 overRideArr['push'] = function(){ console.log("監聽操做"); return [].push.apply(this,arguments) } var arr = ['a','b','c']; // 待監聽的數組 arr.__proto__ = overRideArr; //改變原型指向
爲何要使用__proto__
而不是建立一個繼承Array
的類,並在其內部改寫對應方法呢?
由於除了 new Array()
,別的方式沒法返回數組類型,即便構造了一個類,new 出來的也是會是個對象。因此經過改變實例的原型指向,是目前監聽數組最實用的方式。
哈?你說別的方式?固然就是直接覆寫某個實例數組了,好比這樣:ide
var arr = ['a','b','c']; // 待監聽的數組 arr['push'] = function(){ console.log("監聽操做"); return [].push.apply(this,arguments) }
這種方法只有不支持__proto__
屬性的時候纔會用,遠沒有使用__proto__
方便。this
另外若是你真的很喜歡new的方式,其實能夠包裝一下,弄個形式,可是內裏仍是上面的代碼。es5
function OverArr(arr){ arr.__proto__ = overRideArr; return arr; } var arr2 = ['a','b','c']; // 待監聽的數組 arr2 = new OverArr(arr2); //arr2 = OverArr(arr2); 這種寫法也同樣的