还不了解原型和原型链的小伙伴,请移步“深入理解javascript之原型和原型链”
首先,定义父类:
function Person(name) {
this.name = name;
this.showName = function () {
return this.name;
}
}
// 原型对象上添加属性
Person.prototype.age = 18;
Person.prototype.friends = ['小明', '小强'];
优点:
缺点:
function Student(name) {
Person.call(this, name);
// Person.apply(this, [name]);
}
let s1 = new Student('小红')
console.log(s1.name); // 小红
console.log(s1.showName()); // 小红
// 无法继承原型上的属性和方法
console.log(s1.age); // undefined
console.log(s1.friends); // undefined
function Class1() {
this.showSub = function (a, b) {
console.log(a - b);
}
}
function Class2() {
this.showAdd = function (a, b) {
console.log(a + b);
}
}
// 使用多个 apply、call 就实现多重继承了
function Class3() {
Class1.apply(this);
Class2.apply(this);
//Class1.call(this);
//Class2.call(this);
}
var c = new Class3();
c.showSub(3, 1); // 2
c.showAdd(3, 1); // 4
function Student(name) {
this.name = name;
}
var s1 = new Person('张三');
var s2 = new Student('李四');
console.log(s1.showName.call(s2)); // 李四
// console.log(s1.showName.apply(s2));
虽然调用的是 s1 上的 showName 方法,但是 this 指针指向的是 s2 ,所以 this.name 应该是 “李四”
优点:
Person
)的属性方法和父类原型对象(Person.prototype
)上的属性方法缺点:
Person
)传参(组合方式继承中解决)function Student(name) {
this.name = name;
}
Student.prototype = new Person(); // prototype指向实例对象,可以继承Person的构造函数属性和原型对象上的属性
// Student.prototype = Person.prototype; // 不能继承Person的构造函数属性,只能继承Person原型对象上的属性,还会修改子会影响父
Student.prototype.constructor = Student;
let s1 = new Student('刘一');
console.log(s1.name); // 刘一
console.log(s1.showName()); // 刘一
console.log(s1.age); // 18
console.log(s1.friends); // ["小明", "小强"]
// 父子构造函数的原型对象之间有共享问题(修改子会影响父)
s1.friends.push('小红');
console.log(s1.friends); // ["小明", "小强", "小红"]
let s2 = new Student();
console.log(s2.friends); // ["小明", "小强", "小红"]
组合式继承是比较常用的一种继承方法,其背后的思路是:
Child.prototype = new Parent()
)Parent.call(this,hello)
)优点:
缺点:
function Worker(name) {
Person.call(this, name); // 构造函数继承
}
Worker.prototype = new Person(); // 原型链继承
Worker.prototype.constructor = Worker;
let w1 = new Worker('张三');
console.log(w1.name); // 张三
console.log(w1.showName()); // 张三
console.log(w1.age); // 18
console.log(w1.friends); // ["小明", "小强"]
//一个实例修改了原型属性,另一个实例的原型属性也会被修改
w1.friends.push('李四')
let p1 = new Person()
console.log(p1.friends); // ["小明", "小强", "李四"]
优点:
缺点:
content()
对传入其中的对象执行了一次浅复制
,将构造函数Fn
的原型直接指向传入的对象。
function content(obj) { // 先封装一个函数容器,用来输出对象和承载继承的原型
function Fn() {}
Fn.prototype = obj; // 修改类的原型为obj, 于是Fn的实例都将继承obj上的方法
return new Fn();
}
let p1 = content(new Person('李四'));
//上面在ECMAScript5 有了一新的规范写法,Object.create() 效果是一样的
// let p1 = Object.create(new Person('李四'));
console.log(p1.name); // 李四
console.log(p1.showName()); // 李四
console.log(p1.age); // 18
console.log(p1.friends); // ["小明", "小强"]
优点:
缺点:
function content(obj) {
function Fn() {}
Fn.prototype = obj; // 继承了传入的参数
return new Fn();
}
// 在原型式继承外面套了个壳子
function subObj(obj) {
let sub = content(obj);
sub.name = "王五";
return sub;
}
let p1 = subObj(new Person());
console.log(p1.name); // 王五
console.log(p1.showName()); // 王五
console.log(p1.age); // 18
console.log(p1.friends); // // ["小明", "小强"]
优点:
// 寄生
function content(obj) {
function Super() {}
Super.prototype = obj; // 继承了传入的参数
return new Super();
}
let con = content(Person.prototype);
// 组合
function Student(name) {
Person.call(this, name);
}
Student.prototype = con;
con.constructor = Student; // S
let s1 = new Student('赵六');
console.log(s1.name); // 赵六
console.log(s1.showName()); // 赵六
console.log(s1.age); // 18
console.log(s1.friends); // // ["小明", "小强"]
将上面的简化下
function Student(name) {
Person.call(this, name);
}
// 创建一个没有实例方法的类
let Super = function () {};
Super.prototype = Person.prototype;
//将实例作为子类的原型
Student.prototype = new Super();
let s1 = new Student('赵六');
console.log(s1.name); // 赵六
console.log(s1.showName()); // 赵六
console.log(s1.age); // 18
console.log(s1.friends); // // ["小明", "小强"]
function Temp(name) {
this.aaa = Person; // this.aaa是作为一个临时的属性,并且指向 Person 所指向的对象,
this.aaa(name); // 执行this.aaa方法,即执行 Person 所指向的对象函数
delete this.aaa; // 销毁this.aaa属性,即此时 Temp 就已经拥有了 Person 的所有属性和方法
// 以上三行相当于: Person.call(this);
}
var s1 = new Temp('张三');
console.log(s1.name); // 李四
console.log(s1.showName()); // 李四
// 无法继承原型对象上的属性方法
console.log(s1.age); // undefined
console.log(s1.friends); // undefined