对象是js的基本数据类型。
对象是一种复合值:它将很多值 聚合在一起,可通过名字访问这些值 。
对象也可看种是属性的无序集合,每个属性都是一个名/值对。属性名是字符串,因此我们可以把对象看成是从字符串到值的映射。
JS对象还可以从一个称为原型的对象继承属性。对象的方法通常是继承的属性。
对象最常见的用法是创建create,设置set,查找query,删除delete,检测test ,枚举 enumerate
属性包括名字和值。
对象除了包含属性外,每个对象还拥有三个相关的对象特性:
对象的原型
对象的类
对象的扩展标记
三类JS对象和两类属性:
内置对象: 规范定义的对象或类。比如 数组,函数,日期和正则表达式
宿主对象: 由JS解释器(如浏览器)所嵌入的宿主环境定义的。
自定义对象: 运行JS中代码创建的对象
自有属性: 是直接在对象中定义的属性
继承属性: 是在对象的原型中定义的属性
创建对象
可以通过对象直接量、关键字new 和 Object.create()函数来创建对象。
对象直接量
对象直接量是由若干名/值对组成的映射表。名/值用冒号分隔,名/值对之间用逗号分隔,整个映射表用花括号括起来。例子:
var empty = {}; //没有任何属性
var point = { x:0 , y:0 }; //两个属性
var point2 = { x:point.x, x.point.y + 1}; //更复杂的值
var book = {
"main title" : "Javascript", ,//属性名字里有空格,必须用字符串表示
'sub-title' : "The ... ", //属性名字里有连字符,必须用字符串表示
"for": "all ..." , //"for"是保留字,因此必须用引号
author: {//这个属性值是一个对象
firstName: "Jin", //这里的属性名都没有引号
surName: "He"
}
}
通过new创建对象
new运算符创建并初始化一个新对象。关键字new后跟随一个函数调用。
这里的函数称为构造函数(constructor),构造函数用以初始化一个新创建的对象。例如:
var o = new Object(); //创建一个空对象,和 {}一样
var a = new Array(); //创建一个空数组,和[]一样
var d = new Date(); //创建一个表示当前时间的Date对象
var r = new RegExp("js"); //创建一个可以进行模式匹配的RegExp对象
原型
每一个Js对象(ull除外)都和另一个对象相关联,‘另一个对象’就是我们要说的原型,每一个对象都从原型继承属性.
Object.prototype获得原型对象的引用。
通过new Object()创建的对象也是继承自Object.prototype.
通过new Array()创建的对象也是继承自Array.prototype.
通过new Date()创建的对象也是继承自Date.prototype.
所有的内置构造函数都具有一个继承自Object.prototype的原型。例如 Date.prototype的属性继承自Object.prototype.因此new Date()创建的Date对象的属性同时继承自Date.prototype和Object.prototype.这一系列链接的原型对象就是所谓的“原型链接”(prototype chain)
Object.creat()
该方法创建一个新对象,其中第一个参数是这个对象的原型。第二个是可选参数,用以对对象的属性进行进一步的描述。
Object.creat()是一个静态函数,而不是提供给某个对象调用的方法
var o1 = Object.create({x:1,y:2}); //o1继承了属性x和y
可以通过传入参数null来创建一个没有原型的新对象。但这种方式创建的对象不会继承任何东西。
var o2 = Object.create(null) ; //o2不继承任何属性和方法
如果想创建一个普通的空对象,如通过{} 或 new Object() 创建的对象,需要传入Object.prototype:
var o3 = Object.create(Object.prototype); //o3和{}和new Object()一样
通过原型继承创建一个新对象:
//inherit()返回了一个继承自原型对象p的属性的新对象
//使用Object.create()
//如果 不存在Object.create(),则退化使用其他方法
function inherit(p) {
if( p == null) throw TypeError(); //p是一个对象 ,但不能是Null
if(Object.create) //如果Object.create 存在
return Object.create(p) //直接使用它
var t = typeof p; //否则进行进一步的检测
if(t!=="object" && !=="function") throw TypeError();
function f() {};//定义一个空构造函数
f.prototype = p ; //将其原型属性设置为p
return new f(); //使用f()创建p的继承对象
}
inherit() 它返回的新对象继承了参数对象的属性。
属性的查询和设置
可以通过点(.)或方括号([])运算符来获取属性的值。
运算符左侧是一个表达式,它返回一个对象。对于点(.)来说,右侧必须是一个以属性名称命名的简单标识符。对于方括号([])来说,方括号内必须是一个计算结果为字符串的表达式。如:
var author = book.author; //得到book的"author"属性
var name = author.surname; //得到获得author的"surname"属性
var title = book["main title"] //得到book的"book title"属性
也可以通过这要的方式创建属性或是给属性赋值,但是放在左侧,如:
book.edition = 6; //给book创建一个名为"edition"的属性
book["main title"] = "Js"; //给 "main title"属性赋值
点运算符后的标识符不能是保留字,比如不能是 o.for,o.class
作为关联数组的对象
object.prototype;
object[prototype];
第二种,用方括号括起来,这个数组元素是通过字符串索引而不是数字索引,这种数组就是关联性数组,也称做散列,映射或字典。Javascript对象都是关联数组。
当通过[]来访问对象的属性时,属性名通过字符串来表示 。字符串是javascript的数据类型,在程序 运行时可以修改和创建它们。
var addr = "";
for(i = 0; i < 4 ; i++) {
addr += customre["address" + i] + '\n';
}
//这段代码读取customer对象的address0,address1,address2,address3属性,并将他们连接起来
function addstock (portfolio, stockname, shares) {
portfolio[stockname] = shares;
}
假设这个程序,利用网络计算当前用户股票市场投资的金额。程序允许用户输入每只股票的名称和购股份额。该程序使用protofolio的对象来存储这些信息。每只股票在这个对象中都有对应的属性,属性名称就是股票名称,属性值就是购股数量。例,如果用户持有IBM的50股,那就是portfolio.ibm属性的值 就是50。
由于用户在程序运行时输入的股票,因此无法得知这些股票是什么名称。因此不能用(.)来访问对象portfolio的属性,但可以使用[]。
下面例子利用for/in计算portfolio的总值:
function getvalue (portfolio) {
var total = 0.0;
for(stock in portfolio) {//遍历portfolio中的每只股票
var shares = portfolio[stock]; //得到每只股票的份额
var price = getquote(stock); //查找股票价格;
total += shares * price; //将结果累加至total中
}
return total; //返回total的值
}
属性继承
Javascript对象具有 “自有属性” (own prototype),也有一些属性是从原型继承而来的。用inherit()函数传入指定原型对象来创建实例
var o = {}; //o从Object.prototype继承对象的方法
o.x = 1; //给o定义一个属性x
var p = inherit(o); //p继承o和Object.prototype
p.y = 2; //给这定义一个属性y
var q = inherit(p); //q继承p、o和Object.prototype
q.z = 3; //给q定义一个属性z
var s = q.toString(); //toString继承自Object.prototype
q.x + q.y ; //3.x和y分别继承自o和p
属性删除
delete运算符。
delete book.author; //book不再有属性author
delete book["main title"]; //book不再有属性'main title'
delete运算符只能删除自有属性,不能删除继承属性。
当delete表达式删除成功没有任何副作用,它返回true.如果delete后不是一个属性访问表达式,delete同样返回true:
o = { x:1 }; //o有一个属性x,并继承属性toString
delete o.x ; //删除x,返回true
delete o.x ; //什么都没有做(事实上x已经不存在了),返回true
delete o.toString; //什么也没做(toString是继承来的),返回true
delete 1; //无意义,返回true
检测属性
判断某个属性是否存在于某个对象中。可以通过 in运算符,hasOwnPreperty(),propertyIsEnumerable()。
in运算符 的左侧是属性名,右侧是对象。
var o = { x:1 };
"x" in o; //true:'x'是o的属性
"y" in o; //false: 'y'不是o的属性
"toString" in o; //true: o继承toString属性
对象的hasOwnProperty()方法用来检测给定的名字是否是对象的自有属性。对于继承属性它将返回false:
var o = { x:1 };
o.hasOwnProperty("x"); //true,o有一个自有属性x
o.hasOwnProperty("y"); //false,o不存在属性y
o.hasOwnProperty("toString"); //false,tostring是继承属性
propertyIsEnumerable()检测是自有属性且这个属性的可枚举性。
var o = inherit({ y:2 });
o.x = 1;
o.propertyIsEnumerable("x"); //true : o 有一个可枚举的自有属性x
o.propertyIsEnumerable("y"); //false : y是继承来的
Object.prototype.propertyIsEnumerable("toString"); //false: 不可枚举
枚举属性
除了检测对象的属性是否存在,我们还会经常遍历对象的属性。
var o = {x:1, y:2, z:3}; //三个可枚举的自有属性
o.propertyIsEnumerable("toString"); //false,不可枚举
for(p in o) //遍历属性
console.log(p); //输出x,y,z,不会输出toString;
除了for/in循环之外,还有两个用以枚举属性名称的函数。
第一个是Object.keys(),它返回一个数组,这个数组由对象中可枚举的自有属性和名称组成。
第二个是Object.getOwnPropertyNames().和Object.keys()类似。
属性getter和setter
对象属性是由名字,值 和一组特性构成的。属性值可以脾一个或两个方法替代,就是getter和setter。由他们定义的属性称为“存储器属性”(accessor property)。不同于数据属性(data property),数据属性只有一个简单的值。和数据属性不同,存取器属性不具有可写性。只有只读属性。
var 0 = {
data_prop: value, //普通的数据属性
get accessor_prop() { /*这里是函数体*/ },
set accessor_prop() { /*这里是函数体*/ },
}
和数据属性一样,存取器属性是可以继承的。
var q = inherit(p);
q.x = 1, q.y = 1; //给q添加两个属性
console.log(q.r); //可以使用继承的存取器属性
console.log(q.theta)
属性的特性
除了包含名字和值之外,属性还包含一些标识它们可写、可枚举和可配置的特性。
可以通过API给原型对象添加方法,并将它们设置为不可枚举,看起来像内置对象。
可以通过API给对象定义不能修改或删除的属性,借此“锁定”这个对象
数据属性的4个特性是:值 value,可写性writable,可枚举性enumerable,可配置性configwritable
存取器属性的4个特性是:可读get,写入set,可枚举性enumerable,可配置性configwritable
通过调用Object.getOwnPropertyDescriptor()可以获得某个对象特定属性的属性描述符:
Object.getOwnPropertyDescriptor({x:1},"x");
//返回{value: 1, writable: true, enumerable: true, configurable: true}
要想设置属性的特性,或者让新建属性具有某种特性,则需要调用Object.definePeoperty(),传入要修改的对象,要创建或修改的属性的名称以及属性描述符对象。
对象的三个属性
每一个对象都有与这相关的原型(prototype),类(class)和可扩展性(extensible attribute).
原型属性
要检测一个对象是否是另一个对象的原型,使用isPrototypeOf()方法。例如,可以通过p.isPrototypeOf(o)来检测p是否o的原型:
var p = { x:1}; //定义一个原型对象
var o = Object.create(p); //使用这个原型创建一个对象
p.isPrototypeOf(o); //true: o继承自p
Object.prototype.isPrototypeOf(o); //true:p继承自Object.prototype
类属性
对象的类属性是一个字符串,用以表示 对象的类型信息。
要想获得对象的类,可以调用对象的toString()方法。
classof()函数可以传入任何类型的参数。数字、字符串和布尔值可以直接调用soString()方法一样。
function classof(o) {
if( o=== null ) return "Null";
if( o=== undefined ) return "Undefined";
return Object.prototype.toString.call(o).slice(8,-1);
}
可扩展属性
对象的可扩展属性用以表示 是否可以给对象添加新属性。所有内置对象和自定义对象都是电显式可扩展的,宿主对象的可扩展性由Javascript引擎定义的。
通过将对象传入 Object.esExtensions(),来判断该对象是否是可扩展的。如果 想将对象设置为不可扩展,调用Object.preventExtension()。
Object.seal()可将对象设置为不可扩展,还可将对象的自有属性设置为不可配置。可以用Object.isSealed()来检测对象是否封闭。
Object.freeze()将更严格地锁定对象--“冻结”。Object.isFrozen()来检测对象是否冻结。
//创建一个封闭对象,包括一个冻结的原型和一个不可枚举的属性
var o = Object.seal(Object.create(Object.freeze({x:1} , y:{valus:2,writable:true})));
序列化对象
对象的序列化是指将对象的状态转换为字符串,也可将字符串还原为对象。
内置函数JSON.stringify()和JSON.parse()用来序列化和还原JS对象。
o = {x:1,y:[false,null,""]};
s = JSON.stringify(o);
p = JSON.parse(s); //p是o的深拷贝
对象方法
toString()方法
该方法没有参数,它将返回一个表示调用这个方法的对象值的字符串。
var s = {x:1, y:2}.toString();
console.log(s) //[object Object]
toLocaleString()方法
该方法返回一个表示这个对象的本地化字符串。
toJSON()方法
JSON.stringify()
valueOf()方法
将对象转换为某种原始值 而非字符串的时候才调用它。
更多详情查阅 《JavaScript权威指南》