js 的对象

除了字符串、数字、true、false、null 和 undefined 之外,JavaScript 中的值都是对象。引用类型的值(对象)是引用类型的一个实例。在 ECMAScript 中,引用类型是一种数据结构,用于将数据和功能组织在一起。

从宏观的角度讲,对象是对客观事物的抽象,事物的特征可以用属性表示,事物的行为可以用方法表示;

从微观的角度讲,对象就是一种数据类型,通过对象可以方便地对变量和函数进行管理。

对象的创建

JavaScript 对象的创建有三种方式:直接量,new 关键字 和 Object.creat() 方来创建。

对象直接量

对象的直接量就是由若干兼职对组成的映射表,键和值之间用 “:” 分隔,键值对之间用 “,” 分隔,整个映射表用一对花括号包裹,比如通过字面量声明(更加简便)var obj= {};

键值对

键值对就是一种对应关系,通过键能够方便地找到值

键:值 key:value k:v

例子:

1
2
3
4
5
6
7
8
9
10
11
12
var emptyObj = {};
var pointObj = {x:0, y:0};
var pointObj2 = {x: pointObj.x, y: pointObj.y};
var books = {
"main title": "JavaScript", // 属性名中有空格时,必须用字符串
'sub-title': "The Definitive Guide", // 属性名中有连字符,必须用字符串
"for": "all audiences", // "for" 是保留字,必须用引号
author: { // 这里的属性名没有引号
firstname: "Hiraku",
surname: "Hongqin"
}
};

对象的字面量是一个表达式,这个表达式的每次运算都创建并初始化一个新对象。每次计算对象直接量的时候,也会计算它的每个属性的值。也就是说,如果在一个重复调用的函数中的循环体内使用对象直接量,它将创建很多新对象,并且每次创建的对象的属性值可有可能不同。

通过 new 关键字创建对象

new 运算符创建并初始化一个新对象。n关键字 new 后面跟随一个函数调用。

通过构造函数声明(更加通用)var obj= new Object(); // 创建一个空对象,和 {} 一样

对象具有属性和方法

  • 属性 用来描述对象的特征 一般是名词 对应变量
  • 方法 用来描述对象的行为 一般是动词 对应函数

原型初识

除了 null 以外的每一个 JavaScript 对象都和 原型 相关联。所有通过对象直接量创建的对象对具有同一个原型对象,可通过 Object.prototype 获得对原型对象的引用。通过 new 关键字和构造函数调用创建的对象的原型就是构造函数的 prototype 属性的值。因此,和使用 {} 创建对象一样,通过 new Object() 创建的对象也继承自 Object.prototype。

没有原型的对象有 Object.prototype,它不继承任何属性。其他对象都是普通对象,都有原型。所有的内置构造函数比如:Array(), Date() 都具有一个继承自 Object.prototype 的原型。

Object.create()

ECMAScript 5 定义了一个名为 Object.create() 方法,是一个静态函数,可以用来创建对象。有两个参数:第一个参数是对象,第二个参数可选,用于对属性进行详细描述。

1
2
3
4
5
6
// o1 继承了属性 x 和 y
var o1 = Object.create({x:1, y:1});
// o2 不继承任何属性和方法,没有原型,不能和 + 运算符一起工作
var o2 = Object.create(null);
// o3 创建一个普通的空对象
var o3 = Object.create(Object.prototype);

可以通过任意原型对象创建新对象,即可以使任意对象继承。

例子: 返回一个继承自原型对象 p 的属性的新对象

1
2
3
4
5
6
7
8
9
function inherit(p){
if (p == null) throw TypeError(); // p 是一个对象
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 的继承对象
}

属性和方法的查询和设置

属性

  • 属性的定义
    • 对象.属性名 = 值;
    • 对象[“属性名”] = 值;
  • 属性的调用
    • 对象.属性名;
    • 对象[“属性名”];

