データ応答式の基礎


Object.defineProperty()
Object.defineProperty()メソッドは、オブジェクトに直接新しい属性を定義したり、オブジェクトの既存の属性を変更したりして、このオブジェクトに戻ります.三つのパラメータを受信します.
  • obj:新しい属性を定義する必要があるオブジェクト
  • prop:定義または修正される属性名
  • descriptor:定義または修正される属性の記述子は、データ・ディスクリプタとアクセス・ディスクリプタに分類される.
  • データ記述子:すなわち、descriptorは具体的な値であり、またはvalue属性を持つオブジェクトである:
  • Object.defineProperty(obj, 'data', 'abc')
    Object.defineProperty(obj, 'data', {
           
    	value: 'abc',
    	writable: false
    })
    
  • アクセスディスクリプタ:属性のgetterとsetter関数
  • です.
    let value
    Object.defineProperty(obj, 'data', {
           
    	get () {
           
    		return obj.data
    	},
    	set (newVal) {
           
    		value = newVal
    	}
    })
    
    また、2つの記述子が共有する2つのキーがあります.
  • configrable:この属性のconfigrableのキー値がtrueである場合にのみ、この属性の記述子は変更され、対応するオブジェクトから削除されることができる.デフォルトはfalseです
  • enumerable:この属性のenumerableキーがtrueである場合にのみ、この属性はオブジェクトの列挙属性に現れる.デフォルトはfalseですObject.defineProperty()方法の詳細については、ここをクリックしてください.
    実現データ応答式
  • Objectの応答式
  • 最初に簡単なページを実現します.
    この中のobj.fooは一秒ごとに変化します.setter関数を触発して、setter関数の中でビューを更新できます.ここで簡単なページを実現しました.
    
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Documenttitle>
    head>
    
    <body>
      <div id="app">div>
      <script>
        const app = document.getElementById('app')
        function definedReactive(obj, key, val) {
          
          Object.defineProperty(obj, key, {
          
            get() {
          
              console.log('get', key, val)
              return val
            },
            set(newVal) {
          
              if (newVal !== val) {
          
                console.log('set', key, newVal)
                val = newVal
                //          
                update()
              }
            }
          })
        }
    
        function update() {
          
          app.innerText = obj.foo
        }
        const obj = {
          }
        definedReactive(obj, 'foo', '')
        obj.foo = new Date().toLocaleTimeString()
        setInterval(() => {
          
          obj.foo = new Date().toLocaleTimeString()
        }, 1000)
      script>
    body>
    
    html>
    
    しかし、このOB jの構造はとても簡単で、一つのkey、一つのvalue、それはobj={foo]、baz:'baz}のように複数のkeyの対象があります.このように処理すると明らかに足りなくなります.各keyを遍歴して、各keyに対して応答式の処理を行う必要があります.
  • defineReactive.js
  • //     key
    function observe (obj) {
         
      if (!obj || typeof obj !== 'object') {
         
        return
      }
      Object.keys(obj).forEach(key => {
         
        defineReactive(obj, key, obj[key])
      })
    }
    
    //    key   getter setter
    function defineReactive (obj, key, val) {
         
      Object.defineProperty(obj, key, {
         
        get () {
         
          console.log('get', key, val)
          return val
        },
        set (newVal) {
         
          if (newVal !== val) {
         
            console.log('set', key, newVal)
            val = newVal
          }
        }
      })
    }
    
    let obj = {
          foo: 'foo', baz: 'baz' }
    observe(obj)
    
    obj.foo // get foo foo
    obj.foo = 'fooooooooo' //set foo fooooooooo
    
    もっと複雑なオブジェクトに対しては、Obj={foo],baz:{a:1}を再帰的に使って応答式の処理を完了する必要があります.
    //     key
    function observe (obj) {
         
      //   obj      ,      
      if (!obj || typeof obj !== 'object') {
         
        return
      }
      Object.keys(obj).forEach(key => {
         
        defineReactive(obj, key, obj[key])
      })
    }
    
    //    key   getter setter
    function defineReactive (obj, key, val) {
         
      //   val   object,         ,        observe()   ,        
      observe(val)
      Object.defineProperty(obj, key, {
         
        get () {
         
          console.log('get', key, val)
          return val
        },
        set (newVal) {
         
          if (newVal !== val) {
         
            //      newval,        
            observe(newVal)
            console.log('set', key, newVal)
            val = newVal
          }
        }
      })
    }
    
    let obj = {
          foo: 'foo', bar: 'bar', baz: {
          a: 1 } }
    observe(obj)
    
    obj.baz.a // get baz { a: [Getter/Setter] }  get a 1
    obj.baz.a = {
          b: 1 } // get baz { a: [Getter/Setter] }  set a { b: [Getter/Setter] }
    
  • 以上はObjectタイプの変数に応答して処理します.Arayタイプに対しては、別の処理をする必要があります.
    jsでは、元の配列を変える方法は7つしかありません.push()、pop()、shift()、unshift()、sort()、reverse()、splice()です.だから、これらの方法を書き換えて、更新を知らせる機能を持たせるだけで、配列の応答式を実現することができます.
    // 1、       
    // 2、       ,         
    // 3、      
    // 4、                     
    
    const arrayProto = Array.prototype
    const newProto = Object.create(arrayProto)
    const methods = ['push', 'pop', 'shift', 'unshift', 'sort', 'reverse', 'splice']
    methods.forEach(method => {
         
      newProto[method] = function () {
         
        //            
        arrayProto[method].apply(this, arguments)
        //           ,    arr      
        observe(this)
        console.log(method + '   !')
      }
    })
    
    元のobserve関数を修正します.
    //     key
    function observe (obj) {
         
      //      object,       
      if (!obj || typeof obj !== 'object') {
         
        return
      }
      //   obj     
      if (Array.isArray(obj)) {
         
        //    arr          ,        arrayProto  
        obj.__proto__ = Object.assign({
         }, arrayProto, newProto)
        //          ,      object,         
        for (let i = 0; i < obj.length; i++) {
         
          observe(obj[i])
        }
      } else {
         
        Object.keys(obj).forEach(key => {
         
          defineReactive(obj, key, obj[key])
        })
      }
    }
    
    テストをします
    let arr = [1, 2, 3]
    observe(arr)
    arr.push({
          c: 4 }) // push   !
    console.log(arr[3]) // { c: [Getter/Setter] }
    arr[3].c = 5 // set c 5
    
    ここをクリックしてObject.creat()の方法について詳しく紹介します.
    ここをクリックしてObject.assignの方法について詳しく紹介します.