1.简单原型链
// 创建要继承的父类
function Father() {
// 自带的属性和方法
this.name = "li";
this.show = function() {
console.log("帅");
}
};
// 在父类的原型上添加方法
Father.prototype.callback = function() {
console.log(this.name);
}
// 定义Son
function Son() {
}
// 令Son继承Father
Son.prototype = new Father();// 这句是原型链继承的重点
// 创建Son的对象实例
var son = new Son();
var son1 = new Son();
console.log(son.name); // 可以调用继承于父类上的属性
son.show(); // 可以调用继承于父类上的方法
son1.callback(); // 可以调用father中原型上添加的方法
重点:
让新实例的原型等于父类的实例。
优点:
简单,易于实现
缺点:
- 原型对象的引用属性是所有实例共享的。
// 创建要继承的父类
function Father() {
// 自带的属性和方法
this.name = "li";
};
// 定义Son
function Son() {
}
// 令Son继承Father
Son.prototype = new Father(); // 这句是原型链继承的重点
// 创建Son的对象实例
var son = new Son();
var son1 = new Son();
son1.__proto__.name = "zhang"; //通过一个实例改变Son的prototype上的name
// 导致所有实例的name都改变,就叫做引用值共享
console.log(son.name); //"zhang"
console.log(son1.name); //"zhang"
- 创建子类实例时,无法向父类构造函数传参
- 继承单一
2. 构造函数式继承
// 创建父类
function Father(name) {
this.name = name;
this.arr = [1];
this.show = function() {
console.log("shuai");
}
}
Father.prototype.call = function() {
console.log("call");
};
// 创建子类
function Son(name) {
// 借父类的构造函数来增强子类实例,等于是把父类的实例属性复制了一份给子类实例装上了(完全没有用到原型)
Father.call(this, name); // 核心代码
}
var son1 = new Son("n1");
var son2 = new Son("n2");
son1.arr.push(2); // 向son1的数组中添加一个数
console.log(son1);
console.log(son2);
// 这时son实例的prototype的原型是object
console.log(son1.show === son2.show); // false
重点:
用.call()和.apply()将父类构造函数引入子类函数(在子类函数中做了父类函数的自执行(复制))
优点:
- 只继承了父类构造函数的属性,没有继承父类原型的属性
- 解决了原型链继承的缺点
- 可以继承多个构造函数的属性(利用call())
- 在子实例中可向父实例传参。
缺点:
- 只能继承父类构造函数的属性
- 无法实现构造函数的复用(每次使用时都要重新调用)
- 每个新实例都有父类构造函数的副本,臃肿
3. 组合继承(组合原型链继承和借用构造函数继承)(常用)
// 创建父类
function Father(name) {
this.name = name;
this.arr = [1];
this.show = function() {
console.log("shuai");
}
}
Father.prototype.call = function() {
console.log("call");
};
function Son(name) {
Father.call(this, name); // 借用构造函数继承
};
Son.prototype = new Father(); // 原型链继承
var son1 = new Son('li');
var son2 = new Son('zhang');
console.log(son1);
console.log(son2);
重点:
结合了两种模式的优点,传参和复用
优点:
- 可以继承父类上的属性,可以传参,可复用
- 每个新实例引入的构造函数属性是私有的。
缺点:
调用了两次父类构造函数(耗内存),子类的构造函数会代替原型上的那个父类构造函数。
4. 原型式继承
// 创建父类
function Father(name) {
this.name = name;
this.arr = [1];
this.show = function() {
console.log("shuai");
}
}
Father.prototype.call = function() {
console.log("call");
};
// 生育函数
function beget(obj) {
function F() {
}
F.prototype = obj; //继承了传入的参数
return new F(); //返回函数对象
}
var son1 = new Father(); // 先拿到父类的对象
console.log(son1);
// (核心代码)
var son2 = beget(son1); // 将父类中的属性赋值到每一个子类的函数的原型中
var son3 = beget(son1); // 同上
son2.name = "zhang"; // 修改实例中的属性
console.log(son2);
console.log(son3);
console.log(son2.call); //子类的实例都可以访问父类的原型上的属性和方法
重点:
用一个函数包装一个对象,然后返回这个函数的调用,这个函数就变成了个可以随意增添属性的实例或对象。(每创建一个对象实例都会返回一个新的函数)、
优点:
复制一个对象,用函数来包装。
缺点:
- 所有实例都会继承原型上的属性。
- 无法实现复用。
五.寄生式继承
// 创建父类
function Father(name) {
this.name = name;
this.arr = [1];
this.show = function() {
console.log("shuai");
}
}
Father.prototype.call = function() {
console.log("call");
};
function beget(obj) {
function F() {
}
F.prototype = obj; //继承了传入的参数
return new F(); //返回函数对象
}
var son = new Father();
// 将原型式继承的核心代码套一个壳子
function getSubObject(obj) {
var clone = beget(obj); // 核心
return clone;
}
var son1 = getSubObject(new Father());
console.log(son1);
重点:
就是给原型式继承外面套了个壳子。
优点:
没有创建自定义类型,因为只是套了个壳子返回对象(这个),这个函数顺理成章就成了创建的新对象。
缺点:
没用到原型,无法复用。
六、寄生组合式继承(常用)
寄生:
在函数内返回对象然后调用
组合:
1、函数的原型等于另一个实例。
2、在函数中用apply或者call引入另一个构造函数,可传参
// 创建父类
function Father(name) {
this.name = name;
this.arr = [1];
this.show = function() {
console.log("shuai");
}
}
Father.prototype.call = function() {
console.log("call");
};
function beget(obj) {
function F() {
}
F.prototype = obj; //继承了传入的参数
return new F(); //返回函数对象
}
var bet = beget(Father.prototype);
// 继承了父类函数的原型
function Son() {
Father.call(this);
}
Son.prototype = bet; //继承了get的实例;
bet.constructor = Son; // 修复实例
var son1 = new Son();
// Sub的实例就继承了构造函数的属性,父类实例,
console.log(son1.call);
重点:修复了组合继承的问题