方法

  • 方法的定义
    • 对象.方法名 = function() { //函数体 };
    • 对象[“方法名”] = function() { //函数体 };
  • 方法的调用
    • 对象.方法名();
    • 对象[“方法名”];

作为关联数组的对象

对象属性和方法的第二种设置和访问用了 “[]”,只是利用的不是索引访问,而是字符串。这种数据就是关联数组,也叫散列、映射或者字典。

当我们通过 [] 来访问对象的属性时,在程序运行是可以设置和修改或者创建它们。

继承

对象具有自有属性(own property),也有一些属性是从原型对象上继承来的。

还是这个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function inherit(p){
if (p == null) throw TypeError(); // p 是一个对象
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.x = 1;
var p = inherit(o); // p 继承自对象 o 和 Object.prototype
p.y = 2;
var q = inherit(p); // q 继承自对象 p、 o 和 Object.prototype
q.z = 3;
var s = q.toString();
console.log(s); // "[object object]"
console.log(q.x + q.y + q.z); // 6 属性 x 和 y 分别继承自 o 和 p

属性访问错误

  • JavaScript 中,属性访问时,并不总是会话或者设置。
  • 在查询不存在的属性时不会报错,而返回 undefined。
  • 内置构造函数的原型是只读的。

删除属性

delete 运算符可以删除对象的属性。delete 只是断开属性和宿主对象的联系,而不会操作属性中的属性。

delete 只能删除自有属性,不能删除继承属性。

delete 不能删除可配置属性。

在非严格模式下,删除全局独享的可配置属性时,可以省略对全局对象的引用,直接在 delete 操作符后跟要删除的属性名即可。

在严格模式下,delete x; 报错, delete this.x; 正常运行

属性检测

通过 in 运算符、hasOwnProperty()、propertyIsEnumerable() 方法;

  • in 运算符
1
2
var o = {x: 1};
"x" in o; // true "x" 是 o 的属性
  • hasOwnProperty() 方法
1
2
3
var o = {x: 1};
o.hasOwnProperty(x); // true "x" 是 o 的自有属性
o.hasOwnProperty(constructor); // false constructor 是原型中的属性
  • propertyIsEnumerable() 方法
1
2
3
4
5
6
7
8
9
10
11
12
13
function inherit(p){
if (p == null) throw TypeError(); // p 是一个对象
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 = interit({x: 1});
o.y = 1;
o.propertyIsEnumerable("x"); // false "x" 属性是继承的,不能被枚举
o.propertyIsEnumerable("y"); // true

除了 in 以外, 另一种简单的判断一个属性是否是 “undefined” 使用 “!==”

1
2
3
4
var o = {x: 1};
o.x !== undefined; // true
o.y !== undefined; // false;
o.toString !== undefined; // true

但 in 可以区分不存在的属性和存在但值为 undefined 类型的属性

1
2
3
4
5
6
7
var o = {x: undefined};
o.x !== undefined; // false 属性存在,但是值为 undefined
o.y !== undefined; // false 属性不存在
"x" in o; // true 属性存在
"y" in o; // false 属性不存在
delete o.x; // 删除了属性 x
"x" in o; // false 属性不再存在

对象的枚举属性

除了检测对象的属性,我们经常需要遍历对象的属性,通常使用 for-in 遍历;

ECMAScript 5 提供了 Object.keys() 方法,返回值是一个数组,这个数组由对象中可枚举的自有属性的名称组成。

ECMASciript 5 提供的另一种枚举属性的方法是 Object.getOwnPropertyNames(), 和 Object.keys() 类似,只是它返回对象的所有自有属性的名称,而不仅仅是可以枚举的属性。

用来枚举对象属性的工具函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
/**
* [extend description] 把 P 中的可枚举属性复制到 o 中,
* 如果有同名属性,则覆盖 o 中的属性
* @param {[type]} o [description]
* @param {[type]} p [description]
* @return {[type]} [description]
*/
function extend(o, p) {
for (var prop in p) {
o[prop] = p [prop];
}
return o;
}
/**
* [merge description] 把 P 中的可枚举属性复制到 o 中,
* 如果有同名属性, o 中的属性不受影响
* @param {[type]} o [description]
* @param {[type]} p [description]
* @return {[type]} [description]
*/
function merge (o, p) {
for (var prop in p) {
if (o.hasOwnProperty[prop]) continue;
o[prop] = p [prop];
}
return o;
}
/**
* [restrict description] 如果 o 中的属性没有同名属性,则从 o 中删除这个属性
* @param {[type]} o [description]
* @param {[type]} p [description]
* @return {[type]} [description] 返回 o
*/
function restrict (o, p) {
for (var prop in p) {
if (!(prop in p)) delete o[prop];
}
return o;
}
/**
* [substract description] 如果 o 中存在同名属性,则删除这个属性
* @param {[type]} o [description]
* @param {[type]} p [description]
* @return {[type]} [description]
*/
function substract(o, p) {
for (var prop in p) {
delete o[prop];
}
return o;
}
/**
* [union description] 如果有重名属性,使用 p 中的属性
* @param {[type]} o [description]
* @param {[type]} p [description]
* @return {[type]} [description] 返回一个新对象这个对象同时拥有 o 的属性
*/
function union (o, p) {
return extend (extend({}, o), p);
}
/**
* [keys description] 返回的数组中包含的是 o 中可枚举的自有属性的名字
* @param {[type]} o [description]
* @return {[type]} [description] 返回一个数组
*/
function keys(o) {
if (typeof o !== "object") throw TypeError();
var result = [];
for (var prop in o){
if (o.hasOwnProperty(prop)) result.push(prop);
}
return result;
}

属性 getter 和 setter

当程序执行查询存储属性的值的时候,JavaScript 调用 getter 方法(无参数),当设置属性值的时候,调用的是 setter 方法。

属性的特性

属性包含一个属性名和4个特性:

  • value 属性值
  • writable 可写性
  • enumerable 可枚举性
  • configurable 可配置性

存取器属性不具有值 value 和 可写性

ECMAScript 5 中定义了属性描述符 Object.getOwnPropertyDesctiptor() 获取某个对象特定的属性,这个方法只能得到自有属性的特性。

让新建的属性具有某种特性,需要调用 Object.definePeoperty() 方法;传入要修改的对象,要创建或修改的属性的名称以及属性描述符对象;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var o = {}; // 创建一个空对象
// 插入一个不可枚举的数据属性 x, 并赋值为 1;
Object.defineProperty(o, "x", {
value: 1,
writable: true,
enumerable: false,
configurable: true
});
o.x; // --> 1
Object.keys(o); // --> []
// 对属性 x 做修改变为只读
Object.defineProperty(o, "x", {writable: false});
// 试图更改这个属性值
o.x = 2; // 更改操作失败,在严格模式中抛出异常
o.x; // => 1;
// 属性依然是可配置的,因此可以通过这种方式对它进行修改
Object.defineProperty(o, "x", {value: 2});
o.x; // => 2
// 现在讲 x 从数据属性修改为存储器属性
Object.defineProperty(o, "x", {
get: function(){
return 0;
}
});
o.x; // => 0

需要同时修改多个属性,使用 Object.defineProperties(),它返回修改后的对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* 复制属性的特性
* 给 Object.prototype 上添加一个不可枚举的extend() 方法
* 这个方法继承自它的调用对象,将作为参数传入的对象的属性以一幅字
* 除了值之外也赋值属性所有的特性,除非在目标对象中存在同名的属性。
* 参数对象的所有自有属性(包括不可枚举的属性)也会一一复制
*/
Object.defineProperty(object.prototype, "extend", { // 定义 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;
// 获取 o 中的属性描述符
var desc = Object.getOwnPropertyDescriptor(o, names[i])
// 用它给 this 创建一个属性
Object.defineProperty(this, names[i], desc)
}
}
});

对象的三个属性

每一个对象都有与之相关联的原型 (prototype)、类 (class) 和可扩展性 (extensible attribute)。

原型属性

通过直接量创建的对象使用 Object.prototype 作为它们的原型,而使用 new 创建的对象使用 构造函数的 prototype 属性作为它们的原型。而通过 Object.create() 创建的对象使用第一个参数作为它们的原型,也可以是 null .

ECMAScript 5 中将对象作为参数传入 Object.getPrototypeOf() 方法中可以查询它的原型。

要检测一个对象是否是另一个对象的原型(或处于原型链中),用 isPrototypeOf() 方法。

类属性

对象的类属性是一个字符串,用以表示对象的类型信息。只有一种间接的方法可以访问,即toString() 方法。默认的 toString() 方法继承自 Object.prototype,返回 [object class] 格式的字符串。

如何获得对象的类?

1
2
3
4
5
6
7
8
/**
* classof() 函数
*/
function classof(o){
if (o === null ) return "null";
if (o === undefined) return "undefined";
return Object.prototype.toString.call(o).slice(8,-1);
}

这个函数可以传入任意的参数。

对象的可扩展性

ECMAScript 5 的 Object.preventExtensions() 方法将对象转为不可扩展的,Object.seal() 还可以设置对象的属性不可配置。

序列化对象(JSON 对象表示法)

是指将对象的状态转为字符串,也可以将字符串还原为对象。

ECMAScript 5 提供了内置函数 JSON.stringify() 和 JSON.parse() 用来序列化和还原JavaScript对象。这两个方法使用 JSON 数据格式。

JSON, 即 JavaScript Object Notation( JavaScript 对象表示法)是仿照JS中对象字面量的格式去书写的一串用来记录对象数据的字符串,可以用于数据传输。 即 JSON

注意:函数、RegExp、Error 对象和 undefined 值不能序列化和还原

  • JSON 转字符串 序列化 JSON.stringfy();
  • 字符串转 JSON 反序列化 JSON.parse();

遍历的 JSON 两种方式

通过for可以对集合进行有序的遍历for(var k in json) { 语句 }; k变量代表的是json中的各个属性(key)和 var i = 0中的i是一个意思,名字不同而已

对象的方法

  • toString() 方法
  • toLocalString() 方法
  • toJSON() 方法
  • valueOf() 方法
感谢您的支持!