JavaScript对象
2016-05-14 18:12
441 查看
对象是一种复合值:它将很多值(原始值或其他对象)聚合在一起,可通过名字访问这些值。
对象也可看做是属性的无序集合,每个属性都是一个名/值对。
除了包含属性之外,每个对象还拥有3个特性(object attribute):
对象的原型(prototype):指向另外一个原型对象,本对象的属性继承自它的原型对象。
对象的类(class):一个标识对象类型的字符串。
对象的扩展标记(extensible flag):指明是否可以向该对象添加新属性。
JavaScript中包含了3类对象和2类属性:
内置对象(native object):由ECMAScript规范定义的对象或类。如,Array, function, Date, RegExp等。
宿主对象(host object): 由JavaScript解释器所嵌入的宿主环境(如Wbd浏览器)定义的。如,表示网页结构的HTMLElement对象。
自定义对象(user-defined object):由运行中的JavaScript代码创建的对象。
继承属性(inherited property):在对象的原型中定义的属性。
自有属性(own property):直接在对象中定义的属性。
对象直接量是一个表达式,这个表达式的每次运算都创建并初始化一个新的对象。
可以通过任意原型创建新对象,如:
inherit()并不能代替Object.create(),它不能传入null原型来创建对象,也不能接收第二个参数。
假设要查询对象o的属性x,如果o中不存在x,那么将会继续在o的原型对象中查询,直接找到x或者找到一个原型是null的对象为止。
但是,如果对象不存在,那么试图查询这个不存在对象的属性就会报错。
有一此属性是只读的,不能重新赋值,但是设置属性的失败操作不会报错。
下面提供两种避免出错的方法:
delete 只能删除自有属性,不能删除继承属性(要删除继承属性必须从定义这个属性的原型对象上删除它,而且这会影响所有继承的对象)
delete 不能删除那么可配置性为false的属性
其中propertyIsEnumerable()是hasOwnProperty()的增强版,保有检测到是自有属性并且这个属性是可枚举时才返回true。
除了使用in运算符之外,还有更简便的方法(!== undefined):
然而有一种场景这种方法是不可行的:属性值被显式地定义为undefined。
注:还有一种情况是,这个属性只有setter方法,而没有getter方法。
和数据属性不同,存取器属性不具有可写性。如果属性同时具有getter和setter方法,那么它是一个读/写属性;如果只有getter方法,则是一个只读属性;如果只有setter方法,则是一个只写属性,读取时返回undefined。
和数据属性一样,存取器属性是可以继承的。
定义存取器属性(使用直接量语法)的语法如下:
可通过Object.getOwnPropertyDescriptor()获取属性描述符:
Object.defineProperty()用于设置属性的特性:
一个更完善的属性扩展属性函数extend():
没有原型的对象为数不多,Object.prototype就是其中之一。它不继承任何属性。
使用isPrototypeOf()进行原型判断,判断在原型链(prototype chain)上进行,如:
默认的toString()方法(继承自Object.prototype)返回了如下这种格式的字符串:
很多对象继承的toString()方法重写了,为了能调用正确的toString()方法,必须间接地调用Function.call()方法。
通过对象直接量、new构造函数或者Object.create()这3种方式创建的对象,类属性都是”Object”。
所有的内置对象和自定义对象都是显式可扩展的,宿主对象的可扩展性由JavaScript引擎定义的。
JSON.stringify()只能序列化对象可枚举的自有属性。
Number、String、Boolean、null、对象、数组都可以序列化和还原。NaN、Infinity和-Infinity序列化结果是null。
Date对象序列化的结果是ISO格式的日期字符串(参照Date.toJSON()函数),但JSON.parse()依然保留它们的字符串形态。
函数、RegExp、Error对象和undefined值不能序列化和还原(没有定义toJSON()方法)。
例如,数组转换为字符串时,结果是一个数组元素列表;当函数转换为字符串时,得到函数的源代码。
Date和Number类对toLocaleString()方法做了定制,可以用它对数字、日期和时间做本地化的转换。
如果在待序列化的对象中存在这个方法,则调用它,返回值即是序列化的结果,而不是原始的对象。
对象也可看做是属性的无序集合,每个属性都是一个名/值对。
除了包含属性之外,每个对象还拥有3个特性(object attribute):
对象的原型(prototype):指向另外一个原型对象,本对象的属性继承自它的原型对象。
对象的类(class):一个标识对象类型的字符串。
对象的扩展标记(extensible flag):指明是否可以向该对象添加新属性。
JavaScript中包含了3类对象和2类属性:
内置对象(native object):由ECMAScript规范定义的对象或类。如,Array, function, Date, RegExp等。
宿主对象(host object): 由JavaScript解释器所嵌入的宿主环境(如Wbd浏览器)定义的。如,表示网页结构的HTMLElement对象。
自定义对象(user-defined object):由运行中的JavaScript代码创建的对象。
继承属性(inherited property):在对象的原型中定义的属性。
自有属性(own property):直接在对象中定义的属性。
对象的创建
直接量创建对象
对象直接量是同若干名/值对组成的映射表,名/值对中间用冒号分隔,名/值对之间用逗号分隔,整个映射表用花括号括起来。var empty = {}; // 没有任何属性的对象 var point = { x:0, y:0 }; // 包含2个属性的对象 var point2 = { x:point.x, y:point.y+1 } // 使用对象的值来赋值属性的值 var book = { "main title":"JavaScript", // 属性名字里有空格,必须用引号 "sub-title":"The Definitive Guide", // 属性名字里有连字符,必须用引号 "for":"all audiences", // "for"是保留字,必须使用引号 author:{ // 对象属性 firstname:"David", surname:"Flanagan" } }
对象直接量是一个表达式,这个表达式的每次运算都创建并初始化一个新的对象。
通过new创建对象
new运算符创建并初始化一个新对象,关键字new后面跟随一个构造函数(constructor)调用。var o = new Object(); // 创建一个空对象,和{}一样(只包含Object.prototype) var a = new Array(); // 创建一个空数组,和[]一样 var d = new Date(); // 创建一个表示当前时间的Date对象 var r = new RegExp("js"); // 创建一个可以模式匹配的RegExp对象
通过Object.creat()创建对象
Object.creat()是一个静态函数,其中第一个参数是待创建对象的原型,第二个参数可选,用以对对象的属性进行进一步描述。var o1 = Object.create(Object.property); // 创建一个空对象,同{}、new Object()一样 var o2 = Object.create({x:1, y:2}); // 创建一个新对象,并继承了属性x和y var o3 = Object.creat(null); // 创建一个对象,但不继承任何属性及方法
可以通过任意原型创建新对象,如:
// inherit()返回一个继承自原型对象p属性的新对象 function inherit(p) { // p是一个对象,但不能是null if(p == null) throw TypeError(); // 如果Object.create()存在,则直接使用它来创建对象 if(Object.create) return Object.create(p) var t = typeof p; if(t !== "object" && t !== "function") throw TypeError(); // 通过prototype属性创建对象 function f() {} f.prototype = p; return new f(); }
inherit()并不能代替Object.create(),它不能传入null原型来创建对象,也不能接收第二个参数。
属性相关操作
属性继承
通过继承创建的新对象,对新对象的操作并不会影响到原型对象。var a = { r:1 }; var b = inherit(a); b.x = 1; b.y = 1; // 新对象增加2个属性 b.r = 2; // 修改新对象的继承属性 a.r; // => 1,原型对象的属性不会受影响
假设要查询对象o的属性x,如果o中不存在x,那么将会继续在o的原型对象中查询,直接找到x或者找到一个原型是null的对象为止。
属性访问错误
查询一个不存在的属性并不会报错。book.subtitle; // => undefined:属性不存在,但不会报错
但是,如果对象不存在,那么试图查询这个不存在对象的属性就会报错。
book.subtitle.length; // undefined没有length属性,抛出一个类型错误异常
有一此属性是只读的,不能重新赋值,但是设置属性的失败操作不会报错。
Object.prototype = 0; // => Object原型是只读的,赋值失败,但不会报错
下面提供两种避免出错的方法:
// 一种冗余但很易懂的方法 var len = undefined; if(book) { if(book.subtitle) len = book.subtitle.length; } // 一种更简练的方法,len被赋值为undefined或length var len = book && book.subtitle && book.subtitle.length;
删除属性
delete只是断开属性和宿主对象的联系,而不会去操作属性中的属性a = { p:{x:1} }; b = a.p; delete a.p; b.x; // => 1,已经删除的属性引用(b)依然存在
delete 只能删除自有属性,不能删除继承属性(要删除继承属性必须从定义这个属性的原型对象上删除它,而且这会影响所有继承的对象)
delete 不能删除那么可配置性为false的属性
var x = 1; // 使用var定义的全局变量 delete this.x; // 不能删除,可配置性为false this.y = 1; // 不使用var定义的全局变量 delete y; // 可删除,可配置性为true
检测属性
可通过in运算符、hasOwnProperty()和propertyIsEnumerable()来判断某个属性是否存在于某个对象中。其中propertyIsEnumerable()是hasOwnProperty()的增强版,保有检测到是自有属性并且这个属性是可枚举时才返回true。
var o = { x: 1 }; "x" in o; // true,x是o的属性 "y" in o; // false,y不是o的属性 "toString" in o; // true,【继承属性】(方法) o.hasOwnProperty("x"); // true,x是【自有属性】 o.hasOwnProperty("y"); // false o.hasOwnProperty("toString"); // false,toString是【继承属性】
除了使用in运算符之外,还有更简便的方法(!== undefined):
var o = { x: 1 }; o.x !== undefined; // true o.y !== undefined; // false o.toStirng !== undefined; // true
然而有一种场景这种方法是不可行的:属性值被显式地定义为undefined。
var o = { x: undefined }; o.x !== undefined; // false,属性实际上是存在的!!!
注:还有一种情况是,这个属性只有setter方法,而没有getter方法。
枚举属性
for/in循环可遍历对象中所有可枚举的属性(包括自有属性和继承属性),对象的内置方法是不可枚举的,但在代码中给对象添加的属性是可枚举的。// 把p中的可枚举属性复制到o中,并返回o // 如果o和p有同名属性,则会覆盖o中的属性 // 这个函数不处理getter和setter以及复制属性 function extend(o, p) { // 遍历p中所有可枚举属性 for(prop in p) { o[prop] = p[prop]; } return o; }
存取器属性(getter & setter)
在ECMAScript 5中,属性值可以用一个或两个方法替代,这两个方法就是getter和setter。和数据属性不同,存取器属性不具有可写性。如果属性同时具有getter和setter方法,那么它是一个读/写属性;如果只有getter方法,则是一个只读属性;如果只有setter方法,则是一个只写属性,读取时返回undefined。
和数据属性一样,存取器属性是可以继承的。
定义存取器属性(使用直接量语法)的语法如下:
var p = { // 普通的数据属性 x: 1.0, y: 1.0, // 存取器属性 get r() { return Match.sqrt(this.x*this.x + this.y*this.y); } , // 不需要function声明,并且在最后添加“,” set r(newvalue) { this.x = newvalue; this.y = newvalue; } , get theta() { return Match.atan2(this.y, this.x); } } 代码中定义了可读写属性r和只读属性theta。
属性的特性
除了包含名字和值之外,属性还包含可写、可枚举和可配置的特性。属性类型 | 属性特性 |
---|---|
数据属性 | 值(value)、可写性(writable)、可枚举性(enumerable)、可配置性(configurable)。 |
存取器属性 | 存取(get)、写入(set)、可枚举性和可配置性。 |
// 返回{value: 1, writable:true, enumerable:true, configurable:true} Object.getOwnPropertyDescriptor({x:1}, "x"); var random = { get octet() { return 1; } } // 返回{get:/*func*/, set:undefined, enumerable:true, configurable:true} Object.getOwnPropertyDescriptor(random, "octet"); // 对于继承属性和不存在的属性,返回undefined Object.getOwnPropertyDescriptor({}, "x"); // undefined,没有这个属性 Object.getOwnPropertyDescriptor({}, "toStirng"); // undefined,继承属性
Object.defineProperty()用于设置属性的特性:
var o = {}; // 创建一个空对象 // 添加一个属性x,并赋值为1 Object.defineProperty(o, "x", { value: 1, writable: false, enumerable: false, configurable: true } ); o.x = 2; // 操作失败,属性不可写 // 可通过重新设置属性特性来改变属性的值 Object.defineProperty(o, "x", {value: 2}); o.x; // => 2 // 将数据属性修改存取器属性 Object.defineProperty(o, "x", { get: function() { return 3; }); o.x; // => 3
前提 | 特性 |
---|---|
对象不可扩展 | 可以编辑已有属性,但不能添加新属性 |
属性不可配置 | 不能修改它的可配置性和可枚举性 |
存取器属性不可配置 | 不能修改其getter和setter方法,也不能将它转换为数据属性 |
数据属性不可配置 | 则不能将它转换为存取器属性;不能修改它的可写性false=>true,但可以修改true=>false |
数据属性不可配置且不可写 | 不能修改属性的值 |
数据属性可配置不可写 | 可以修改属性的值(实际上先修改属性为可写,然后修改值) |
Object.defineProperty(Object.prototype, "extend", { writable: true, enumerable: false, configurable: true, // 扩展值是一个函数 value: fucntion(o) { // 得到所有的自有属性,包括不可枚举属性 var names = Object.getOwnPropertyNames(o); // 将属性逐个扩展给Object.prototype for(var i=0; i < names.length; i++) { // 如果属性已经存在,则跳过 if(names[i] in this) continue; var desc = Object.getOwnPropertyDescriptor(o, names[i]); Object.defineProperty(this, names[i], desc); } } } );
对象的三个属性
原型属性
每一个JavaScript对象(null除外)都和另外一个对象相关联,这个对象就是原型对象,每一个对象都从原型继承属性。没有原型的对象为数不多,Object.prototype就是其中之一。它不继承任何属性。
对象 | 原型 |
---|---|
通过对象直接量创建的对象 | Object.prototype。 |
通过new创建的对象 | 构造函数的prototype属性。 |
通过Object.creat()创建的对象 | 第一个参数(也可以是null)作为它们的原型。 |
var p = { x: 1 }; var o = Object.create(p); p.isPrototypeOf(o); // true,o继承自p Object.prototype.isPrototypeOf(o); // true,p继承自Object.prototype
类属性
对象的类属性(class attribute)是一个字符串,用以表示对象的类型信息。默认的toString()方法(继承自Object.prototype)返回了如下这种格式的字符串:
[object class]
很多对象继承的toString()方法重写了,为了能调用正确的toString()方法,必须间接地调用Function.call()方法。
function classof(o) { if(o === null) return "Null"; if(o == undefined) return "Undefined"; return Object.prototype.toString.call(o).slice(8, -1); // 使用Function.call方法 }
通过对象直接量、new构造函数或者Object.create()这3种方式创建的对象,类属性都是”Object”。
classof(null); // "Null" classof(1); // "Number" classof(""); // "String" classof(false); // "Boolean" classof({}); // "Object" classof([]); // "Array" classof(/./); // "RegExp" classof(new Date());// "Date" classof(window); // "Window" function f() {} classof(f); // "Function",注意与下面的区别 classof(new f()); // "Object"
可扩展性
对象的可扩展性表示是否可以给对象添加新属性。所有的内置对象和自定义对象都是显式可扩展的,宿主对象的可扩展性由JavaScript引擎定义的。
方法 | 作用 |
---|---|
Object.isExtensible() | 判断对象是否可扩展。 |
Object.preventExtensions() | 将对象设置为不可扩展。注意:一旦设置为不可扩展,将无法再转换回可扩展。 |
Object.seal() | 将对象设置为不可扩展,同时所有的自有属性不可配置。注意:对于已经封闭(sealed)的对象不能解封。 |
Object.freeze() | 除了完成seal()的功能外,同时还将所有的数据属性设置为只读(如果对象的存取器属性具有settter方法,存取器属性将不受影响,仍可以通过给属性赋值调用它们)。 |
序列化对象(serialization)
对象序列化(JSON.stringify())是指对象的状态转换为字符串,也可将字符串还原(JSON.parse())为对象。JSON.stringify()只能序列化对象可枚举的自有属性。
o = { x:1, y:{z:[false, null, ""]} }; s = JSON.stringify(o); // s是"{ x:1, y:{z:[false, null, ""]} }" p = JSON.parse(s); // p是o的深拷贝
Number、String、Boolean、null、对象、数组都可以序列化和还原。NaN、Infinity和-Infinity序列化结果是null。
Date对象序列化的结果是ISO格式的日期字符串(参照Date.toJSON()函数),但JSON.parse()依然保留它们的字符串形态。
函数、RegExp、Error对象和undefined值不能序列化和还原(没有定义toJSON()方法)。
var obj = new Date(); var s = JSON.stringify(obj); var d = JSON.parse(s); alert(obj); // Sat May 14 2016 17:33:04 GMT+0800 alert(s); // "2016-05-14T09:33:04.874Z" (stringify返回时包含2层"") alert(d); // 2016-05-14T09:33:04.874Z alert(typeof d); // string类型,无法还原为Date类型
对象方法
toStirng()
由于默认的toString()方法并不会输出很多有用的信息([object Object]),因此很多类都带有自定义的toString()。例如,数组转换为字符串时,结果是一个数组元素列表;当函数转换为字符串时,得到函数的源代码。
toLocaleString()
这个方法返回本地化字符串。Object默认的toLocaleString()并不做任何本地化自身的操作,它仅调用toString()方法并返回对应值。Date和Number类对toLocaleString()方法做了定制,可以用它对数字、日期和时间做本地化的转换。
toJSON()
Object.property实际上没有定义toJSON()方法,但对于需要序列化的对象来说,JSON.stringify()方法会调用toJSON()方法。如果在待序列化的对象中存在这个方法,则调用它,返回值即是序列化的结果,而不是原始的对象。
valueOf()
当JavaScript需要将对象转换为某种原始值时调用,尤其是转换为数字的时候。相关文章推荐
- Android Native 绘图方法
- prototype Element学习笔记(篇一)
- Prototype 学习 工具函数学习($方法)
- Prototype使用指南之selector.js说明
- prototype1.4中文手册
- 如何定义一个getter和seter设置的属性可以被绑定
- prototype 1.5相关知识及他人笔记
- prototype Element学习笔记(Element篇三)
- prototype Element学习笔记(篇二)
- C#中struct和class的区别详解
- VBS ArrayList Class vbs中的数组类
- 大家看了就明白了css样式中类class与标识id选择符的区别小结
- prototype 的说明 js类
- Prototype 学习 工具函数学习($A方法)
- Prototype源码浅析 String部分(三)之HTML字符串处理
- javascript prototype,executing,context,closure
- prototype 1.5 & scriptaculous 1.6.1 学习笔记
- prototype 学习笔记整理
- 深入了解PHP类Class的概念
- jquery 表单验证之通过 class验证表单不为空