zoukankan      html  css  js  c++  java
  • 使用defineProperty实现自定义setter, 简化前端Angular的重构工作

    一、问题场景

    Angular的双向绑定给我们开发提供了很大的遍历,将父scope的引用变量作为参数传递给子指令,这样就可以方便的在父作用域内进行业务操作,数据变更会自动传递到子指令。但是如果你基于一个已有的复杂业务模块进行扩展开发,同时要将耦合其中一个功能提取为指令,这个时候就涉及到参数的传递问题。最简的方式就是直接将已有的根数据对象作为参数直接传递过去,参数携带数据大而全,指令内部肯定数据够用不会报错,但是缺点就是参数结构复杂,使用者无法准确的连接所需参数,极大地降低了指令的可用性;

    二、问题分析

    新开发的指令一般都是只传递需要的参数,一般结构形式比较简单,直接在原有的复杂结构中找到对应的字段直接赋值到新的参数即可;但是这里会涉及到一些非引用类型的字段,会给Angular的双向绑定带来问题;这就需要两者之间进行数据的同步更新,最简单的方式就是找到原来数据变更的地方,然后给指令的参数进行赋值即可,但是这种方式不仅繁琐容易出错,而且给以后的开发维护带来不便。

    那么有没有更好的方式来解决这个问题呢,从代码的重构实践来说,最好将数据的变更封装在一个方法中,方便对数据字段的访问控制,那么js是否提供有相关的机制来实现字段的自定义settor和getter呢?

    三、js的属性描述符

    从ES5开始,所有的属性都具备了属性描述符。我们可以通过getOwnPropertyDescriptor获取属性的描述符信息,这个普通的对象属性对应的属性描述符可不仅仅只是一个字符串。它还包含另外三个特性:writable(可写)、enumerable(可枚举)和configurable(可配置)。

      var myObj ={
          name:'mango'
      };
    
      descriptor = Object.getOwnPropertyDescriptor(myObj,'name');
      console.log(descriptor);
    
      // {
      //     "value": "mango",
      //     "writable": true,
      //     "enumerable": true,
      //     "configurable": true
      // }
    

    在创建普通属性时属性描述符会使用默认值,我们也可以使用Object.defineProperty(..)来添加一个新属性或者修改一个已有属性(如果它是configurable)并对特性进行设置。我们使用defineProperty(..)给myOb添加了一个普通的属性并显式指定了一些特性。然而,一般来说我们不会使用这种方式,除非想修改属性描述符。

    var myObj = {};
    Object.defineProperty(myObj, 'name', {
        value: 'apple',
        writable: true,
        configurable: true,
        enumerable: true
    });
    
    //apple
    

    writable决定是否可以修改属性的值。

    var myObj = {};
    Object.defineProperty(myObj, 'name', {
        value: 'apple',
        writable: false,
        configurable: true,
        enumerable: true
    });
    
    myObj.name='pear'
    console.log(myObj.name)
    
    //apple
    

    可以看到,我们对于属性值的修改静默失败(silently failed)了。如果在严格模式下,这种方法会出错,TypeError错误表示我们无法修改一个不可写的属性。

    'use strict'
    
    var myObj = {};
    Object.defineProperty(myObj, 'name', {
        value: 'apple',
        writable: false,
        configurable: true,
        enumerable: true
    });
    
    myObj.name='pear'
    console.log(myObj.name)
    
    // Uncaught TypeError: Cannot assign to read only property 'name' of object '#<Object>'
    

    通过configurable可以控制是否可以修改属性描述符,同时会限制不能删除属性;

    enumerable控制这个属性是否会出现在对象的属性枚举中,比如说for..in循环。如果把enumerable设置成false,这个属性就不会出现在枚举中,虽然仍然可以正常访问它。相对地,设置成true就会让它出现在枚举中。

    在ES5中可以使用getter和setter部分改写默认操作,但是只能应用在单个属性上,无法应用在整个对象上。getter是一个隐藏函数,会在获取属性值时调用。setter也是一个隐藏函数,会在设置属性值时调用。当你给一个属性定义getter、setter或者两者都有时,这个属性会被定义为“访问描述符”(和“数据描述符”相对)。对于访问描述符来说,JavaScript会忽略它们的value和writable特性,取而代之的是关心set和get(还有configurable和enumerable)特性。

    var myObj = {
        _no:0,
        get no(){
            return this._no;
        },
        set no(val){
            this._no = val;
        }
    }
    
    Object.defineProperty(myObj, 'name', {
        get:function(){
            return 'mango' + this.no;
        }
    });
    
    console.log(myObj.no);
    console.log(myObj.name)
    
    myObj.no = 3
    console.log(myObj.name)
    
    // 0
    // mango0
    // mango3
    

    四、解决方案

    通过了解js提供的属性描述符机制,我们可以通过defineProperty来给需要同步的字段添加getter和setter访问控制器,实现方式如下

        function addPropertyControl(obj, property, syncObj, syncProperty) {
            syncProperty = syncProperty ? syncProperty : property;
            var dProperty = '_' + property;
            obj[dProperty] = obj[property];
            Object.defineProperty(obj, property, {
                get: function () {
                    return obj[dProperty];
                },
                set: function (value) {
                    syncObj[syncProperty] = value;
                    obj[dProperty] = value;
                }
            });
        }
    
    
        function autoSyncDataModel() {
            var sObj = $scope.dataModel;
            var syncObj = $scope.option;
            addPropertyControl(sObj, 'name', syncObj);
            addPropertyControl(sObj, 'parents', syncObj);
            addPropertyControl(sObj, 'age', syncObj);
            addPropertyControl(sObj, 'isMan', syncObj);
        }
    

    原文地址

  • 相关阅读:
    通信接收机同步模块
    CAZAC序列
    Verilog Tricks
    载波同步
    Matlab step方法
    CRC校验码
    比特冗余
    Vivado RAM使用
    collection
    hashlib
  • 原文地址:https://www.cnblogs.com/wufengtinghai/p/14720070.html
Copyright © 2011-2022 走看看