JavaScript的继承的实现与其它基于类的语言不同,JavaScript中的每个对象都有一个属性(
__proto__
),通过它来指向一个名为原型的对象,该原型对象也有自身的原型,层层向上,直到null
,null
表示再往上没有原型。通过这种方式,形成了原型链。
函数
在js中,函数是非常特殊的,只有函数才有prototype
属性。函数可以是一般函数(实现某个逻辑的代码块),也可以作为构造函数用于创建对象。
构造函数
js
function person(name) {
this.name = name
}
const personA = new person('xiaoming');
console.log(personA.name); // xiaoming
new Person('xiaoming')
通过person
构造函数创建了一个实例对象personA
。而personA
的原型就是函数person
的prototype
,即personA.__proto__
指向preson.prototype
js
console.log(personA.__proto__ === person.prototype); // true
console.log(person.prototype);
函数person的prototype
是一个对象,大概如下所示:
js
{
constructor: ƒ person(name),
[[Prototype]]: {
constructor: ƒ Object(),
hasOwnProperty: ƒ hasOwnProperty(),
isPrototypeOf: ƒ isPrototypeOf(),
propertyIsEnumerable: ƒ propertyIsEnumerable(),
toLocaleString: ƒ toLocaleString(),
toString: ƒ toString(),
valueOf: ƒ valueOf()
}
}
可以看出person.prototype
自带一个属性constructor
,它指向了构造函数本身,我们可以验证一下
js
console.log(person.prototype.constructor === person); // true
通过在构造函数的prototype
对象上增加属性or函数,从而让所有的实例都能访问和使用。
js
person.prototype.speak = function(msg) {
console.log(msg);
}
person.prototype.hasEyes = true;
console.log(personA.hasEyes); // true
console.log(personA.speak('hello')); // hello
用下面这张图表示构造函数、实例和实例原型之间的关系:
__proto__
每个对象都有__proto__属性,指向的是它对应的原型对象。__proto__属性其实是来自于Object.prototype
,所有的对象都继承了Object.prototype
。
另外,使用__proto__
是不被推荐的,只是现代浏览器保留了而已,在未来的web标准中,可能会被移除,官方推荐使用Object.getPrototypeOf(obj)
来获取对象的原型,使用Object.setPrototypeOf(obj, parent)
可以将一个指定对象(obj)的原型设置为另一个对象(parent)。
js
const obj = {};
console.log(Object.getPrototypeOf(obj) === Object.prototype); // true
改变原型链
实现对象A继承对象B,有很多种方法
使用Object.setPrototypeOf
js
const A = {a: 1};
const B = {b: 2}
Object.setPrototypeOf(A, B);
console.log(A.b); // 2
使用类
js
class A {
a = 1;
}
class B extends A {
b = 2
}
const b = new B();
console.log(b.a); // 1
console.log(b.__proto__ === B.prototype); // true
console.log(Object.getPrototypeOf(b) === B.prototype); // true
console.log(B.prototype.__proto__ === A.prototype); // true
console.log(Object.getPrototypeOf(B.prototype) === A.prototype); // true