对象简介
- 对象是
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