監聽陣列變化,實現響應

2022-08-01 03:54:14 字數 3338 閱讀 3295

舉個例子,來說明下為什麼監聽不到陣列變化

var target =

let _value =target.val

object.defineproperty(target,"val",,

set:

function

(newval)

})console.log(target.val) //1

console.log(target.val = ) //

setted

console.log(target.val = [1,2,3]) //

setted [1,2,3]

console.log(target.val[1]=10) //

10console.log(target.val.push(8)) //

4console.log(target.val.length=5) //

5console.log(target.val) //

[1, 10, 3, 8, empty]

從本例中可以看到,當taget.val被設定為陣列後,想要對陣列內部進行修改,通過陣列索引去賦值target.val[1]=10,不會觸發set方法執行。那麼該如何實現呢?

我們先來了解下 array.prototype.push.call() 相關知識,便於監聽陣列,實現響應做鋪墊。

var a = [1,2,3];

var b = [4,5,6];

console.log(a)

//[1,2,3,4,5,6]

注:合併陣列為什麼不直接使用array.prototype.concat()呢?

列印結果看來,陣列的隱式原型上掛載了一些方法,如push()、pop()、shift()、unshift()、splice()、sort()、reverse()等。

我們重新改寫下方法

let arr = [1,2,3]

arr.__proto__ =

}arr.push(6)

console.log('修改後陣列:',arr)

在官方文件,所需監視的只有 push()、pop()、shift()、unshift()、splice()、sort()、reverse() 7 種方法。我們可以遍歷一下:

只需要監聽我們需要監聽的資料陣列的乙個變更,而不是針對原生array的乙個重新封裝。 

會重寫array.prototype.push方法,並生成乙個新的陣列賦值給資料,這樣資料雙向繫結就會觸發。

首先讓這個物件繼承array本身的所有屬性,這樣就不會影響到陣列本身其他屬性的使用,後面對相應的函式進行改寫,也就是在原方法呼叫後去通知其它相關依賴這個屬性發生了變化,這點和object.definepropertysetter所做的事情幾乎完全一樣,唯一的區別是可以細化到使用者到底做的是哪一種操作,以及陣列的長度是否變化

不會汙染到原生array上的原型方法。

首先我們將需要監聽的陣列的原型指標指向newarrproto,然後它會執行原生array中對應的原型方法,與此同時執行我們自己重新封裝的方法。

那麼問題來了,這種形式咋這麼眼熟呢?這不就是我們見到的最多的繼承問題麼?子類(newarrproto)和父類(array)做的事情相似,卻又和父類做的事情不同。但是直接修改__proto__隱式原型指向總感覺心裡怪怪的(因為我們可能看到的多的還是prototype),心裡不(w)舒(t)服(f)。

const arrayproto =array.prototype;

const arraymethods =object.create(arrayproto);

const newarrproto =;

['push', 'pop','shift','unshift','splice','sort','reverse'].foreach(method =>

})let list1 = [1, 2];

//將我們要監聽的陣列的原型指標指向上面定義的空陣列物件

//newarrproto的屬性上定義了我們封裝好的push,pop等方法

list1.__proto__ =newarrproto;

list1.push(3); //

監聽到陣列的變化啦! 3

//list2沒有被重新定義原型指標,所以這裡會正常執行原生array上的原型方法

let list2 = [1, 2];

list2.push(3); //

3

var obj ={}

console.log(array.prototype.push.call(obj, 'a','b','c')) //

3console.log(obj) //

var obj1 =

console.log(array.prototype.push.call(obj1, 'a','b','c')) //

8console.log(obj1) //

var obj2 =

console.log(array.prototype.push.call(obj2, 'a','b','c')) //

10console.log(obj2) //

通過上面對比結果,我們可以看出:

1)當物件中不含有length屬性時,呼叫陣列原型方法push,將物件轉為類陣列物件,新增屬性的索引從0開始,且lengt指是新增屬性的個數

2)當物件中含有length屬性時,新增屬性的索引命名從length長度開始計算。 

eg: obj1中length為5,新增加屬性的索引分別為5、6、7;obj2中length為7,新增加屬性的索引分別為7、8、9 

array.prototype.slice.call()方法是只能在類陣列上起作用的,並不能同push()方法一樣可以可以使物件轉換為帶有length屬性的類陣列物件。

結論,當物件中沒有length屬性時,預設新增的新屬性索引應為0,因為a中已經有為0的key了,於是將原來的banana覆蓋了,便有了現在的結果。

KVO監聽陣列變化

1 某乙個類.h檔案 新建乙個model類 裡面有待監聽selectarray 待監聽陣列模型類 inte ce selectarraymodel nsobject 勾選儲存陣列 property nonatomic,strong nsmutablearray selectarray end 2 某...

iOS KVO 監聽陣列變化

首先,陣列不能直接使用kvo使用監聽。當我們想要使用kvo監聽陣列,我們需要進行一下幾步。1.kvo不能監聽uiviewcontroller中的陣列。我們需要先建立乙個模型,將陣列新增值模型中。inte ce selectedsarr nsobject property nonatomic,stro...

vue監聽陣列變化

1 觸發更新檢視 2function updateview 56 重新定義陣列原型 7 const oldarrayproperty array.prototype8 建立新物件,原型指向 oldarrayproperty 再擴充套件新的方法不會影響原型 9 const arrproto objec...