要点
- 通过构造函数的属性prototype可以访问其原型
- 构造函数有默认的原型Object,但也可将原型(prototype)设置为其他(自定义的)原型对象
- 归根结底,所有原型和对象都是从Object派生而来的
- object包含所有对象都将继承的属性和方法,如
toString
和hasOwnProperty
- 可给内置对象(如Object和String)添加属性,也可重写它们的既有属性
例如,重写内置的Object
对象的toString
方法;
为内置的String
对象新增hasKeyWord
方法;
但这样做时必须小心,因为你所作的修改可能带来深远的影响。
原型链的终点是Object,而非小狗原型
前面介绍的两个原型链(这里的“原型链”实际上是一个简化版本):
- 小狗原型<-小狗实例;
- 小狗原型<-表演犬原型<-表演犬实例;
真正的原型链:
- Object<-小狗原型<-小狗实例;
- Object<-小狗原型<-表演犬原型<-表演犬实例;
真正的原型链
何为Object
- Object可视为对象始祖,所有对象都是从它派生而来的。
- ①每个对象都有原型,该原型默认为Object;
②可将对象原型设置为其他对象,如表演犬的原型是小狗实例; - 但无论如何,所有原型链的终点都是Object
i.e. 任何对象都继承了"Object原型"的属性和方法 - Object实现了多个重要的方法,它们是JavaScript对象系统的核心部分
- Object的常用方法:
hasOwnProperty
、toString
(但实例通常会重写它)等
一、利用继承来重写内置的Object行为
由上,所有对象都是从Object派生而来的,即 任何对象都继承了"Object原型"的属性和方法
因此,我们可以对任何对象调用hasOwnProperty
、toString
等方法
不仅如此,我们还可以根据自己的需要,重写这些内置方法
继承内置对象时,可重写内置对象自带的方法。
一种常见的情形是,重写Object定义的方法toString。所有对象都可使用方法toString来获取对象的字符串表示,我们可根据需要来重写该方法
例子:重写toString
方法
从Object继承而来的toString
方法,在显示对象时做得并不好:
function Robot(name, year, owner) {
this.name = name;
this.year = year;
this.owner = owner;
}
var robby = new Robot("Robby", 1956, "Dr. Morbius");
console.log(robby.toString());
输出[object Object]
这样的输出意义不大,我们可以根据需要重写toString
方法,得到想要的字符串表示
function Robot(name, year, owner) {
...
}
Robot.prototype.toString = function(){
return "Robot " + this.name + ", belonging to " + this.owner;
}
var robby = ...
console.log(robby.toString());
输出 Robot Robby, belonging to Dr. Morbius
谨慎重写Object的属性和方法
重写内置对象的属性和方法时,一定要特别小心;
若随意重写某些Object的属性和方法,可能改变一些依赖于这些属性来完成工作的代码的行为,以意想不到的方式破坏既有的代码、引发难以发现的bug
可以重写的Object的属性和方法
toString
toLocaleString
类似于toString,提供描述对象的本地化的(用母语表示的)字符串
valueOf
默认返回当前对象,可重写并让它返回你希望的其他值
不可重写的Object的属性和方法
constructor
指向与该原型相关的构造函数
hasOwnProperty
isPrototypeOf
判断一个对象是否是另一对象的原型
propertyIsEnumerable
判断对象中指定的属性是否可以被 for…in 循环枚举,但是通过原型链继承的属性除外
二、利用继承来扩展内置对象的行为
可以对内置对象添加新的方法,从而让所有对象实例都可使用该方法
例子:为内置的String
对象新增hasKeyWord
方法
我们知道,对于所有字符串对象都可使用substring
等方法;
但如果想添加更多自定义的新方法,且让任何字符串(String实例)都能使用它,可以利用继承来扩展内置对象
为String原型扩展一个hasKeyWord方法,在字符串包含某些关键字时返回true
代码如下:
注意,这里是直接对内置对象String
的原型String.prototype
进行操作
String.prototype.hasKeyWord= function() {
var keyWord= ["load","touch", "open"];
for (var i = 0; i < keyWord.length; i++) {
var index = this.indexOf(keyWord[i]);
//this指向调用hasKeyWord方法的对象(字符串实例)
if (index >= 0) {//字符串中包含关键字
return true;
}
}
return false;
};
例子2:为内置的String
对象新增palindrome
方法
为String原型扩展一个palindrome方法,判断是否为回文串
代码如下:
注意,这里是直接对内置对象String
的原型String.prototype
进行操作
String.prototype.palindrome= function() {
var rvs = this.split("").reverse().join("");
//字符串对象没有reverse方法,数组才有
return (rvs===this.valueOf());
};
要注意,在为内置对象扩展新方法时,我们操作的始终是一个对象,而非作为基本类型的字符串
因此,始终应该用this
来表示调用该方法的字符串对象:
- 必须用
this.valueOf()
表示字符串,因为this
是一个对象,而非字符串(基本类型),需要valueOf
方法返回 String 对象的原始值。
谨慎扩展内置对象
为String等内置对象添加方法时,要小心:
- 编写自己的代码,确保新方法的名称不与对象的既有方法冲突
- 使用他人的代码,即 链接其他代码时,一定要清楚这些代码包含的自定义扩展,以防名称冲突
- 有些内置对象(如Array)不能扩展