前言
对于js这种面向过程的编程语言而言,oo不是它的强项,但有时也会在代码中OOP。
这里收集了七种继承方式,并测试做了比较。还有一些其他的实现形式暂未考虑,比如通过拷贝的形式实现继承;
由于技术原因,具体用到哪些场景不是很在行,所以这里就做一些简单的示范和介绍。
代码
/**
* https://www.cnblogs.com/qing-5/p/11365614.html
* 父级构造函数中的属性在子级实例上有一份在原型上有一份(多余),浪费内存
*/
/*
function A(name) {
this.name = name;
this.propertys = {
sex: "F"
};
}
A.prototype.sayName = function() {
console.log("A.prototype.sayName===", this.name);
};
A.prototype.sayName2 = function() {
console.log("A.prototype.sayName===", this.name);
};
function B(age, name) {
A.call(this, name); //
this.age = age;
this.propertys = {
sex: "M"
};
}
B.prototype = new A(); //
B.prototype.constructor = B; //
B.prototype.sayName = function() {
console.log("B.prototype.sayName===", this.name);
};
B.prototype.sayAge = function() {
console.log("B.prototype.sayAge===", this.age);
};
var a = new A("aaa");
var b = new B(22, "bbb");
a.sayName();
b.sayName();
b.sayAge();
console.log("a===", a);
console.log("b===", b);
console.log("a.__proto__", a.__proto__);
console.log("b.__proto__", b.__proto__);
*/
/**
* es6 class语法糖
* 不继承构造器里面的属性
* 代码易维护
*/
/*
class A {
constructor(name) {
this.name = name;
}
sayName() {
console.log("A.prototype.sayName===", this.name);
}
}
class B extends A {
constructor(age, name) {
super(name);
this.name = name;
this.age = age;
}
sayName(name) {
console.log("B.prototype.sayName===", this.name);
}
sayAge() {
console.log("B.prototype.sayAge===", this.age);
}
}
var a = new A("aaa");
var b = new B(22, "bbb");
a.sayName();
b.sayName();
b.sayAge();
console.log("a===", a);
console.log("b===", b);
console.log("a.__proto__", a.__proto__);
console.log("b.__proto__", b.__proto__);
*/
//
/**
* <<ES6标准入门 9.11>>
* 实例之间的无构造函数的继承
* 要产生多个对象需要克隆并一一改变它们的属性
*/
/*
var a = {
name: "aaa",
superName: "aaa_super",
propertys: [{
_a: "_a"
}],
sayName() {
console.log("a.sayName:", this.name);
}
};
var b = Object.create(a);
// var b = Object.assign(
// Object.create(a), {
// name: 'bbb'
// }
// );
b.name = "bbb";
b.age = 22;
b.sayAge = function() {
console.log("b.sayAge:", this.age);
}
a.sayName();
b.sayName(); //能调用原型上的方法
b.sayAge();
b.propertys[0]._b = "_b"; //能改原型上的对象
console.log("a===", a);
console.log("b===", b);
console.log("a.__proto__", a.__proto__); //Object.prototype
console.log("b.__proto__", b.__proto__);
*/
/**
* http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_inheritance.html
* 构造函数绑定
* 通过子类构造函数中,绑定父类this上的方法:A.call(this, name);
* 只能访问父类构造函数内定义的属性和方法,不能访问父类原型上的
* 子级实例上的属性拥有父级实例一样的属性,实际上消耗了内存换取了速度,因为在原型上查询是耗时的
*/
/*
function A(name) {
this.name = name;
this.propertys = {
sex: "F"
};
this._a = "_a";
}
A.prototype.sayName = function() {
console.log("A.prototype.sayName===", this.name);
};
A.prototype.say = function() {
console.log("A.prototype.say===", this._a);
};
function B(age, name) {
//继承属性 使用借用构造函数实现对实例属性的继承
A.call(this, name);
this.name = name; //覆盖属性
this.age = age; //父级构造函数中的this上不会有age属性
this.propertys = {
sex: "M"
};
}
B.prototype.sayName = function() { //覆盖方法
console.log("B.prototype.sayName===", this.name);
};
B.prototype.sayAge = function() { //自定义方法
console.log("B.prototype.sayAge===", this.age);
};
*/
/**
* http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_inheritance.html
* prototype模式
* 通过子级构造函数的prototype设置为父级实例,并改变constructor指向
* B.prototype = new A();
* B.prototype.constructor = B;
* 子集对象的原型上有父级对象上的构造函数中的方法和属性,子级对象可以使用父级对象构造函数和原型上的方法属性
* 子级原型上有一个父级构造函数中相同的副本,额外增加了内存消耗,也容易发生命名冲突
*/
/*
function A(name) {
this.name = name;
this.propertys = {
sex: "F"
};
this._a = "_a";
}
A.prototype.sayName = function() {
console.log("A.prototype.sayName===", this.name);
};
A.prototype.say = function() {
console.log("A.prototype.say===", this._a);
};
function B(age, name) {
this.name = name;
this.age = age;
this.propertys = {
sex: "M"
};
}
B.prototype = new A("aa");
B.prototype.constructor = B;
B.prototype.sayName = function() { //覆盖方法
console.log("B.prototype.sayName===", this.name);
};
B.prototype.sayAge = function() { //自定义方法
console.log("B.prototype.sayAge===", this.age);
};
B.prototype.name = "B.prototype.name";
*/
/**
* http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_inheritance.html
* 直接继承prototype(共享prototype)
* 子级的构造函数的prototype上定义的东西会覆盖父级的,他们相互影响
* 省内存
*/
/*
function A(name) {
this.name = name;
this.propertys = {
sex: "F"
};
this._a = "_a";
}
A.prototype.sayName = function() {
console.log("A.prototype.sayName===", this.name);
};
A.prototype.say = function() {
console.log("A.prototype.say===", this._a);
};
function B(age, name) {
this.name = name;
this.age = age;
this.propertys = {
sex: "M"
};
}
B.prototype = A.prototype;
B.prototype.constructor = B;
B.prototype.sayName = function() { //覆盖方法
console.log("B.prototype.sayName===", this.name);
};
B.prototype.sayAge = function() { //自定义方法
console.log("B.prototype.sayAge===", this.age);
};
B.prototype.name = "B.prototype.name";
*/
/**
* http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_inheritance.html
* 直接继承prototype(空函数中介)
* 父子(实例/原型)上的属性和方法都泾渭分明
* 调用父级原型上的方法this会错误,需显式改变this指向
*/
/*
function A(name) {
this.name = name;
this.propertys = {
sex: "F"
};
this._a = "_a";
}
A.prototype.sayName = function() {
console.log("A.prototype.sayName===", this.name);
};
A.prototype.say = function() {
console.log("A.prototype.say===", this._a);
console.log("A.prototype.say | this===", this);
};
function B(age, name) {
this.name = name;
this.age = age;
this.propertys = {
sex: "M"
};
}
var F = function() {};
F.prototype = A.prototype
B.prototype = new F();
B.prototype.constructor = B;
B.prototype.sayName = function(a) {
console.log("B.prototype.sayName===", this.name);
};
B.prototype.sayAge = function() {
console.log("B.prototype.sayAge===", this.age);
};
B.prototype.name = "B.prototype.name";
*/
// var a = new A("aaa");
// var b = new B(22, "bbb");
// a.sayName();
// b.sayName();
// b.sayAge();
// console.log("a===", a);
// console.log("b===", b);
// // console.log("A.name===", A.name);
// // console.log("B.name===", B.name);
// console.log("a.__proto__", a.__proto__);
// console.log("b.__proto__", b.__proto__);
不足
1、没考虑混入(多继承)。
2、具体应用场景。
3、更多的继承方式。