当前位置:网站首页>Object. Can defineproperty also listen for array changes?

Object. Can defineproperty also listen for array changes?

2022-06-25 08:48:00 InfoQ

Brief introduction

give the thumbs-up  +  Focus on  +  Collection  =  Learned to



First , Answer the title :
Object.defineProperty
  Cannot listen for changes to native arrays . To listen to an array , To convert an array into an object .


stay  
Vue2
  Was used  
Object.defineProperty
  Monitor data changes , But I checked  
file
, Find out  
Object.defineProperty
  Is used to listen for changes in the specified attributes of an object . I don't see any that can listen for array changes .

but  
Vue2
  There are some methods that can really listen to an array and change the value of the array . The goal of this article is to untie this knot .


Basic usage

Object.defineProperty()  file

About  
Object.defineProperty()
  Usage of , You can see the official documents .

The basic part of this article is just a simple explanation .

grammar

Object.defineProperty(obj, prop, descriptor)

Parameters

  • obj
      The object to define the property .
  • prop
      The name of the property to be defined or modified or  
    Symbol
     .
  • descriptor
      Property descriptor to define or modify .

const data = {}
let name = ' Thunder monkey '

Object.defineProperty(data, 'name', {
 get() {
 console.log('get')
 return name
 },
 set(newVal) {
 console.log('set')
 name = newVal
 }
})

console.log(data.name)
data.name = ' Shark chilli '

console.log(data.name)
console.log(name)

The above code will output

get
Thunder monkey
set
Shark chilli
Shark chilli


It means , If you need to visit  
data.name
 , Then return  
name
  Value .

If you want to set  data.name , That will put the value you passed in into the variable  
name
  in .

Visit again at this time  
data.name
  perhaps  
name
 , Will return the newly assigned value .


There is another basic usage :
“ frozen ” Specify properties

const data = {}

Object.defineProperty(data, 'name', {
 value: ' Thunder monkey ',
 writable: false
})

data.name = ' Shark chilli '
delete data.name
console.log(data.name)

This example , hold  
data.name
  Frozen , Whether you want to modify or delete, it will not take effect , Once visited  
data.name
  All return  
Thunder monkey
 .

That's all  
Object.defineProperty
  Basic usage .


Deep monitoring

The above example is to listen to the basic object . But if the object also contains objects , In this case, you can use recursion .

Recursion needs to create a method , Then determine whether to call itself repeatedly .

//  Trigger update view
function updateView() {
 console.log(' View update ')
}

//  Redefining properties , Listen up ( The core )
function defineReactive(target, key, value) {

 //  Deep monitoring
 observer(value)

 //  The core  API
 Object.defineProperty(target, key, {
 get() {
 return value
 },
 set(newValue) {
 if (newValue != value) {
 //  Deep monitoring
 observer(newValue)

 //  Set new value
 //  Be careful ,value  Always in closures , After setting here , Again  get  It will also get the latest value
 value = newValue

 //  Trigger view update
 updateView()
 }
 }
 })
}

//  Deep monitoring
function observer(target) {
 if (typeof target !== 'object' || target === null) {
 //  Not an object or an array
 return target
 }

 //  Redefine each attribute (for in  You can also iterate over groups )
 for (let key in target) {
 defineReactive(target, key, target[key])
 }
}

//  Prepare the data
const data = {
 name: ' Thunder monkey '
}

//  Start listening
observer(data)

//  test 1
data.name = {
 lastName: ' Shark chilli '
}

//  test 2
data.name.lastName = ' Cockroach bully '

The above example will output 2 Time “ View update ”.



I created a  
updateView
  Method , This method simulates updating  
DOM
 ( similar  
Vue
The operation of ), But here I simplify it to output  “ View update ” . Because this is not the focus of this article .


test 1
  It will trigger once  “ View update ” ;
test 2
  It will also trigger once .

Because in  
Object.defineProperty
  Of  
set
  I called it once  
observer(newValue)
 , 
observer
  It will determine whether the value passed in is an object , If it is an object, call again  
defineReactive
  Method .

This simulates a recursive state .


That's all  
Deep monitoring
  Principle , It's just recursion .

But recursion has one bad thing , If the object level is very deep , The amount of calculation is very large , Because it needs to be calculated once .


Listening array

The array without  
key
 , Only  
Subscript
. So if you need to listen for changes in the contents of the array , You need to convert an array into an object , And also simulate the array method .

