prototype 对象
3.1 构造函数的缺点
实例对象的属性和方法,可以定义在构造函数内部。
通过构造函数为实例对象定义属性,虽然很方便,但是有一个缺点。同一个构造函数的多个实例之间,无法
共享属性,从而造成对系统资源的浪费。
function Cat(name, color) {
this.name = name;
this.color = color;
this.meow = function () {
console.log('喵喵');
};
}
var cat1 = new Cat('大毛', '白色');
var cat2 = new Cat('二毛', '黑色');
cat1.meow === cat2.meow
// false
上面代码中,cat1和cat2是同一个构造函数的两个实例,它们都具有meow方法。由于meow方法是生成在
每个实例对象上面,所以两个实例就生成了两次。
这个问题的解决方法,就是 JavaScript 的原型对象(prototype)。
3.2 prototype 属性的作用
JavaScript 的每个对象都继承另一个对象,后者称为“原型”(prototype)对象。一方面,任何一个对
象,都可以充当其他对象的原型;另一方面,由于原型对象也是对象,所以它也有自己的原型。null也可以
充当原型,区别在于它没有自己的原型对象。
原型对象的作用,就是定义所有实例对象共享的属性和方法。这也是它被称为原型对象的原因,而实例对象
可以视作从原型对象衍生出来的子对象。
Animal.prototype.walk = function () {
console.log(this.name + ' is walking');
};
上面代码中,Animal.prototype对象上面定义了一个walk方法,这个方法将可以在所有Animal实例对象上
面调用。构造函数就是普通的函数, 所以实际上所有函数都有prototype属性。
3.3 原型链
对象的属性和方法,有可能定义在自身,也有可能定义在它的原型对象。由于原型本身也是对象,又有自己
的原型,所以形成了一条原型链(prototype chain)。比如,a对象是b对象的原型,b对象是c对象的原型
,以此类推。如果一层层地上溯,所有对象的原型最终都可以上溯到Object.prototype,即Object构造函数
的prototype属性。那么,Object.prototype对象有没有它的原型呢?回答是有的,就是没有任何属性和方
法的null对象,而null对象没有自己的原型。
如果让某个函数的prototype属性指向一个数组,就意味着该函数可以当作数组的构造函数,因为它生成的
实例对象都可以通过prototype属性调用数组方法。
var MyArray = function () {};
MyArray.prototype = new Array();
MyArray.prototype.constructor = MyArray;
var mine = new MyArray(); mine.push(1, 2, 3);
mine.length // 3
mine instanceof Array // true
3.4 constructor 属性
prototype对象有一个constructor属性,默认指向prototype对象所在的构造函数。
function P() {}
P.prototype.constructor === P
// true
由于constructor属性定义在prototype对象上面,意味着可以被所有实例对象继承。
function P() {}
var p = new P();
p.constructor
// function P() {}
p.constructor === P.prototype.constructor
// true
p.hasOwnProperty('constructor')
// false