对象和继承(Objects and inheritance)
和所有的值类型一样,对象有属性。事实上,你可以将对象当作一组属性的集合,每个属性都是一对(键和值)。键是字符串,值可以是任意JavaScript值。到目前为止,我们仅仅见过键是标识符的属性,因为点操作符处理的键必须为标识符。在这节,你讲见到另一种访问属性的方法,能将任意字符串作为键。
单个对象(Single objects)
在JavaScript中,你可以直接创建对象,通过对象字面量:
var jane = {
name: 'Jane',
describe: function () {
'use strict';
return 'Person named '+this.name;
}
};
上面的对象有两个属性:name
和 describe
。你能读(“get
”)和 写(“set
”)属性:
> jane.name // get
'Jane'
> jane.name = 'John'; // set
> jane.newProperty = 'abc'; // 自动创建
属性是函数如 describe 可以被当作方法调用。当调用他们时可以在它们内部通过this引用对象。
> jane.describe() // 调用方法
'Person named John'
> jane.name = 'Jane';
> jane.describe()
'Person named Jane'
in 操作符用来检测一个属性是否存在:
> 'newProperty' in jane
true
> 'foo' in jane
false
若读取一个不存在的属性,将会得到undefined值。因此上面的两个检查也可以像下面这样:
> jane.newProperty !== undefined
true
> jane.foo !== undefined
false
delete操作符用来删除一个属性:
> delete jane.newProperty
true
> 'newProperty' in jane
false
任意键属性(Arbitrary property keys)
属性的键可以是任意字符串。到目前为止,我们看到的对象字面量中的和点操作符后的属性关键字。按这种方法你只能使用标识符。如果你想用其他任意字符串作为键名,你必须在对象字面量里加上引号,并使用方括号获取和设置属性。
> var obj = { 'not an identifier': 123 };
> obj['not an identifier']
123
> obj['not an identifier'] = 456;
方括号允许你动态计算属性关键字:
> var x = 'name';
> jane[x]
'Jane'
> jane['na'+'me']
'Jane'
引用方法(Extracting methods)
如果你引用一个方法,它将失去和对象的连接。就其本身而言,函数不是方法,其中的this值为undefined(严格模式下)。
> var func = jane.describe;
> func()
TypeError: Cannot read property 'name' of undefined
解决办法是使用函数内置的bind()方法。它创建一个新函数,其this值固定为给定的值。
> var func2 = jane.describe.bind(jane);
> func2()
'Person named Jane'
方法内部的函数(Functions inside a method)
每个函数都有一个特殊变量this。如果你在方法内部嵌入函数是很不方便的,因为你不能从函数中访问方法的this。下面是一个例子,我们调用forEach循环一个数组:
var jane = {
name: 'Jane',
friends: [ 'Tarzan', 'Cheeta' ],
logHiToFriends: function () {
'use strict';
this.friends.forEach(function (friend) {
// 这里的“this”是undefined
console.log(this.name+' says hi to '+friend);
});
}
}
调用 logHiToFriends 会产生错误:
> jane.logHiToFriends()
TypeError: Cannot read property 'name' of undefined
有两种方法修复这问题。
1:将this存储在不同的变量。
logHiToFriends: function () {
'use strict';
var that = this;
this.friends.forEach(function (friend) {
console.log(that.name+' says hi to '+friend);
});
}
2:forEach的第二个参数允许提供this值。
logHiToFriends: function () {
'use strict';
this.friends.forEach(function (friend) {
console.log(this.name+' says hi to '+friend);
}, this);
}
在JavaScript中函数表达式经常被用作函数参数。时刻小心函数表达式中的this。
构造函数:对象工厂(Constructors: factories for objects)
目前为止,你可能认为JavaScript的对象仅是键值的映射,通过JavaScript对象字面量可以得出这个观点,看起来很像其他语言中的地图/字典(map/dictionary)。然而,JavaScript对象也支持真正意义上的面向对象特性:继承(inheritance)。本节不会完全讲解JavaScript中继承的工作原理,但会给你以此为开始的简单模式。如果你想得到更多知识,请查阅这篇文章“JavaScript inheritance by example”。 除了作为“真正”的函数和方法,函数还在JavaScript中扮演第三种角色:如果通过new操作符调用,他们会变为构造函数,对象的工厂。构造函数是对其他语言中的类的粗略模拟。约定俗成,构造函数的第一个字母大写。例如:
// 设置实例数据
function Point(x, y) {
this.x = x;
this.y = y;
}
// 方法
Point.prototype.dist = function () {
return Math.sqrt(this.x*this.x + this.y*this.y);
};
我们看到构造函数分为两部分:首先,Point函数设置实例数据。其次,Point.prototype属性包含对象的方法。前者的数据是每个实例私有的,后面的数据是所有实例共享的。 我们通过new操作符调用Point:
> var p = new Point(3, 5);
> p.x
3
> p.dist()
5.830951894845301
p是Point的一个实例:
> p instanceof Point
true
> typeof p
'object'
深入阅读
- The pitfalls of using objects as maps in JavaScript [important, read soon]
- JavaScript inheritance by example
- Object properties in JavaScript [advanced: each property has attributes that determine whether it is writable, etc.]
- Private data for objects in JavaScript
- Javascript继承 原型的陷阱
- Javascript 封装问题
更多建议: