首先,我们先要明白,JS里的继承,是对原型的继承,而不是改变构造函数的原型。
那这句话到底是什么意思呢?让我们通过一个例子解释一下。
function User() {}
function Admin() {}
User.prototype.name = function () {
console.log("user name")
};
Admin.prototype = User.prototype;
let admin = new Admin();
admin.name(); //user name
这里我们往User的原型对象中添加了一个name方法,而且我们的Admin函数也希望能调用到User原型对象下的name方法,因此这里我们把User的原型对象直接赋值给了Admin的原型对象,也就是说Admin的原型对象被替换成了User的原型对象。
这种方法乍一看好像是没有问题的,也能顺利使用到User原型对象上的方法。但是当我们有多个函数同时希望通用到User原型对象上的方法的时候,问题就来了。
function User() {}
function Admin() {}
function Member() {}
Member.prototype = User.prototype;
Admin.prototype = User.prototype;
User.prototype.name = function () {
console.log("user name")
};
Admin.prototype.role = function () {
console.log("admin.role")
};
Member.prototype.role = function () {
console.log("Member.role")
};
let a = new Admin();
a.role();
let m = new Member();
m.role();
当我们的Member构造函数和Admin的构造函数的原型对象都改为User的原型对象的时候,Member和Admin本身原型对象上的方法自然而然就添加到了User的原型对象中了,而当Member的方法名跟Admin的方法名相同的时候,就会出现覆盖的情况(后调用的方法覆盖前一个方法),就会导致其中一个函数的方法不可使。
比如说在我们生活中,张三继承了一笔财产,是在自己原有财产的基础上新增了一笔财产,而不是继承了财产之后原来的财产小时掉。
因此这种直接改变原型对象的方式不是继承!
那正确的做法是什么呢?
我们知道,我们的构造函数的原型对象的对象原型指向的都是Object,因此我们可以通过改变对象原型实现正确继承的方法。
方法一:
function User() {}
function Admin() {}
function Member() {}
Member.prototype.__proto__ = User.prototype;
Admin.prototype.__proto__ = User.prototype;
User.prototype.name = function () {
console.log("user name")
};
Admin.prototype.role = function () {
console.log("admin.role")
};
Member.prototype.role = function () {
console.log("Member.role")
};
let a = new Admin();
a.role();
let m = new Member();
m.role();
方法二:
function User() {}
function Admin() {}
function Member() {}
Member.prototype = Object.create(User.prototype);
Object.defineProperty(Member.prototype, 'constructor', {
value: Member,
enumerable: false
});
Admin.prototype = Object.create(User.prototype);
Object.defineProperty(Admin.prototype, 'constructor', {
value: Admin,
enumerable: false
});
User.prototype.name = function () {
console.log("user name")
};
Admin.prototype.role = function () {
console.log("admin.role")
};
Member.prototype.role = function () {
console.log("Member.role")
};
let a = new Admin();
a.role(); //admin.role
let m = new Member();
m.role(); //Member.role
这两种方法均可以实现继承。
此时经过这样设置之后,我们的Member和Admin的原型对象的对象原型指向的就不再是Object了,而是User的原型对象。通过这种做法,我们能让Menber和Admin保留它们自身的方法,还能让他们都共用到User原型对象中的方法。这就是真正意义上的继承。