• Object.defineProperty


    对象是由多个名/值对组成的无序的集合。对象中每个属性对应任意类型的值。
    定义对象可以使用构造函数或字面量的形式:

    var obj = new Object;  //obj = {}
    obj.name = "张三";  //添加描述
    obj.say = function(){};  //添加行为

    除了以上添加属性的方式,还可以使用Object.defineProperty定义新属性或修改原有的属性。

    语法:

    Object.defineProperty(obj, prop, descriptor)

    参数说明:

    obj:必需。目标对象 
    prop:必需。需定义或修改的属性的名字
    descriptor:必需。目标属性所拥有的特性

    返回值:

    传入函数的对象。即第一个参数obj

    针对属性,我们可以给这个属性设置一些特性,比如是否只读不可以写;是否可以被for..inObject.keys()遍历。

    给对象的属性添加特性描述,目前提供两种形式:数据描述和存取器描述。

    Object.defineProperty ,顾名思义,为对象定义属性。在js中我们可以通过下面这几种方法定义属性

    // (1) define someOne property name
    someOne.name = 'cover';
    //or use (2) 
    someOne['name'] = 'cover';
    // or use (3) defineProperty
    Object.defineProperty(someOne, 'name', {
        value : 'cover'
    })
    

    从上面看,貌似使用Object.defineProperty很麻烦,那为啥存在这样的方法呢?

    带着疑问,我们来看下 Object.defineProperty的定义。


     

    其中descriptor的参数值得我们关注下,该属性可设置的值有:

    • 【value】 属性的值,默认为 undefined。
    • 【writable】 该属性是否可写,如果设置成 false,则任何对该属性改写的操作都无效(但不会报错),对于像前面例子中直接在对象上定义的属性,这个属性该特性默认值为为 true。
    var someOne = { };
    Object.defineProperty(someOne, "name", {
        value:"coverguo" , //由于设定了writable属性为false 导致这个量不可以修改
        writable: false 
    });  
    console.log(someOne.name); // 输出 coverguo
    someOne.name = "linkzhu";
    console.log(someOne.name); // 输出coverguo
    
    • 【configurable]】如果为false,则任何尝试删除目标属性或修改属性以下特性(writable, configurable, enumerable)的行为将被无效化,对于像前面例子中直接在对象上定义的属性,这个属性该特性默认值为为 true。 。
    var someOne = { };
    Object.defineProperty(someOne, "name", {
        value:"coverguo" ,
        configurable: false 
    });  
    delete someOne.name; 
    console.log(someOne.name);// 输出 coverguo
    someOne.name = "linkzhu";
    console.log(someOne.name); // 输出coverguo
    
    • 【enumerable】 是否能在for-in循环中遍历出来或在Object.keys中列举出来。对于像前面例子中直接在对象上定义的属性,这个属性该特性默认值为为 true。

    注意 在调用Object.defineProperty()方法时,如果不指定, configurable, enumerable, writable特性的默认值都是false,这跟之前所 说的对于像前面例子中直接在对象上定义的属性,这个特性默认值为为 true。并不冲突,如下代码所示:

    //调用Object.defineProperty()方法时,如果不指定
    var someOne = { };
    someOne.name = 'coverguo';
    console.log(Object.getOwnPropertyDescriptor(someOne, 'name'));
    //输出 Object {value: "coverguo", writable: true, enumerable: true, configurable: true}
    
    //直接在对象上定义的属性,这个特性默认值为为 true
    var otherOne = {};
    Object.defineProperty(otherOne, "name", {
        value:"coverguo" 
    });  
    console.log(Object.getOwnPropertyDescriptor(otherOne, 'name'));
    //输出 Object {value: "coverguo", writable: false, enumerable: false, configurable: false}
    
    • 【get】一旦目标对象访问该属性,就会调用这个方法,并返回结果。默认为 undefined。
    • 【set】 一旦目标对象设置该属性,就会调用这个方法。默认为 undefined。

    从上面,可以得知,我们可以通过使用Object.defineProperty,来定义和控制一些特殊的属性,如属性是否可读,属性是否可枚举,甚至修改属性的修改器(setter)和获取器(getter)

    那什么场景和地方适合使用到特殊的属性呢?


    实际运用

    在一些框架,如vue、express、qjs等,经常会看到对Object.defineProperty的使用。那这些框架是如何使用呢?

    MVVM中数据‘双向绑定’实现

    如vue,qjs等大部分mvvm框架(angular用的是脏处理)都是通过Object.defineProperty来实现数据绑定的 为了更详细的说明,我将在下一篇文章跟大家讲解下。下面篇幅先不展开。(别扔砖。。。)

    优化对象获取和修改属性方式

    这个优化对象获取和修改属性方式,是什么意思呢? 过去我们在设置dom节点transform时是这样的。

    //加入有一个目标节点, 我们想设置其位移时是这样的
    var targetDom = document.getElementById('target');
    var transformText = 'translateX(' + 10 + 'px)';
    targetDom.style.webkitTransform = transformText;
    targetDom.style.transform = transformText;
    

    通过上面,可以看到如果页面是需要许多动画时,我们这样编写transform属性是十分蛋疼的。(┬_┬)

    但如果通过Object.defineProperty, 我们则可以

    //这里只是简单设置下translateX的属性,其他如scale等属性可自己去尝试
    
    Object.defineProperty(dom, 'translateX', {
    set: function(value) {
             var transformText = 'translateX(' + value + 'px)';
            dom.style.webkitTransform = transformText;
            dom.style.transform = transformText;
    }
    //这样再后面调用的时候, 十分简单
    dom.translateX = 10;
    dom.translateX = -10;
    //甚至可以拓展设置如scale, originX, translateZ,等各个属性,达到下面的效果
    dom.scale = 1.5;  //放大1.5倍
    dom.originX = 5;  //设置中心点X
    }
    

    上面只是个简单的版本,并不是最合理的写法,但主要是为了说明具体的意图和方法

    有兴趣了解更多可以看下面这个库:https://github.com/AlloyTeam/AlloyTouch/blob/master/transform.js

    增加属性获取和修改时的信息

    如在Express4.0中,该版本去除了一些旧版本的中间件,为了让用户能够更好地发现,其有下面这段代码,通过修改get属性方法,让用户调用废弃属性时抛错并带上自定义的错误信息。

    [
      'json',
      'urlencoded',
      'bodyParser',
      'compress',
      'cookieSession',
      'session',
      'logger',
      'cookieParser',
      'favicon',
      'responseTime',
      'errorHandler',
      'timeout',
      'methodOverride',
      'vhost',
      'csrf',
      'directory',
      'limit',
      'multipart',
      'staticCache',
    ].forEach(function (name) {
      Object.defineProperty(exports, name, {
        get: function () {
          throw new Error('Most middleware (like ' + name + ') is no longer bundled with Express and must be installed separately. Please see https://github.com/senchalabs/connect#middleware.');
        },
        configurable: true
      });
    });
    

    其他如设置常量等用途。


    兼容

    最后注意下,Object.defineProperty是ES5的属性,大部分场景使用是没问题的, 但在一些场景如IE8以下是使用不到的哈。

    在ie8下只能在DOM对象上使用,尝试在原生的对象使用 Object.defineProperty()会报错

  • 相关阅读:
    openstack o版本自动化脚本安装
    定时关机重启
    centos7.2 安装openstack
    Ubuntu 16.03 apt-get更换为国内阿里云源
    centos7 安装php7+mysql5.7+nginx+redis
    centos7 安装LNMP7
    多个路由器配置静态路由 简单
    puppet笔记
    MySQL备份与恢复实战案例及生产方案
    WAF:web应用防火墙
  • 原文地址:https://www.cnblogs.com/zhangdzz/p/10556256.html
Copyright © 2020-2023  润新知