JavaScript 对象 - 与属性的相关知识
function inherit(p){
if(p == null) throw TypeError();
if(Object.create)
return Object.create(p);
var t = typeof p;
if(t !== "object" && t !== "function") throw TypeError();
function f(){};
f.prototype = p ;
return new f();
};
创建对象
对象创建的三种方式:
对象自面量、构造函数、ECMAScript5中引入的 Object.create()
自面量的方式:
var o = {name:"wj",age:"20"};
构造函数:
function FunctionName(){}
Object.create()
Object.create(proto, [ propertiesObject ])
proto 一个对象,作为新创建对象的原型。
propertiesObject
可选。该参数对象是一组属性与值,该对象的属性名称将是新创建的对象的属性名称,值是属性描述符(这些属性描述符的结构与Object.defineProperties()的第二个参数一样)。
注意:该参数对象不能是 undefined,另外只有该对象中自身拥有的可枚举的属性才有效,也就是说该对象的原型链上属性是无效的。
如果 proto 参数不是 null
或一个对象值,则抛出一个 TypeError
异常。
实例一:
//Shape - superclass
function Shape() {
this.x = 0;
this.y = 0;
}
Shape.prototype.move = function(x, y) {
this.x += x;
this.y += y;
console.info("Shape moved.");
};
// Rectangle - subclass
function Rectangle() {
Shape.call(this); //call super constructor.
}
Rectangle.prototype = Object.create(Shape.prototype);
var rect = new Rectangle();
rect instanceof Rectangle //true.
rect instanceof Shape //true.
rect.move(1, 1); //Outputs, "Shape moved."
实例二: proto属性与 propertiesObject 结合使用
var o;
// 创建一个原型为null的空对象
o = Object.create(null);
o = {};
// 以字面量方式创建的空对象就相当于:
o = Object.create(Object.prototype);
o = Object.create(Object.prototype, {
// foo会成为所创建对象的数据属性
foo: { writable:true, configurable:true, value: "hello" },
// bar会成为所创建对象的访问器属性
bar: {
configurable: false,
get: function() { return 10 },
set: function(value) { console.log("Setting `o.bar` to", value) }
}})
function Constructor(){}
o = new Constructor();
// 上面的一句就相当于:
o = Object.create(Constructor.prototype);
// 当然,如果在Constructor函数中有一些初始化代码,Object.create不能执行那些代码
// 创建一个以另一个空对象为原型,且拥有一个属性p的对象
o = Object.create({}, { p: { value: 42 } })
// 省略了的属性特性默认为false,所以属性p是不可写,不可枚举,不可配置的:
o.p = 24
o.p
//42
o.q = 12
for (var prop in o) {
console.log(prop)
}
//"q"
delete o.p
//false
//创建一个可写的,可枚举的,可配置的属性p
o2 = Object.create({}, { p: { value: 42, writable: true, enumerable: true, configurable: true } });
属性的查询和设置
查询与设置两种方式: . 或 [] 方式
对象给属性赋值时,首先会检查原型链上是否允许赋值操作。 如果 o 继承来的x是一个只读属性,那么是不允许赋值的,严格模式下会报错。 如果允许的话,只会在原对象上添加或修改,不会去修改原型链。
案例一:通过字面方式定义属性,默认属性特性是: 可配置、可枚举、可读/可写
var person = {name:"wj"};
var o = inherit(person);
o.name = "wjj";
console.log(o.name); // wjj
console.log(person.name); //wj
案例二:defineProperty 修改 name 的特性时
var person = {name:"wj"};
Object.defineProperty(person,"name",{
writable:false
})
var sub = inherit(person);
sub.name = "wjj"; //TypeError: Cannot assign to read only property 'name' of object '#<Object>'
console.log(sub.name);
console.log(person.name);
注意:1:JavaScript中,只有在查询属性时才会体会到继承的存在,在设置属性则和继承无关,这是JavaScript的一个特性,该特性让程序员可以有选择地覆盖(override)继承的属性。
2:属性的赋值要么失败,要么创建一个属性,要么在原始对象中设置属性。
删除属性
delete 操作符 删除自有属性 并且是 configable:true;
delete 操作符只能删除自有属性,而不能删除继承属性(如果要删除继承属性必须通过原型对象来删除,删除同时所有继承该原型的对象都会受到影响)
检测属性 与 枚举属性
存取器属性 setter getter
基本组成
enumerable
configurable
set
get
注意:存取器属性不具有可写性(writeable ), 如果属性同时拥有 set、get 则其可写、可读, 如果只 “set” 可写 如果只添加了 “get” 可读;
属性的特性
属性除了包含名字和值之外,属性还包含一些标识它们可写、可枚举、可配置的特性。在ECMAScript3无法设置这些特性,其属性都是可读可写、可枚举、可配置的根本没有提供API供修改。
在ECMAScript5中引入了 查询 和 修改这些属性的API。
这些给库开发者带来好处是:
1 可以通过这些API给原型对象添加方法,并设置其不可枚举,这让其看起来像内置方法
2 通过API设置属性不能修改和删除。
ECMAScript5为了获取和设置这些特性,引入描述对象,而描述对象获取通过 Object.getOwnPropertyDescriptor;
根据属性类型不同,描述对象返回的内容也不一样:
数据属性: 值 value 读\写 writeable、枚举 enumerable、可配置 configurable
存取器属性: set*、get*、枚举 enumerable、可配置 configurable
先来了解下 getOwnPropertyDescriptor
语法:
/**
* Gets the own property descriptor of the specified object.
* An own property descriptor is one that is defined directly on the object and is not inherited from the object's prototype.
* @param o Object that contains the property.
* @param p Name of the property.
*/
getOwnPropertyDescriptor(o: any, p: string): PropertyDescriptor;
从指定对象,获取自身属性描述对象; 从名字看出来,只能返回自生的属性,而继承而来的属性则只能通过获取其原型对象了
下面看看具体实例
var o = {name:"HD"};
Object.defineProperty(o,"age",{
configurable:true,
enumerable:true,
set:function(age){
this.age = age;
},
get:function(){
return this.age;
}
});
var oDesc_name = Object.getOwnPropertyDescriptor(o,"name");
var oDesc_age = Object.getOwnPropertyDescriptor(o,"age");
var oDesc_money = Object.getOwnPropertyDescriptor(o,"toString");
var none = Object.getOwnPropertyDescriptor(o,"none");
// { get: [Function: get],set: [Function: set],enumerable: true,configurable: true }
console.log(oDesc_age);
// { value: 'HD',writable: true,enumerable: true, configurable: true }
console.log(oDesc_name);
// undefined
console.log(oDesc_money);
// undefined
console.log(none);
如果要设置或修改描述对象通过 Object.defineProperty 和 Object.defineProperties 前者是单个,后者为批量;这两个区别是后者属性是同时添加的。
先了解下 Object.defineProperty 语法
/**
* Adds a property to an object, or modifies attributes of an existing property.
* @param o Object on which to add or modify the property. This can be a native JavaScript object (that is, a user-defined object or a built in object) or a DOM object.
* @param p The property name.
* @param attributes Descriptor for the property. It can be for a data property or an accessor property.
*/
defineProperty(o: any, p: string, attributes: PropertyDescriptor): any;
Object.defineProperty 添加或者修改自有属性,不能修改继承来的属性
1 var o = {name:"HD"};
2 //添加
3 Object.defineProperty(o,"custom",{
4 writable:true,
5 configurable:true,
6 enumerable:true,
7 value:"我是先添加的"
8 });
9 console.log(Object.getOwnPropertyDescriptor(o,"custom"));
10 //修改
11 Object.defineProperty(o,"custom",{
12 writable:true,
13 configurable:true,
14 enumerable:true,
15 value:"被修改了 这么神奇"
16 });
17
18 console.log(Object.getOwnPropertyDescriptor(o,"custom"));
{ value: '我是先添加的',writable: true,enumerable: true,configurable: true }
{ value: '被修改了 这么神奇', writable: true, enumerable: true,configurable: true }
一种特殊情况,当属性configurable 只能从 true -> false ,当configurable为false时 除了writable可以被修改外 其它特新均不能被更改
1 var o = {name:"HD"};
2 //添加
3 Object.defineProperty(o,"custom",{
4 writable:true,
5 configurable:false,
6 enumerable:true,
7 value:"我是先添加的"
8 });
9 //{ value: '我是先添加的',writable: true,enumerable: true,configurable: false }
10 console.log(Object.getOwnPropertyDescriptor(o,"custom"));
11 //修改 configurable
12 Object.defineProperty(o,"custom",{
13 writable:true,
14 configurable:true, //TypeError: Cannot redefine property: custom
15 enumerable:true,
16 value:"被修改了 这么神奇"
17 });
18 //修改 enumerable
19 Object.defineProperty(o,"custom",{
20 writable:true,
21 configurable:false,
22 enumerable:false,//TypeError: Cannot redefine property: custom
23 value:"被修改了 这么神奇"
24 });
25 //修改 writable
26 Object.defineProperty(o,"custom",{
27 writable:false,
28 configurable:false,
29 enumerable:true,
30 value:"被修改了 这么神奇"
31 });
32
33 console.log(Object.getOwnPropertyDescriptor(o,"custom"));
34
35 // { value: '被修改了 这么神奇',writable: false,enumerable: true,configurable: false }
当需要一次添加或修改多个属性时,我们可能里用 defineProperties() 先来看看语法
/**
* Adds one or more properties to an object, and/or modifies attributes of existing properties.
* @param o Object on which to add or modify the properties. This can be a native JavaScript object or a DOM object.
* @param properties JavaScript object that contains one or more descriptor objects. Each descriptor object describes a data property or an accessor property.
*/
defineProperties(o: any, properties: PropertyDescriptorMap): any;
先看看下面实例:
1 var now = Object.defineProperties({},{
2 "custom":{
3 writable:false,
4 configurable:false,
5 enumerable:true,
6 value:"被修改了 这么神奇"
7 },
8 "custom2":{
9 writable:false,
10 configurable:false,
11 enumerable:true,
12 value:"被修改了 这么神奇"
13 }
14 });
15
16 console.log(now); //{ custom: '被修改了 这么神奇', custom2: '被修改了 这么神奇' }
在不允许创建或修改的属性来说,如果用 Object.defineProperty()和 Object.defineProperties()会抛出错误!!!!
问题汇总:
1 如果对象不可配置,可以编辑已有自有属性,但是不能添加新属性;
2 当属性configurable 只能从 true -> false ,当configurable为false时 除了writable可以被修改外 其它特新均不能被更改;
3 当属性不可配置时,数据属性 存取器属性 间不能相互转换;
4 当数据属性不可写时,修改其值会报错;