Javascript是面向对象的语言,但是它没有“子类”和“父类”的概念,它的继承靠“原型链(prototype chain)”来实现。
首先从 JavaScript 创建实例说起
我定义了一个构造函数
function Cat(name){
this.name = name;
}
并生成两个实例
var cat1 = new Cat('小黑');
var cat2 = new Cat('小白');
这个时候 cat1 和 cat2 都继承了 Cat 的 name/age 属性
console.log(cat1.name);//小黑
console.log(cat2.name);//小白
但是如果我想要定义一个公共属性呢,譬如说使这两个实例的种类都是‘猫’,那么
function Cat(name){
this.species = '宠物猫';
this.name = name;
}
var cat1 = new Cat('小黑');
var cat2 = new Cat('白猫');
console.log(cat1.name,cat1.species);//小黑 宠物猫
console.log(cat2.name,cat2.species);//小白 宠物猫
这样小黑和小白在出生的时候就species属性就都是 '宠物猫' 了。
然而,有一天人们发现 Cat 这个构造函数的species属性描述并不准确(不潮流),'宠物猫' 应该变成 '猫主子'。
于是我修改了Cat 的属性
Cat.species = '猫主子';
console.log(Cat.species);//猫主子
console.log(cat1.species);//宠物猫
console.log(cat2.species);//宠物猫
cat1和cat2的属性还是宠物猫,所以我还要这样改
cat1.species = '猫主子';
cat2.species = '猫主子';
console.log(cat1.species);//猫主子
console.log(cat2.species);//猫主子
如果我有一百只宠物猫呢?...
有没有办法让 cat1 和 cat2 的属性自动随 Cat 属性变化呢,伟大的prototype来了。我们修改一下原代码
function Cat(name){
this.name = name;
}
Cat.prototype.species = '宠物猫';
var cat1 = new Cat('小黑');
var cat2 = new Cat('白猫');
console.log(cat1.name,cat1.species);//小黑 宠物猫
console.log(cat2.name,cat2.species);//小白 宠物猫
这时候,只要改变构造函数的属性,实例属性也会自动变化
Cat.prototype.species = '猫主子';
console.log(Cat.species);//undefined
console.log(Cat.prototype.species);//猫主子
console.log(cat1.species);//猫主子
console.log(cat2.species);//猫主子
解释一下以上代码:
prototype 是构造函数(或者说函数)的一个特有的属性,这个属性值为一个对象,实例一旦创建,将自动引用构造函数 prototype 对象的属性和方法。
当 species 属性是未定义在 prototype 对象中时,实例相当于创建了自己独有的species 属性,其值在创建后不受构造函数的影响;
当 species 属性定义在 prototype 对象中时,实例相当于引用了prototype的species 属性,其值随prototype的改变而改变。
实例自己创建species属性后实例中species将不随 prototype 对象属性值改变:
如添加以下代码
cat1.species = '最喜欢的猫';
console.log(Cat.prototype.species);//猫主子
console.log(cat1.species);//最喜欢的猫
console.log(cat2.species);//猫主子
这是因为实例额的属性和方法,分为两种,一种是本地的,一种是引用的。当本地定义了species属性后,将会优先显示本地的属性值。