The general idea and coding process are as follows :

  • Determine whether the data to be monitored is an array
  • Is an array , Just simulate the array as an object
  • Bind the method name of the array to the newly created object
  • Assign the method corresponding to the array prototype to the custom method



The code is as follows

//  Trigger update view
function updateView() {
 console.log(' View update ')
}

//  Redefine array prototypes
const oldArrayProperty = Array.prototype
//  Create new objects , Prototype pointing  oldArrayProperty, Extending the new method will not affect the prototype
const arrProto = Object.create(oldArrayProperty);

['push', 'pop', 'shift', 'unshift', 'splice'].forEach(methodName => {
 arrProto[methodName] = function() {
 updateView() //  Trigger view update
 oldArrayProperty[methodName].call(this, ...arguments)
 }
})

//  Redefining properties , Listen up ( The core )
function defineReactive(target, key, value) {

//  Deep monitoring
observer(value)

 //  The core  API
 Object.defineProperty(target, key, {
 get() {
 return value
 },
 set(newValue) {
 if (newValue != value) {
 //  Deep monitoring
 observer(newValue)

 //  Set new value
 //  Be careful ,value  Always in closures , After setting here , Again  get  It will also get the latest value
 value = newValue

 //  Trigger view update
 updateView()
 }
 }
 })
}

//  Listening object properties ( entrance )
function observer(target) {
 if (typeof target !== 'object' || target === null) {
 //  Not an object or an array
 return target
 }

 //  In the case of arrays
 if (Array.isArray(target)) {
 target.__proto__ = arrProto
 }

 //  Redefine each attribute (for in  You can also iterate over groups )
 for (let key in target) {
 defineReactive(target, key, target[key])
 }
}

//  Prepare the data
const data = {
 nums: [10, 20, 30]
}

//  Monitor data
observer(data)

data.nums.push(4) //  Listening array

The reason why the above code does not directly modify the array method , Such as

 Array.prototype.push = function() {
 updateView()
 ...
 }

Because it will pollute the original  
Array
  Prototype method of , It will not pay off .

The above is the use of  
Object.defineProperty
  Methods .

For more ways to listen , Can be in the array  
['push', 'pop', 'shift', 'unshift', 'splice']
  Add .


Comprehensive code

//  Deep monitoring
function updateView() {
 console.log(' View update ')
}

//  Redefine array prototypes
const oldArrayProperty = Array.prototype
//  Create new objects , Prototype pointing  oldArrayProperty, Extending the new method will not affect the prototype
const arrProto = Object.create(oldArrayProperty);
// arrProto.push = function () {}
// arrProto.pop = function() {}
['push', 'pop', 'shift', 'unshift', 'splice'].forEach(methodName => {
 arrProto[methodName] = function() {
 updateView() //  Trigger view update
 oldArrayProperty[methodName].call(this, ...arguments)
 }
})

//  Redefining properties , Listen up ( The core )
function defineReactive(target, key, value) {

 //  Deep monitoring
 observer(value)

 //  The core  API
 // Object.defineProperty  Does not have the ability to listen to arrays
 Object.defineProperty(target, key, {
 get() {
 return value
 },
 set(newValue) {
 if (newValue != value) {
 //  Deep monitoring
 observer(newValue)

 //  Set new value
 //  Be careful ,value  Always in closures , After setting here , Again  get  It will also get the latest value
 value = newValue

 //  Trigger view update
 updateView()
 }
 }
 })
}

//  Listening object properties ( entrance )
function observer(target) {
 if (typeof target !== 'object' || target === null) {
 //  Not an object or an array
 return target
 }

 if (Array.isArray(target)) {
 target.__proto__ = arrProto
 }

 //  Redefine each attribute (for in  You can also iterate over groups )
 for (let key in target) {
 defineReactive(target, key, target[key])
 }
}

summary

The code above mainly simulates  
Vue 2
  Monitor data changes , Although easy to use , But there are also shortcomings .

shortcoming

  • Deep monitoring , You need to recurse to the end , A large amount of calculation
  • Unable to listen for new properties / Delete attribute ( So we need to use  Vue.set  and  Vue.delete)
  • Cannot listen to native arrays , Require special treatment


So in  
Vue 3
  in , hold  
Object.defineProperty
  Change to  
Proxy
 .

but  
Proxy
  The disadvantages are also obvious , It's about compatibility . So you need to choose according to your project  
Vue 2
  still  
Vue 3
 .

原网站

版权声明
本文为[InfoQ]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/176/202206250743462770.html