Javascript 是面向对象语言,但是它不是基于类的,而是基于原型(prototype)的语言,原型是 JavaScript 中的继承的基础,JavaScript 的继承就是基于原型的继承。
每个对象都拥有一个原型对象,对象以其原型为模板、从原型继承方法和属性。原型对象也可能拥有原型,并从中继承方法和属性,一层一层、以此类推。这种关系常被称为原型链,它解释了为何一个对象会拥有定义在其他对象中的属性和方法。
prototype 属性
我们可以通过 prototype 属性向对象添加属性和方法。
语法如下:
object.prototype.name=value
示例:
下面我们创建了一个 Person
构造函数:
function Person(id, name, age){
Person.id = id;
Person.name = name;
Person.age = age;
}
如何使用 prototype
属性向这个构造函数中添加一个属性呢,代码如下:
var per = new Person(1, "xkd", 22);
// 使用 prototype 向构造函数添加属性
Person.prototype.address = "长沙";
// 在控制台输出新添加的属性值
console.log(per.address); // 输出:长沙
constructor属性
Object 对象的每个实例都具有 constructor 属性,constructor(构造函数)保存用于创建当前对象的函数。
示例:
所有对象都会从它的原型上继承一个 constructor
属性:
function Person(id, name, age){
Person.id = id;
Person.name = name;
Person.age = age;
}
var per1 = new Person(1, "xkd", 22);
var per2 = new Person(2, "summer", 23);
console.log(per1.constructor == Person); // 输出: true
console.log(per2.constructor == Person); // 输出: true
使用prototype属性实现继承
所有的 JavaScript 对象都会从一个 prototype 原型对象中继承属性和方法。例如上述的 Person
对象会从Person.prototype
继承属性和方法。
JavaScript 并没有显示的继承语法,在 JavaScript 中所有对象都是 Object 的子类,所以对象之间都是平等的。
如果我们要实现继承的效果,可以通过特殊的方法来实现。上面我们讲了 JavaScript 中所有的对象都有一个prototype
属性,为该属性增加属性方法可以视为对类的扩展,也就是增加了prototype
属性的类继承了原有的类,这就是 JavaScript 所提供的伪继承机制。
示例:
我们来看下面这个函数:
function Person(id, name, age){
this.id = id;
this.name = name;
this.age = age;
this.show = function(){
console.log("编号:" + id + ", 姓名:" + name + ", 年龄:" + age);
}
}
var per1 = new Person(1, "水星仔", 18);
per1.show(); // 输出:编号:1, 姓名:水星仔, 年龄:18
var per2 = new Person(2, "小飞侠", 20);
per2.show(); // 输出:编号:2, 姓名:小飞侠, 年龄:20
上述代码中我们在构造函数 Person
内定义了一个 show()
方法,这种在函数内定义函数的方法是有弊端的。通过这种方式定义函数,每次创建一个对象都会创建一个新的 show()
函数,但是我们实际上只需要一个 show()
函数,所以这样就会降低性能。并且 show()
函数中的局部变量产生闭包,闭包会扩大局部变量的作用域,使局部变量存活到函数外。
为了避免这些情况,我们可以使用 prototype 属性来向对象添加方法:
// 使用 prototype 为 Person 添加方法
Person.prototype.hobby = function(){
console.log(this.name + "爱学习");
}
// 创建一个新的对象
var per3 = new Person(3, "小师妹", 16);
// per3 可以调用prototype属性增加的方法
per3.hobby(); // 输出:小师妹爱学习
// 也可以调用Person中的方法
per3.show(); // 输出:编号:3, 姓名:小师妹, 年龄:16
// per1和per2也可以调用prototype属性增加的方法
per1.hobby(); // 输出:水星仔爱学习
per2.hobby(); // 输出:小飞侠爱学习
输出:
编号:1, 姓名:侠课岛, 年龄:18
编号:2, 姓名:大侠, 年龄:16
大侠爱学习
侠课岛爱学习
上述代码中,我们为 prototype
属性增加了一个 hobby()
方法,可以认为是为 Person 动态地添加了一个 hobby
方法,使用这种方式增加的方法,可以让 Person
的所有实例共享。并且因为hobby()
不在 Person 函数内,所以不会产生闭包。