首先描述一下定义以及用法javascript
Object.defineProperty()的做用就是直接在一个对象上定义一个新属性,或者修改一个已经存在的属性。java
Object.defineProperty(obj, prop, desc)
obj 须要定义属性的当前对象
prop 当前须要定义的属性名
desc 属性描述符
通常经过为对象的属性赋值的状况下,对象的属性能够修改也能够删除,可是经过Object.defineProperty()定义属性,经过描述符的设置能够进行更精准的控制对象属性。数组
属性的特性以及内部属性
javacript 有三种类型的属性浏览器
命名数据属性:拥有一个肯定的值的属性。这也是最多见的属性
命名访问器属性:经过getter和setter进行读取和赋值的属性
内部属性:由JavaScript引擎内部使用的属性,不能经过JavaScript代码直接访问到,不过能够经过一些方法间接的读取和设置。好比,每一个对象都有一个内部属性[[Prototype]],你不能直接访问这个属性,但能够经过Object.getPrototypeOf()方法间接的读取到它的值。虽然内部属性一般用一个双吕括号包围的名称来表示,但实际上这并非它们的名字,它们是一种抽象操做,是不可见的,根本没有上面两种属性有的那种字符串类型的属性
<script>
var obj={};
Object.defineProperty(obj,"name",{
get:function(){
return document.querySelector("#name").innerHTML;
},
set:function(val){
document.querySelector("#name").innerHTML=val;
}
});
obj.name="Jerry";
</script>
语法节
Object.defineProperty(obj, prop, descriptor)
参数节
obj
要在其上定义属性的对象。
prop
要定义或修改的属性的名称。
descriptor
将被定义或修改的属性描述符。
返回值节
被传递给函数的对象。app
在ES6中,因为 Symbol类型的特殊性,用Symbol类型的值来作对象的key与常规的定义或修改不一样,而Object.defineProperty 是定义key为Symbol的属性的方法之一。函数
描述节
该方法容许精确添加或修改对象的属性。经过赋值操做添加的普通属性是可枚举的,可以在属性枚举期间呈现出来(for...in 或 Object.keys 方法), 这些属性的值能够被改变,也能够被删除。这个方法容许修改默认的额外选项(或配置)。默认状况下,使用 Object.defineProperty() 添加的属性值是不可修改的。性能
属性描述符节
对象里目前存在的属性描述符有两种主要形式:数据描述符和存取描述符。数据描述符是一个具备值的属性,该值多是可写的,也可能不是可写的。存取描述符是由getter-setter函数对描述的属性。描述符必须是这两种形式之一;不能同时是二者。this
数据描述符和存取描述符均具备如下可选键值(默认值是在使用Object.defineProperty()定义属性的状况下):prototype
configurable
当且仅当该属性的 configurable 为 true 时,该属性描述符才可以被改变,同时该属性也能从对应的对象上被删除。默认为 false。
enumerable
当且仅当该属性的enumerable为true时,该属性才可以出如今对象的枚举属性中。默认为 false。
数据描述符同时具备如下可选键值:日志
value
该属性对应的值。能够是任何有效的 JavaScript 值(数值,对象,函数等)。默认为 undefined。
writable
当且仅当该属性的writable为true时,value才能被赋值运算符改变。默认为 false。
存取描述符同时具备如下可选键值:
get
一个给属性提供 getter 的方法,若是没有 getter 则为 undefined。当访问该属性时,该方法会被执行,方法执行时没有参数传入,可是会传入this对象(因为继承关系,这里的this并不必定是定义该属性的对象)。
默认为 undefined。
set
一个给属性提供 setter 的方法,若是没有 setter 则为 undefined。当属性值修改时,触发执行该方法。该方法将接受惟一参数,即该属性新的参数值。
默认为 undefined。
描述符可同时具备的键值
configurable enumerable value writable get set
数据描述符 Yes Yes Yes Yes No No
存取描述符 Yes Yes No No Yes Yes
若是一个描述符不具备value,writable,get 和 set 任意一个关键字,那么它将被认为是一个数据描述符。若是一个描述符同时有(value或writable)和(get或set)关键字,将会产生一个异常。
记住,这些选项不必定是自身属性,若是是继承来的也要考虑。为了确认保留这些默认值,你可能要在这以前冻结 Object.prototype,明确指定全部的选项,或者经过 Object.create(null)将__proto__属性指向null。
// 使用 __proto__ var obj = {}; var descriptor = Object.create(null); // 没有继承的属性 // 默认没有 enumerable,没有 configurable,没有 writable descriptor.value = 'static'; Object.defineProperty(obj, 'key', descriptor); // 显式 Object.defineProperty(obj, "key", { enumerable: false, configurable: false, writable: false, value: "static" }); // 循环使用同一对象 function withValue(value) { var d = withValue.d || ( withValue.d = { enumerable: false, writable: false, configurable: false, value: null } ); d.value = value; return d; } // ... 而且 ... Object.defineProperty(obj, "key", withValue("static")); // 若是 freeze 可用, 防止代码添加或删除对象原型的属性 // (value, get, set, enumerable, writable, configurable) (Object.freeze||Object)(Object.prototype); 示例节 若是你想了解如何使用Object.defineProperty方法和类二进制标记语法,看看这篇文章。 建立属性节 若是对象中不存在指定的属性,Object.defineProperty()就建立这个属性。当描述符中省略某些字段时,这些字段将使用它们的默认值。拥有布尔值的字段的默认值都是false。value,get和set字段的默认值为undefined。一个没有get/set/value/writable定义的属性被称为“通用的”,并被“键入”为一个数据描述符。 var o = {}; // 建立一个新对象 // 在对象中添加一个属性与数据描述符的示例 Object.defineProperty(o, "a", { value : 37, writable : true, enumerable : true, configurable : true }); // 对象o拥有了属性a,值为37 // 在对象中添加一个属性与存取描述符的示例 var bValue; Object.defineProperty(o, "b", { get : function(){ return bValue; }, set : function(newValue){ bValue = newValue; }, enumerable : true, configurable : true }); o.b = 38; // 对象o拥有了属性b,值为38 // o.b的值如今老是与bValue相同,除非从新定义o.b // 数据描述符和存取描述符不能混合使用 Object.defineProperty(o, "conflict", { value: 0x9f91102, get: function() { return 0xdeadbeef; } }); // throws a TypeError: value appears only in data descriptors, get appears only in accessor descriptors 修改属性节 若是属性已经存在,Object.defineProperty()将尝试根据描述符中的值以及对象当前的配置来修改这个属性。若是旧描述符将其configurable 属性设置为false,则该属性被认为是“不可配置的”,而且没有属性能够被改变(除了单向改变 writable 为 false)。当属性不可配置时,不能在数据和访问器属性类型之间切换。 当试图改变不可配置属性(除了value和writable 属性以外)的值时会抛出TypeError,除非当前值和新值相同。 Writable 属性 当writable属性设置为false时,该属性被称为“不可写”。它不能被从新分配。 var o = {}; // Creates a new object Object.defineProperty(o, 'a', { value: 37, writable: false }); console.log(o.a); // logs 37 o.a = 25; // No error thrown // (it would throw in strict mode, // even if the value had been the same) console.log(o.a); // logs 37. The assignment didn't work. // strict mode (function() { 'use strict'; var o = {}; Object.defineProperty(o, 'b', { value: 2, writable: false }); o.b = 3; // throws TypeError: "b" is read-only return o.b; // returns 2 without the line above }()); 如示例所示,试图写入非可写属性不会改变它,也不会引起错误。 Enumerable 特性 enumerable定义了对象的属性是否能够在 for...in 循环和 Object.keys() 中被枚举。 var o = {}; Object.defineProperty(o, "a", { value : 1, enumerable:true }); Object.defineProperty(o, "b", { value : 2, enumerable:false }); Object.defineProperty(o, "c", { value : 3 }); // enumerable defaults to false o.d = 4; // 若是使用直接赋值的方式建立对象的属性,则这个属性的enumerable为true for (var i in o) { console.log(i); } // 打印 'a' 和 'd' (in undefined order) Object.keys(o); // ["a", "d"] o.propertyIsEnumerable('a'); // true o.propertyIsEnumerable('b'); // false o.propertyIsEnumerable('c'); // false Configurable 特性 configurable特性表示对象的属性是否能够被删除,以及除value和writable特性外的其余特性是否能够被修改。 var o = {}; Object.defineProperty(o, "a", { get : function(){return 1;}, configurable : false } ); // throws a TypeError Object.defineProperty(o, "a", {configurable : true}); // throws a TypeError Object.defineProperty(o, "a", {enumerable : true}); // throws a TypeError (set was undefined previously) Object.defineProperty(o, "a", {set : function(){}}); // throws a TypeError (even though the new get does exactly the same thing) Object.defineProperty(o, "a", {get : function(){return 1;}}); // throws a TypeError Object.defineProperty(o, "a", {value : 12}); console.log(o.a); // logs 1 delete o.a; // Nothing happens console.log(o.a); // logs 1 若是o.a的configurable属性为true,则不会抛出任何错误,而且该属性将在最后被删除。 添加多个属性和默认值节 考虑特性被赋予的默认特性值很是重要,一般,使用点运算符和Object.defineProperty()为对象的属性赋值时,数据描述符中的属性默认值是不一样的,以下例所示。 var o = {}; o.a = 1; // 等同于 : Object.defineProperty(o, "a", { value : 1, writable : true, configurable : true, enumerable : true }); // 另外一方面, Object.defineProperty(o, "a", { value : 1 }); // 等同于 : Object.defineProperty(o, "a", { value : 1, writable : false, configurable : false, enumerable : false }); 通常的 Setters 和 Getters节 下面的例子展现了如何实现一个自存档对象。 当设置temperature 属性时,archive 数组会获取日志条目。 function Archiver() { var temperature = null; var archive = []; Object.defineProperty(this, 'temperature', { get: function() { console.log('get!'); return temperature; }, set: function(value) { temperature = value; archive.push({ val: temperature }); } }); this.getArchive = function() { return archive; }; } var arc = new Archiver(); arc.temperature; // 'get!' arc.temperature = 11; arc.temperature = 13; arc.getArchive(); // [{ val: 11 }, { val: 13 }] 或 var pattern = { get: function () { return 'I alway return this string,whatever you have assigned'; }, set: function () { this.myname = 'this is my name string'; } }; function TestDefineSetAndGet() { Object.defineProperty(this, 'myproperty', pattern); } var instance = new TestDefineSetAndGet(); instance.myproperty = 'test'; // 'I alway return this string,whatever you have assigned' console.log(instance.myproperty); // 'this is my name string' console.log(instance.myname);继承属性 继承属性节 若是访问者的属性是被继承的,它的 get 和set 方法会在子对象的属性被访问或者修改时被调用。若是这些方法用一个变量存值,该值会被全部对象共享。 function myclass() { } var value; Object.defineProperty(myclass.prototype, "x", { get() { return value; }, set(x) { value = x; } }); var a = new myclass(); var b = new myclass(); a.x = 1; console.log(b.x); // 1 这能够经过将值存储在另外一个属性中解决。在 get 和 set 方法中,this 指向某个被访问和修改属性的对象。 function myclass() { } Object.defineProperty(myclass.prototype, "x", { get() { return this.stored_x; }, set(x) { this.stored_x = x; } }); var a = new myclass(); var b = new myclass(); a.x = 1; console.log(b.x); // undefined 不像访问者属性,值属性始终在对象自身上设置,而不是一个原型。然而,若是一个不可写的属性被继承,它仍然能够防止修改对象的属性。 function myclass() { } myclass.prototype.x = 1; Object.defineProperty(myclass.prototype, "y", { writable: false, value: 1 }); var a = new myclass(); a.x = 2; console.log(a.x); // 2 console.log(myclass.prototype.x); // 1 a.y = 2; // Ignored, throws in strict mode console.log(a.y); // 1 console.log(myclass.prototype.y); // 1
兼容性问题节
重定义数组对象的 length 属性节
数组的 length 属性重定义是可能的,可是会受到通常的重定义限制。(length 属性初始为 non-configurable,non-enumerable 以及 writable。对于一个内容不变的数组,改变其 length属性的值或者使它变为 non-writable 是可能的。可是改变其可枚举性和可配置性或者当它是 non-writable 时尝试改变它的值或是可写性,这二者都是不容许的。)然而,并非全部的浏览器都容许 Array.length 的重定义。
在 Firefox 4 至 22 版本中尝试去重定义数组的 length 属性都会抛出一个 TypeError 异常。
有些版本的Chrome中,Object.defineProperty() 在某些状况下会忽略不一样于数组当前length属性的length值。有些状况下改变可写性并不起做用(也不抛出异常)。同时,好比Array.prototype.push的一些数组操做方法也不会考虑不可读的length属性。
有些版本的Safari中,Object.defineProperty() 在某些状况下会忽略不一样于数组当前length属性的length值。尝试改变可写性的操做会正常执行而不抛出错误,但事实上并未改变属性的可写性。
只在Internet Explorer 9及之后版本和Firefox 23及之后版本中,才完整地正确地支持数组length属性的从新定义。目前不要依赖于重定义数组length 属性可以起做用,或在特定情形下起做用。与此同时,即便你可以依赖于它,你也没有合适的理由这样作。
Internet Explorer 8 具体案例节
Internet Explorer 8 实现了 Object.defineProperty() 方法,但 只能在 DOM 对象上使用。 须要注意的一些事情:
尝试在原生对象上使用 Object.defineProperty()会报错。属性特性必须设置一些特定的值。对于数据属性描述符,configurable, enumerable和 writable 特性必须所有设置为 true;对于访问器属性描述符,configurable 必须设置为 true,enumerable 必须设置为 false。(?) 任何试图提供其余值(?)将致使一个错误抛出。从新配置一个属性首先须要删除该属性。若是属性没有删除,就如同从新配置前的尝试。 详情能够参考网址:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/definePropertyhttps://www.jianshu.com/p/8fe1382ba135