Object.defineProperty

 

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

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

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

语法:java

Object.defineProperty(obj, prop, descriptor)

参数说明:git

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

返回值:web

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

针对属性,咱们能够给这个属性设置一些特性,好比是否只读不能够写;是否能够被for..inObject.keys()遍历。express

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

Object.defineProperty ,顾名思义,为对象定义属性。在js中咱们能够经过下面这几种方法定义属性cookie

// (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()会报错

相关文章
相关标签/搜索