javascript --- > vue2.x中原型的使用(攔截數組方法) && 響應式原理(部分)

說明

在Vue2.x中,利用了對原型鏈的理解,巧妙的利用JavaScript中的原型鏈,實現了數組的pop、push、shift、unshift、reverse、sort、splice等的攔截.

你可能需要的知識

原型鏈

JavaScript常被描述爲一種基於原型的語言(prototype-based language),每個對象擁有一個原型.
數組類型也不例外.驗證如下:

let arr = [];
console.log(arr)
/*
   {
   	length: 0
   	__proto__: {
   		length: 0
   		constructor: f Array()
   		...
   		__proto__: Object
   	}
   }
*/

可見數組的原型是繼承於Object。

響應式

參考 - MDN
響應式的核心是使用Object.defineProperty在對數據進行讀取或者寫入時進行劫持操作.

let o = {}
,_gender
Object.defineProperty(o, gender, {
	get(){
		return _gender
	},
	set(newVal){
		_gender = newVal
	}	
})

對一個屬性,同時使用get和set方法時,需要一箇中間變量取存儲,否則會造成循環使用.

Vue 2.x中的響應式

  • Vue在使用過程中,可能會用到很多的變量,而每把一個數據進行響應式化,就需要一個變量去存儲.這樣有可能會污染全局作用域.
  • Vue中採取的方法是使用函數的形參,來實現響應式,實現如下
function defineReactive(target, key, value, enumerable){
   // 注意: 此處的value與上文的_gender類型
   Object.defineProperty(target, key, {
   	configurable: true,
   	enumerable: !!enumerable,
   	get(){
   		console.log(`讀取${value}`)
   		return value
   	},
   	set(newVal){
   		console.log(`寫入: ${value} --> ${newVal}`)
   		value = newVal
   	}
   })
}
let o = {
   name: 'marron',
   age: 26,
   remark: 'hunt for job'
}
Object.keys(o).forEach(k => {
   defineReactive(o,k,o[k],true)
})

在這裏插入圖片描述
以上實現了對數據的攔截: 即對數據進行 寫入/讀取 操作時,會按照一定規則優先執行某些步驟.
但是以上代碼還存在一些小小的瑕疵

對象深層次

以上代碼不對對象的深層次進行響應式化,如下面數據

let o = {
	list: [ 
		{ 
			person1: {
				name:'Marron',
				age: 18
		}},
		{
			person2: {
				name:'Mar',
				age: 25
			}
		}
	]
}

在這裏插入圖片描述
此時,需要考慮數組,和對象的子元素問題.
對於數組問題,我們修改遍歷,如果是數組,則取出數組中的每個元素,進行添加響應式處理

- Object.keys(o).forEach(k =>{
- 	defineReactive(o, k, o[k], true)
- })
+ function reactify(o){
+ 	Object.keys(o).forEach(k => {
+ 		if(Array.isArray(o[k])){
+ 			o[k].forEach(val => reactive(val))
+ 		} else {
+ 			defineReactive(o, k, o[k], true)
+ 		}
+ })}

對於深層次對象問題,我們對defineReactive進行修改

function defineReactive(o, key, value, enumerable){
	if(typeof value =='object' && value !== null && !Array.isArray(value)){
		// 此處可以認爲是對象
		reactify(value)
	}
	// 此處是最後一層,添加響應式
	Object.defineProperty(o, key, {
		configurable: true,
		enumerable: !!enumerable,
		get(){
			console.log(`讀取${key}`)
			return value
		},
		set(newVal){
			console.log(`寫入${key} => ${newVal}`)
			value = newVal
		}
	})
}

在這裏插入圖片描述

Vue2.x對數組部分方法的攔截

上面的響應式無法對數組的pop、push等方法進行響應
在這裏插入圖片描述
在Vue2.x中,使用了修改原型鏈的結構的方式來對數組的變化進行攔截.
先看下面的關係

arr
Array.prototype
Object.prototype
  • 原本的關係圖示已經描述的很清楚了
  • 我們對pop和push的攔截的原理
  • 實際上是對Array原型上的pop、push方法進行重寫
  • 但是我們不可能直接在這個原型上重寫(因爲有些數組的實例,並不需要響應式).
  • 因此我們在arrArray.prototype之間添加一層arr_methods,改進後的關係如下
arr
arr_methods
Array.prototype
Object.prototype

【具體的實現思路】:
先創建一個arr_methods對象其原型是Array.prototype.然後修改arr_methods上需要攔截的方法(存儲在數組ARRAY_METHOD中)

const ARRAY_METHOD = [
	'push',
	'pop',
	'shift',
	'unshift',
	'reverse',
	'sort',
	'splice'
]
let arr_methods = Object.create(Array.prototype)

ARRAY_METHOD.forEach(method=>{
	arr_methods[method] = function(){
		// 攔截的函數
		console.log(`調用${method}方法`)	
		return Array.prototype[method].apply(this, arguments)
	}
})
arr.__proto__ = arr_methods

在這裏插入圖片描述
此時既不影響原生的Array.prototype,又實現了對pop、push...方法的攔截,完成之後只需要修改前面的方法.即可完成對數組pop、push方法的攔截

 function reactify(o){
 	Object.keys(o).forEach(k => {
 		if(Array.isArray(o[k])){
 			// 數組方法的響應式
 			o[k].__proto__ = array_method
. 			o[k].forEach(val => reactive(val))
 		} else {
 			defineReactive(o, k, o[k], true)
 		}
 })}

最後,此時只是攔截,還差一步形成響應式

ARRAY_METHOD.forEach(method=>{
	arr_methods[method] = function(){
		// 攔截的函數
		console.log(`調用${method}方法`)
		for(let i =0, len = arugments.length; i < len; i++){
			reactify(arguments[i])
		}
		return Array.prototype[method].apply(this, arguments)
	}
})
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章