1、创建对象
(1)、通过
对象直接量的方式创建
说明:对象直接量是由若干名/值对组成的映射表,名/值对中间用冒火分隔,名/值对之间用逗号分隔,整个映射表用花括号括起来。
如:
var empty = {}; //创建一个空对象 var point = { x : 0, y : "ccc }
对象直接量是一个表达式,每次运算都会创建并初始化一个新的对象。
(2)、通过
new创建对象
new运算符创建并初始化一个新对象。关键字new后跟随一个函数调用。这里的函数称作构造函数(constructor),构造函数用于初始化一个新创建的对象。
如:
var o = new Object(); //创建一个空对象
(3)、
Object.create()
它创建一个新对象,其中第一个参数是这个对象的原型,第二个为可选参数,用以对对象的属性进一步描述。
它是一个静态函数,而不是提供给某个对象调用的方法。
如:
var o = Object.create({ x: 1, y: 2}); //o继承了属性x和y var o2 = Object.create(Object.prototype); //创建一个空对象
2、属性的查询和设置
当通过[]来访问对象的属性时,属性名通过字符串来表示。
使用[]运算符访问时,字符串值是动态的,可以在运行时更改。
使用 . 运算符访问时,后接的是标识符,标识符是静态的,写死在程序中。
不同的场景可以使用不同的运算符来进行访问。
(1)
继承
对属性赋值操作时首先要检查原型链,以此判定是否允许赋值。
如果一个对象继承自另一个对象的一个自读属性,那么赋值操作是不允许的。
如果允许属性赋值操作,它总是在原始对象上创建属性或对已有的属性赋值,而不会去修改原型链。
//这里使用es5 的Object.create()函数来创建对象 var o = {r: 1}; //一个用来继承的对象 var c = Object.create(o); //创建一个新对象c,c继承o对象属性r c.x = 1; c.y = 2; //c定义两个属性 c.r = 2; //c覆盖继承来的属性 console.log(o.r); //1 原型对象没有修改
(2)、
属性访问错误
在一下这些场景下给对象o设置属性p会失败:
- o中的属性p是只读的:不能给只读属性重新赋值(defineProperty()方法中有一个例外,可以对可配置的只读属性重新赋值)。
- o中的属性p是继承属性,且它是只读的:不能通过同名自有属性覆盖只读的继承属性。
- o中不存在自有属性p:o没有使用setter方法继承属性p,并且o的可扩展性是false。如果o中不存在p,而且没有setter方法可供调用,则p一定会添加至o中。但如果o不是可扩展的,那么在o中不能定义新属性。
通过delete运算符可以删除对象的属性。
delete运算符只能删除自有属性,不能删除继承属性。(要删除继承属性必须从定义这个属性的原型对象上删除它,而且这会影响到所有继承自这个原型的对象)。
delete不能删除那些可配置性为false的属性。(尽管可以删除不可扩展对象的可配置属性)
如
o = {x:1}; delete o.x; //true delete o.x; //什么也没做(x已经不存在),返回true delete o.toString; //什么也没做(toString是继承来的) ,返回true delete 1; //无意义,返回true delete Object.prototype; //不能删除,属性是不可配置的
4、检测属性
(1)、
in运算符
用于检测一个对象是否包含某个属性,如果对象的自有属性或继承属性中包含这个属性则返回true。
如:
var o = { x:1 }; "x" in o; //true "x"是o的属性 "y" in o; //false "y"不是o的属性 "toString" in o; //true o继承toString属性
(2)、
hasOwnProperty()方法
用来检测给定的名字是否是对象的自有属性,对于继承属性它将返回false。
如:
var o = { x:1 }; o.hasOwnProperty("x"); //true o有一个自有属性x o.hasOwnProperty("y"); //false o中不存在属性y o.hasOwnProperty("toString"); //false toString是继承属性
(3)、
propertyIsEnumerable()方法
它用来检测是自有属性且这个属性的可枚举性为true时它才返回true。
某些内置属性是不可枚举的,通常由javascript代码创建的属性都是可枚举的。如
var o = Object.create({ y: 2}); o.x = 1; o.propertyIsEnumerable("x"); // true: o有一个可枚举的自有属性x o.propertyIsEnumerable("y"); //false y是继承来的 Object.prototype.propertyIsEnumerable("toString"); //false 不可枚举
(4)、判断一个属性是否是undefined
使用 “ !== ”,如
var o = { x:1 }; o.x !== undefined; //true o中有属性x o.y !== undefined; //false o中没有属性y o.toString !== undefined; //true o继承了toString属性 o.z = undefined; o.z !== undefined; //false 属性存在,但值为undefined delete o.z; //删除属性 "z" in o; //false,属性不存在
5、属性getter和setter
在es5中,属性值可以用一个或两个方法代替,这个两个方法是getter和setter。由getter和setter定义的属性称做“存取器属性”。
存取器属性不具有可写性。如果属性同时具有getter和setter方法,那么它是一个读/写属性,如果只有getter方法,那么它是一个只读属性,如果它只有setter方法,那么它只有一个只写属性(数据属性中有一些例外)
getter方法:无参数,返回值是属性存取表达式的值。
setter方法:一个参数,设置属性值,
语法:
var o = { data_prop: value, //普通的数据属性 //存取器属性 get accessor_prop() { /*函数体 */ }, set accessor_prop() { /*函数体 */ } };
存取器属性是可以继承的,如
//笛卡尔点坐标对象 var p = { x: 1.0, y: 1.0, get r() { return Math.sqrt(this.x * this.x + this.y * this.y); //this指代这个点的对象,即p }, set r(newvalue) { var oldvalue = Math.sqrt(this.x * this.x + this.y * this.y); var ratio = newvalue / oldvalue; this.x *= ratio; this.y *= ratio; }, get theta() { return Math.atan2(this.y, this.x); } }; var q = Object.create(p); //创建一个继承getter和setter的新对象 q.x = 2, q.y = 1; //给q添加两个属性 console.log(q.r); //可以使用继承的存取器属性 console.log(q.theta);
6、属性的特性
数据属性包含4个特性,分别是它的值(value)、可写性(writable)、可枚举性(enumerable)、可配置性(configurable)。存取器属性不具有值特性和可写性,它们的可写性是由setter方法存在与否决定的。因此存取器属性的4个特性是读取(get)、写入(set)、可枚举性和可配置性。
通过调用
Object.getOwnPropertyDescriptor()可以获得某个对象特定属性的属性描述符(即代表4个特性)。
console.log(Object.getOwnPropertyDescriptor({ x: 1 }, "x")); //返回 {value:1, writable:true, enumerable:true, configurable:true} var random = { get octet() { return Math.floor(Math.random() * 256); }, } //查询定义的random对象的octet属性 console.log(Object.getOwnPropertyDescriptor(random, "octet")); //返回 {get: /*function函数*/, set:undefined, enumerable:true, configurable:true} //对于继承属性和不存在的属性,返回undefined console.log(Object.getOwnPropertyDescriptor({}, "x")); //undefined console.log(Object.getOwnPropertyDescriptor({}, "toString")); //undefined
设置属性的特性,或者让新建属性具有某种特性,则需要调用 Object.definePeoperty() ,传入需要修改的对象、要创建或修改的属性的名称以及属性描述符对象
var o = {}; //创建一个空对象 //添加一个不可枚举的数据属性x,并赋值为1 Object.defineProperty(o, "x", { value: 1, writable: true, enumerable: false, configurable: true }); console.log(o.x); //1 console.log(Object.keys(o)); //返回[] keys方法,返回所有可枚举的属性 //对属性x进行修改,让它变为只读 Object.defineProperty(o, "x", { writable: false }); o.x = 2; //更改属性值 console.log(o.x); //1 没有被更改,属性是只读 //属性是可配置的,因此可以通过这种方式对它进行修改 Object.defineProperty(o, "x", { value: 2 }); console.log(o.x); // 2 //将x从数据属性修改为存取器属性 Object.defineProperty(o, "x", { get: function() { return 0; } }); console.log(o.x); // 0 console.log(Object.getOwnPropertyDescriptor(o, "x")); //查看x的特性
如果需要同时修改或创建多个属性,并设置属性描述符,则需要使用Object.defineProperties()。参数一为该对象,参数二为一个映射表。如:
//首先创建一个空对象,然后给它添加两个数据属性和一个只读存取器属性。 var p = Object.defineProperties({}, { x: { value: 1, writable: true, enumerable: true, configurable: true }, y: { value: 1, writable: true, enumerable: true, configurable: true }, r: { get: function() { return Math.sqrt(this.x * this.x + this.y * this.y) }, enumerable: true, configurable: true } });
规则:
- 如果对象是不可扩展的,则可以编辑已有的自有属性,但不能给它添加新属性。
- 如果属性是不可配置的,则不能修改它的可配置性和可枚举性。
- 如果存取器属性是不可配置的,则不能修改其getter和setter方法,也不能将它转换为数据属性。
- 如果数据属性是不可配置的,则不能将它转换为存取器属性。
- 如果数据属性是不可配置的,则不能将它的可写性从false修改为true,但可以从true修改为false。
- 如果数据属性是不可配置且不可写的,则不能修改它的值。然而可配置但不可写属性的值是可以修改的(实际上是先将它标记为可写,然后修改它的值,最后转换为不可写的)。
例子: 复制属性的特性
/* *给Object.prototype添加一个不可枚举的extend()方法 这个方法继承自调用它的对象,将作为参数传入的对象的属性一一复制 除了值之外,也复制属性的所有特性,除非在目标对象中存在同名的属性 参数对象的所有自有对象(包括不可枚举的属性)也会一一复制 */ Object.defineProperty(Object.prototype, "extend", { writable: true, enumerable: false, //不可枚举 configurable: true, value: function(o) { var names = Object.getOwnPropertyNames(o); //得到所有的自有属性,包括不可枚举属性 for (var i = 0; i < names.length; i++) { if (names[i] in this) { //如果属性已经存在,则跳过 continue; } var desc = Object.getOwnPropertyDescriptor(o, names[i]); //获取o中的属性描述符 Object.defineProperty(this, names[i], desc); //用它给当前对象(this)创建一个属性 } } });
7、对象的三个属性
(1)原型属性
原型属性是在实例对象创建之初就设置好的。
通过
Object.getPrototypeOf()方法可以查询某个对象的原型
如:
function People() {} //空的构造函数 People.prototype = { //设置原型属性 constructor: People, name: "cart", } var cc = new People(); //实例化构造函数,创建一个新的对象 console.log(Object.getPrototypeOf(cc));
检测一个对象是否是另一个对象的原型,用isPrototypeOf()方法
//定义一个原型对象 var p = { x: 1 }; var o = Object.create(p); //使用原型对象创建一个对象 console.log(p.isPrototypeOf(o)); // true console.log(Object.prototype.isPrototypeOf(o)); //true o继承p,p继承Object.prototype
(2)类属性
对象的类属性是一个字符串,用以表示对象的类型信息。
通过toString()方法可以间接的查询到这个对象的类属性。返回如下格式的字符串:
[ object class ]
因此,要想获得对象的类,可以调用对象的toString()方法,然后提取已返回字符串的第8个到倒数第二个位置之间的字符。
如
// 定义一个获取对象类属性的函数 function classof(o) { if (o === null) { return "Null"; } if (o === undefined) { return "Undefined"; } return Object.prototype.toString.call(o).slice(8, -1); //间接调用toString方法返回类属性 } classof(1); // NUmber function f() {}; //定于一个自定义构造函数 classof(new f()); // Object
(3)、可扩展性
- 对象的可扩展性用以表示是否可以给对象添加新属性。可以通过将对象传入Object.isExtensible(),来判断该对象是否是可扩展的。
-
如果想将对象转换为不可扩展的,需要调用Object.preventExtensions(),将待转换的对象作为参数传进去。
对象一旦转换为不可扩展,就无法再将其转换为可扩展的。给一个不可扩展的对象的原型添加属性,这个不可扩展的对象同样会继承这些新属性。
var o = { x: 1, y: { z: [false, null, ""] } }; console.log(Object.isExtensible(o)); //true Object.preventExtensions(o); //将对象转换为不可扩展 console.log(Object.isExtensible(o)); //false
-
Object.seal(Object);它除了可以设置对象的不可扩展,还可以设置对象的自有属性都设置为不可配置的,不能删除和配置。对于它已经有的可写属性依然可以设置。
-
Object.isSealed(Object); 检查对象是否封闭。
-
Object.freeze();更严格的锁定对象(冻结)。除了将对象设置为不可扩展,属性设置为不可配置,所有的自有属性设置为只读的,(如果对象存储器属性有setter方法,存储器属性不受影响,依然可以通过属性赋值给他们)。
- Object.isFrozen() 来检测对象是否冻结。
8、序列化对象
对象序列化(serialization)是指将对象的状态转换为字符串,也可以将字符串还原为对象。
使用函数JSON.stringify()和JSON.parse()用来序列化和还原javascript对象。
// 定义一个测试对象 var o = { x: 1, y: { z: [false, null, ""] } }; var s = JSON.stringify(o); var p = JSON.parse(s); console.log(s); //解析为json格式字符串 {"x":1,"y":{"z":[false,null,""]}} console.log(p); //将字符串转换为javascript对象
函数、RegExp、Error对象和undefined值不能序列化和还原。JSON.stringify()只能序列化对象可枚举的自有属性。