The Object.defineProperty() method defines a new property directly on an object, or modifies an exisiting property on an
object, and returns the object.
从上面得知,我们可以通过 Object.defineProperty这个方法,直接在一个对象上定义一个新的属性,或者是修改已存在的属性。最终这个方法会返回该对象。
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(obj, prop, descriptor)
在ES6中,由于 Symbol类型的特殊性,用Symbol类型的值来做对象的key与常规的定义或修改不同,而Object.defineProperty
是定义key为Symbol的属性的方法之一。
其中descriptor的参数值得我们关注下,该属性可设置的值有:
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
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
注意 在调用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}
从上面,可以得知,我们可以通过使用Object.defineProperty,来定义和控制一些特殊的属性,如属性是否可读,属性是否可枚举,甚至修改属性的修改器(setter)和获取器(getter)
在一些框架,如vue、express、qjs等,经常会看到对Object.defineProperty的使用。那这些框架是如何使用呢?
如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, 我们则可以
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 }
如在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. ); }, configurable: true });
Object.defineProperty是ES5的属性,一些场景如IE8以下是不兼容的