对象简介
- 对象是
js的基本数据类型,对象是一种复合值,它将很多值聚合在一起,可通过名字访问这些值。 - 对象也可看做是属性的无序集合,每个属性都是一个名/值对,属性名是字符串,因此我们也可以把对象看成是从字符串到值的映射。
- 这种基本数据结构还有很多叫法,“散列”、“散列表”、“字典”、“关联数组”。
-
js对象还可以从一个称为原型的对象继承属性(核心特性)。 - 除了字符串、数字、
true、false、null和undefined之外,js的值都是对象。
三类js对象
-
内置对象
(native object):数组、函数、日期、正则表达式 -
宿主对象
(host object):js所运行的环境提供的对象比如:BOM中的Window、DOM中的document。 -
自定义对象
(user-defined object):代码创建的对象。
两类属性
- 自有属性:直接在对象中定义的属性。
- 继承属性:是在对象的原型对象中定义的属性。
创建对象
- 对象直接量(创建对象最简单的方式)
- 关键字
new -
Object.create()函数
1、对象直接量
// 没有任何属性的对象
var empty = {};
// 复杂对象
var book = {
x:0, // 正常属性
"main title":"Js", // 属性名字有空格,必须用字符串表示
"sub-title":"js js", // 属性名字有连字符,必须用字符串表示
"for":"all" // for 是关键字,必须用引号,不建议使用关键字作为属性名
}
如上:对象直接量是一个表达式,这个表达式每次运算都创建并且初始化一个新的对象,每次计算对象直接量的时候,也都会计算它的每个属性的值,所以,如果在一个重复调用的函数中的循环体内使用了对象直接量,它将创建很多新对象,并且每次创建的对象的属性值也有可能不同。
2、new一个对象
new运算符创建并初始化一个新对象,关键字new后跟随一个函数调用,这里的函数称做构造函数(constructor),构造函数用以初始化一个新创建的对象。
var o = new Object(); // 创建一个空对象,和{}一样
var a = new Array(); // 创建一个空数组,和 []一样
var d = new Date(); // 创建一个表示当前时间的Date对象
var r = new RegExp('js'); // 创建一个可以进行模式匹配的RegExp对象
3、Object.create()
Object.create()是一个静态函数,而不是提供给某个对象调用的方法,使用它的方法只需传入所需原型对象即可。*·what·? ? ? *
-
Object.create()只能传入对象或者null,什么都不传报错。
var o1 = Object.create({x:1}); // o1继承了属性x
如上代码所示:o1继承了属性x,注意是继承,此时我们打印o1,输出{},但是打印o1.x却能输出1,再看o1.__proto__输出{x:1},o1的原型对象是{x:1},{x:1}的原型对象是Object,这一系列链接的原型对象就是所谓的“原型链”。

- 可以传入参数
null来创建一个没有原型的新对象,但是通过这种方法创建的对象不会继承任何东西,甚至不包括基本方法,比如toString()。
var o2 = Object.create(null); // o2不继承任何属性和方法。

- 如果想创建一个普通的空对象,需要传入
Object.prototype。
var o3 = Object.create(Object.prototype); // o3和{}和 new Object() 一样

