ECMASsript中有两种属性:数据属性和访问器属性
一、属性讲解
- 数据属性
数据属性包含一个数据值的位置,在这个位置可以读取和写入值,其中有4个描述其行为的特性:
-
Configurable:表示能否通过delete删除此属性,能否修改属性的特性,或能否把属性修改为访问器属性;如果直接使用字面量定义对象,默认值为true
-
Enumerable:表示该属性是否可枚举,即是否通过for-in循环或Object.keys()返回属性;如果直接使用字面量定义对象,默认值为true
-
Writable:能否修改属性的值;如果直接使用字面量定义对象,默认值为true
-
Value:该属性对应的值。读取属性时,从这个位置读;写入属性时,把新值保存在这个位置。默认为undefined
- 访问器属性
-
Configurable:和数据属性的 [[Configurable]] 一样,表示能否通过delete删除此属性,能否修改属性的特性,或能否修改把属性修改为访问器属性,如果直接使用字面量定义对象,默认值为true
-
Enumerable:和数据属性的 [[Configurable]] 一样,表示该属性是否可枚举,即是否通过for-in循环或Object.keys()返回属性,如果直接使用字面量定义对象,默认值为true
-
Get:一个给属性提供 getter 的方法(访问对象属性时调用的函数,返回值就是当前属性的值),如果没有 getter 则为 undefined。该方法返回值被用作属性值。默认为 undefined
-
Set:一个给属性提供 setter 的方法(给对象属性设置值时调用的函数),如果没有 setter 则为 undefined。该方法将接受唯一参数,并将该参数的新值分配给该属性。默认为 undefined
注:访问器属性不能直接定义,必须使用Object.defineProperty()来定义
var book = {
_year: 2004,
edition: 1
};
Object.defineProperty(book, "year", {
get: function () {
return this._year;
},
set: function (newValue) {
if (newValue > 2004) {
this._year = newValue;
this.edition += newValue - 2004;
}
}
});
book.year = 2005;
alert(book.edition); //2
二、创建/修改/获取属性的方法
- Object.defineProperty()
-
功能:方法会直接在一个对象上定义一个新属性,或修改一个对象的现有属性, 并返回这个对象。
-
语法: Object.defineProperty(obj, prop, descriptor)
obj:属性所在的对象
prop:目标对象需要定义或修改的属性的名称
descriptor:将被定义或修改的属性的描述符
var obj = new Object();
Object.defineProperty(obj, 'name', {
configurable: false,
writable: true,
enumerable: true,
value: '张三'
})
console.log(obj.name) //张三
- Object.defineProperties()
-
功能:方法直接在一个对象上定义一个或多个新的属性或修改现有属性,并返回该对象。
-
语法: Object.defineProperties(obj, props)
obj:将要被添加属性或修改属性的对象
props:该对象的一个或多个键值对定义了将要为对象添加或修改的属性的具体配置
var obj = new Object();
Object.defineProperties(obj, {
name: {
value: '张三',
configurable: false,
writable: true,
enumerable: true
},
age: {
value: 18,
configurable: true
}
})
console.log(obj.name, obj.age) // 张三, 18
- Object.getOwnPropertyDescriptor()
-
功能:该方法返回指定对象上一个自有属性对应的属性描述符。(自有属性指的是直接赋予该对象的属性,不需要从原型链上进行查找的属性)
-
语法:Object.getOwnPropertyDescriptor(obj, prop)
obj:需要查找的目标对象
prop:目标对象内属性名称
var person = {
name: '张三',
age: 18
}
var desc = Object.getOwnPropertyDescriptor(person, 'name');
console.log(desc);
// 结果如下
// {
// configurable: true,
// enumerable: true,
// writable: true,
// value: "张三"
// }
- Object. getOwnPropertyDescriptors()
-
功能:所指定对象的所有自身属性的描述符,如果没有任何自身属性,则返回空对象。
-
语法:Object.getOwnPropertyDescriptors(obj)
obj: 需要查找的目标对象
var person = {
name: '张三',
age: 18
}
var desc = Object.getOwnPropertyDescriptors(person);
console.log(desc);
三、各种场景下描述符属性的的扩展示例
- 在调用Object.defineProperty()方法创建一个新的属性时,如果不指定,configurable, writable, enumerable 特性的默认值为false;如果不指定value, get, set,则这些属性默认值为undefined
var book = {
_year: 2004,
edition: 1
};
Object.defineProperty(book,"name",{
value:"vuejs"
});
var descipt=Object.getOwnPropertyDescriptor(book,"name");
console.log(descipt);
- Configurable
在对象中添加一个数据描述符属性
- 将Configurable设置为false,表示不能从对象中删除属性,如果对这个属性调用delete,则在非严格模式下什么也不会发生,而在严格模式下回导致错误。
var person = {};
Object.defineProperty(person, 'name', {
configurable: false,
value: 'John'
}) ;
delete person.name; // 严格模式下抛出错误
console.log(person.name); //John
- 一旦把属性定义为不可配置的,就不能再把它变回可配置的
var person = {};
Object.defineProperty(person, 'name', {
configurable: false,
value: 'John'
}) ;
Object.defineProperty(person, 'name', {
configurable: true //报错
});
- 把属性定义为不可配置的,此时,在调用Object.defineProperty()方法修改特性时,都会导致错误
var person = {};
Object.defineProperty(person, 'name', {
configurable: false,
value: 'John'
}) ;
Object.defineProperty(person, 'name', {
configurable: true //报错
});
Object.defineProperty(person, 'name', {
enumerable: 2 //报错
});
Object.defineProperty(person, 'name', {
writable: true //报错
});
Object.defineProperty(person, 'name', {
value: 2 //报错
});
- 以上是最开始定义属性描述符时,writabl默认为false,才会出现上述效果。如果writable定义为true,则可以修改[[writable]]和[[value]]属性值,修改另外两个属性值报错
var obj = {};
Object.defineProperty(obj, 'a', {
configurable: false,
writable: true,
value: 1
});
Object.defineProperty(obj, 'a', {
// configurable: true, //报错
// enumerable: true, //报错
writable: false,
value: 2
});
var d = Object.getOwnPropertyDescriptor(obj, 'a');
console.log(d);
在对象中添加存取描述符属性
var obj = {};
var aValue; //如果不初始化变量, 不给下面的a属性设置值,直接读取会报错aValue is not defined
var b;
Object.defineProperty(obj, 'a', {
configurable : true,
enumerable : true,
get: function() {
return aValue
},
set: function(newValue) {
aValue = newValue;
b = newValue + 1
}
});
console.log(b); // undefined
console.log(obj.a); // undefined, 当读取属性值时,调用get方法,返回undefined
obj.a = 2; // 当设置属性值时,调用set方法,aValue为2
console.log(obj.a); // 2 读取属性值,调用get方法,此时aValue为2
console.log(b); // 3 再给obj.a赋值时,执行set方法,b的值被修改为2 vue中的计算属性就是利用setter来实现的
全局环境下
只有使用 var,let 等操作符才是定义变量;而不使用 var,直接a=1,这样 a 的含义为 window 的一个属性,并不是我们所说的变量的概念。使用 var 定义的任何变量,其 configurable 属性值都为 false,定义对象也是一样。
- 不使用var定义的变量(相当于定义了window的一个属性,默认值均为true):
a = 1; //a相当于window的一个属性, window.a
var d = Object.getOwnPropertyDescriptor(window, 'a');
console.log(d);
- 使用 var 定义的任何变量(其 configurable 属性值都为 false):
var a = 1; // a属于window, 相当于window.a
var d = Object.getOwnPropertyDescriptor(window, 'a');
console.log(d);
- 使用var定义的变量(其 configurable 属性值都为 false):
var b = {
name: 'bbb'
};
var d = Object.getOwnPropertyDescriptor(window, 'b');
console.log(d);
使用字面量定义的对象,该对象内部的属性的数据描述符属性都为true:
var b = {
name: 'bbb'
};
var d = Object.getOwnPropertyDescriptor(b, 'name');
console.log(d);
- Writable
当writable为false(并且configrubale为true),[[value]]可以通过defineProperty修改,但不能直接赋值修改
- 通过defineProperty修改(可以):
var obj = {};
Object.defineProperty(obj, 'a', {
configurable: true,
enumerable: false,
writable: false,
value: 1
});
Object.defineProperty(obj, 'a', {
configurable: false,
enumerable: true,
writable: false ,
value: 2
});
var d = Object.getOwnPropertyDescriptor(obj, 'a')
console.log(d); // 结果如下
// {
// value: 2,
// writable: false,
// enumerable: true,
// configurable: false
// }
- 直接赋值修改(不可以):
var obj = {}
Object.defineProperty(obj, 'a', {
configurable: true,
enumerable: false,
writable: false,
value: 1
});
obj.a=2;
var d = Object.getOwnPropertyDescriptor(obj, 'a')
console.log(d); // 结果如下
// {
// value: 1, // 没有做出修改
// writable: false,
// enumerable: true,
// configurable: false
// }
- Enumerable
var obj = {};
Object.defineProperties(obj, {
a: {
value: 1,
enumerable: false
},
b: {
value: 2,
enumerable: true
},
c: {
value: 3,
enumerable: false
}
})
obj.d = 4;
//等同于
//Object.defineProperty(obj, 'd', {
// configurable: true,
// enumerable: true,
// writable: true,
// value: 4
//})
for(var key in obj) {
console.log(key);
// 打印一次b, 一次d, a和c属性enumerable为false,不可被枚举
}
var arr = Object.keys(obj);
console.log(arr); // ['b', 'd']
- get和set
简易的数据双向绑定
<body>
<p>input1=><input type="text" id="input1"></p>
<p>input2=><input type="text" id="input2"></p>
<div>我每次比input1的值加1=><span id="span"></span></div>
<script>
var oInput1 = document.getElementById('input1');
var oInput2 = document.getElementById('input2');
var oSpan = document.getElementById('span');
var obj = {};
Object.defineProperties(obj, {
val1: {
configurable: true,
get: function() {
oInput1.value = 0;
oInput2.value = 0;
oSpan.innerHTML = 0;
return 0
},
set: function(newValue) {
oInput2.value = newValue;
oSpan.innerHTML = Number(newValue) ? Number(newValue) : 0
}
},
val2: {
configurable: true,
get: function() {
oInput1.value = 0;
oInput2.value = 0;
oSpan.innerHTML = 0;
return 0
},
set: function(newValue) {
oInput1.value = newValue;
oSpan.innerHTML = Number(newValue)+1;
}
}
});
oInput1.value = obj.val1;
oInput1.addEventListener('keyup', function() {
obj.val1 = oInput1.value;
}, false);
oInput2.addEventListener('keyup', function() {
obj.val2 = oInput2.value;
}, false);
</script>
</body>
在 input1 中输入Number类型的值时,实现input2与span中的值数据双向,且值一样:
在 input2 中输入Number类型的值时,实现input1与span中的值数据双向,且input1的值与input2的值相同,span的值每次比input1的值加1:
总结:
-
在调用Object.defineProperty()方法创建一个新的属性时,如果不指定,configurable, writable, enumerable 特性的默认值为false。
-
直接在对象上定义的属性,它们的configurable, writable, enumerable 特性的默认值为true,而value被设置为指定的值。
-
在全局环境下:不使用 var,直接a=1,这样 a 的含义为 window 的一个属性,并不是我们所说的变量的概念,它们的configurable, writable, enumerable 特性的默认值为true。只有使用 var,let 等操作符才是定义变量,使用 var 定义的任何变量,其 configurable 属性值都为 false,定义对象也是一样。
-
当writable为false(并且configrubale为true),[[value]]可以通过defineProperty修改,但不能直接赋值修改