继承
js对象具有“自有属性”,也有一些属性是从原型对象继承而来的。
function inherit(p) {
if(p == null){
throw TypeError(); // p 是一个对象,但是不能是null
}
if(Object.create) {
return Object.create(p); // 如果Object.create()存在就直接使用
}
var t = typeof p;
if(t !== "Object" && t !== "function") {
throw TypeError();
}
function f(){}; // 定义一个空构造函数
f.prototype = p; // 将其原型属性设置为p
return new f(); // 使用f()创建p的继承对象
}
var o = {}; // o 从 Object.prototype 继承对象的方法
o.x = 1; // 给o定义一个属性x
var p = inherit(o); // p 继承 o 和 Object.prototype
p.y = 2; // 给p定义一个属性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
假设要查询对象 o 的属性 x,如果 o 中不存在 x,那么将会继续在 o 原型对重查询属性 x,如果原型对象中也没有 x,但这个原型对象也有原型,那么继续在这个原型对象的原型上执行查询,直到找到x或者查找到一个原型是null的对象为止,可以看到,对象的原型属性构成一个“链”,通过这个“链”可以实现属性的继承。
现在假设给对象o的属性x赋值,如果o中已经有属性x(这个属性不是继承而来的),那么这个赋值操作只改变这个已有属性x的值,如果o中不存在属性x,那么赋值操作给o添加一个新属性x,如果之前o继承自属性x,那么这个继承的属性就被新创建的同名的属性覆盖了。
var unitcircle = {r:1};
var c = inherit(unitcircle);
c.x = 1;
c.y = 1;
c.r = 2;
unitcircle.r; // => 1 原型对象没有修改
原型
每一个js对象(除了null)都和另一个对象相关联,“另一个”对象就是我们熟知的原型,每一个对象都是从原型继承属性。
属性访问错误
- 查询一个不存在的属性并不会报错,如果在对象
o自身的属性或继承的属性中均未找到属性x,属性访问表达式o.x返回undefined
var o = {y:1};
o.x; // => undefined
- 如果对象不存在,那么查询这个不存在的对象的属性就会报错,
null和undefined值都没有属性,因此查询这些值的属性户报错。
var o = null;
o.x; // => Uncaught TypeError: Cannot read property 'x' of null
var p = {y:1};
o.x.z; // => Uncaught TypeError: Cannot read property 'z' of undefined
删除属性
-
delete运算符可以删除对象的属性 -
delete运算符只能删除自有属性,不能删除继承属性(要删除继承属性必须从定义这个属性的原型对象上删除它,而且这会影响到所有继承自这个原型的对象)
检测属性
js对象可以看做属性的集合,我们经常会检测集合中成员的所属关系,判定某个属性是否存在某个对象中。
js提供四种个方式:
-
in运算符,会去查继承属性 -
hasOwnPreperty(),只会查自有属性,不会去查继承的属性 -
propertyIsEnumerable(),只查自有且可枚举的属性,不查继承属性 - 通过属性查询 ,不能区分值是
undefined的属性
1、in运算符
in运算符的左侧是属性名(字符串),右侧是对象,如果对象的自有属性或继承属性中包含这个属性则返回true。
var o = {x:1};
"x" in o; // => true 自有属性
"y" in o; // => false 不存在属性
"toString" in o; //=> true 继承属性
2、 hasOwnProperty()
hasOwnProperty()用来检测给定的名字是否是对象的自有属性,对于继承的属性它将返回false。
var o = {x:1};
o.hasOwnProperty("x"); // => true 自有属性
o.hasOwnProperty("y"); // => false 不存在属性
o.hasOwnProperty("toString"); // => false ,继承属性
3、propertyIsEnumerable()
propertyIsEnumerable()是hasOwnProperty()的增强版,只有检测到是自有属性且这个属性可枚举为true时它才返回true。
var o = inherit({y:2});
o.x = 1;
o.propertyIsEnumerable("x"); // => true 自有可枚举属性
o.propertyIsEnumerable("y"); // => false 继承属性
Object.prototype.propertyIsEnumerable("toString"); // => false 不可枚举属性
4、使用!==判定一个属性是否是undefined
var o = {x:1,y:undefined};
o.x !== undefined; // => true 自有属性
o.y !== undefined; // => false 不存在属性
o.toString !== undefined; // => true 继承属性
o.y !== undefined; // => false 属性存在但是值为undefined
如上代码:当属性存在,但是值是undefined,!==不能返回希望的结果。
枚举属性
除了检测对象的属性是否存在,我们还会经常的遍历对象的属性。
-
for/in循环遍历 ,所有可枚举属性 -
Object.keys(),(ES5提供)返回由可枚举的自有属性的名称组成的数组 -
Object.getOwnPropertyNames()(ES5提供)只返回自有属性
1、for/in
for/in循环可以在循环体重遍历对象中所有可枚举的属性(自有和继承),把属性名称赋值给循环遍历。
var o = {x:1};
var p = inherit(o);
p.y = 2;
for (var key in p){
console.log(key);
}
// => y 自有属性
// => x 继承属性
// 没有输出 toString ?
"toString" in p; // => true 继承属性
p.propertyIsEnumerable("toString"); // => false 不可枚举属性
如何过滤继承属性?
for (var key in p){
if(!p.hasOwnProperty(key)) continue; // 跳过继承属性
console.log(key)
}
// => x 只输出自有属性
2、Object.keys()
Object.keys() 枚举属性名称的函数,它返回一个数组,这个数组由对象中可枚举的自有属性的名称组成。
var o = {x:1};
var p = inherit(o);
p.y = 2;
p.z = 3;
Object.keys(p); // => ["y","z"] 未返回继承属性x
3、Objcet.getOwnPropertyNames()
只返回对象的自有属性名称,而不仅仅是可枚举属性,不可枚举属性也会返回。
var o = {x:1};
var p = inherit(o);
p.y = 2;
p.z = 3;
Object.defineProperty(p,"h",{value:4,enumerable:true}); // 添加可枚举属性
Object.defineProperty(p,"w",{value:5,enumerable:false}); // 添加不可枚举属性
Object.keys(p); // => ["y","z","h"] 未返回不可枚举属性w 和继承 属性 x
Object.getOwnPropertyNames(p); // => ["y","z","h","w"] 只是未返回继承属性 x
属性getter和setter
在ES5中属性值可以用一个或两个方法代替,这俩方法就是getter和setter。由getter和setter定义的属性称做“存取器属性”。
- 如果属性同时具有
getter和setter方法,那么它是一个读/写属性。 - 如果它只有一个
getter方法,那么它是一个只读属性。 - 如果它只有一个
setter方法,那么它是一个只写属性,读取只写属性总是返回undefined。
var o = {
x:1,
y:2,
h:undefined,
get product(){
return this.x * this.y;
},
set product(value){
this.x = value;
this.y = value * 2;
},
get sum(){
return this.x + this.y;
},
set z(value){
this.h = value;
}
}
// product 是一个读/写属性
o.product; // => 2
o.product = 2;
o.product; // => 8
// sum 是一个只读属性
o.sum; // => 3
o.sum = 5;
o.sum; // => 3
// z 是一个只写属性
o.z; // => undefined
o.z = 4;
this.h; // => 4
属性的特性
除了包含名字和值之外,属性还包含一些标识它们可写、可枚举、可配置的特性。ES3无法设置这些特性。
- 可以通过这些
API给原型对象添加方法,并将它们设置成不可枚举,这让它们看起来更像内置方法。 - 可以通过这些
API给对象定义不能修改或删除的属性,借此“锁定”这个对象。 -
数据属性的
4个特性分别是:
值value)
可写性(writable)
可枚举性(enumerable)
可配置性(configurable) -
存取器属性 不具有 值 和 可写性 它们的可写性由
setter方法存在与否决定,存取器属性的4个特性是:
读取(get)
写入(set)
可枚举
可配置
ES5定义了一个名为“属性描述符”的对象,这个对象代表那4个属性。
{
value:数据属性,表示属性的值,默认: undefined
writable: 可写性,表示能否修改属性。默认值:true
enumerable:可枚举性,表示能否通过 for/in 遍历得到属性,默认true
configurable:可配置性,如果属性为false则不可在对属性描述符进行修改,默认值为true。
}
通过调用Object.getOwnPropertyDescriptor()可以获得某个对象特定属性的属性描述对象
var o = {x:1};
Object.getOwnPropertyDescriptor(o,"x");
// => {"value":1,"writable":true,"enumerable":true,"configurable":true}
Object.getOwnPropertyDescriptors(o);
// => {"x":{"value":1,"writable":true,"enumerable":true,"configurable":true}}
由上边代码可见,这个方法只能得到自有属性的描述符。
那么如何设置属性的特性呢? Object.defineProperty()
- 第一个参数:要修改的对象
- 第二个参数:要创建或者修改的属性名称
- 第三个对象:属性描述符对象
这个方法要么修改已有属性要么新建自有属性,但是不能修改继承属性。
var o = {x:1};
// 设置一个不可枚举的属性,其他属性默认true
Object.defineProperty(o,"x",{value:1,enumerable:false});
Object.keys(o); // => []
Object.defineProperties()
如果要同时修改或者创建多个属性:
- 第一个参数:要修改的对象
- 第二个参数:一个映射表,它包含要新建或修改的属性的名称以及它们的属性描述符
var o = {};
// 给对象o添加俩个属性,x 和 y 其中 y 不可枚举不可配置
Object.defineProperties(o,{
x:{"value":1,"writable":true,"enumerable":true,"configurable":true},
y:{"value":2,"writable":true,"enumerable":false,"configurable":false}
});
// y不可配置,如果这个时候我们想要修改y的属性描述符,会报出错误异常
Object.defineProperties(o,{
y:{"value":2,"writable":true,"enumerable":true,"configurable":false}
});
// => Uncaught TypeError: Cannot redefine property: y
对象的三个属性
- 原型
- 类
- 可扩展性
原型属性
- 对象的原型属性是用来继承属性的。
- 原型属性是在实例对象创建之初就设置好的。
-
ES5中,将对象作为参数传入Objcet.getPrototypeOf()可以查询它的原型(一个并不可靠的方法)。 - 要检测一个对象是否是另一个对象的原型,可以使用
isPrototypeOf()。
var o = {x:1};
var p = Object.create(o);
o.isPrototypeOf(p); // => true p继承自o
Object.prototype.isPrototypeOf(o); // true o 继承自Object.prototype
类
可扩展性
对象的可扩展性用以表示是否可以给对象添加新属性。
序列化对象
对象序列化是指将对象的状态转换为字符串,也可以将字符串还原为对象。
ES5提供了内置函数JSON.stringify()、JSON.parse()用来序列化和还原js对象。
-
NaN、Infinity、-Infinity序列化的结果是null - 函数、
RegExp、Error对象和undefined值不能序列化和还原 -
JSON.stringify()只能序列化对象可枚举的自有属性
JSON.stringify(NaN); // => "null"
JSON.stringify(Infinity); // => "nulll"
JSON.stringify(undefined); // => undefined
instanceof 运算符
instanceof运算符希望左操作数是一个对象,右操作数标识对象的类,如果左侧对象是右侧类的示例,则表达式返回true,否则返回false。
js对象的类是通过初始化它们的构造函数来定义的,这样的话instanceof的右操作数应当是一个函数
- 所有对象都是
Object的实例 - 如果
instanceof左操作数不是对象的话,返回false - 如果右操作数不是函数,则抛出一个类型错误的异常
var d = new Date(); // 通过Date()构造函数来创建一个新对象
d instanceof Date; // => true d是由Date创建的
d instanceof Object ; // => true 所有的对象都是Object的实例
d instanceof Number; // => false