JavaScript继承的发展
为什么需要继承?
原因:我们希望能减少重复性的代码,并且尽量弱化对象间的耦合。然而继承的方式是慢慢发展,不断迭代更新的。
1.传统形式——原型链
//1.原型链的方式继承
//父类构造函数,用于定义实例属性
function Father() {
}
Father.prototype.lastName = "Chen"; //自定义的父类原型对象属性
var father = new Father(); //实例
//子类
function Son() {
}
Son.prototype = father; //JS中,对象的prototype属性要么指向另一个对象,要么为null
var son = new Son();
console.log(father.lastName); //Chen
console.log(son.lastName); //Chen
以上代码非常简单地说明了通过原型链,son可以继承father的lastName属性。其中,为了让一个类继承另一个类,只需将子类的prototype设置为指向超类的一个实例即可(摘自《JavaScript模式》),即用父类实例传参。
特点:使用原型创建对象的方式,可以让所有对象实例共享它所包含的属性和方法。可以实现继承,但是可能会继承很多不需要的东西,不适合工业化使用。
2.call()或者apply()方式借用构造函数的方式继承(类继承)
// 2.借用构造函数的方式继承(类继承)
function Person(name, age, sex, ip) {
this.name = name;
this.age = age;
this.sex = sex;
}
//父类的原型对象属性
Person.prototype.id = 10;
function Student(name, age, sex, grade) {
Person.call(this, name, age, sex)
this.grade = grade;
}
var student = new Student('Chen', '18', 'miss', 'one');
console.log(student.age); //18
console.log(student.id); //undefined
特点:
- 不能继承借用构造函数的原型
- 每次构造函数都要多走一个函数
3. 共享原型
//3.共享原型
function Father() {
}
Father.prototype.lastName = "Chen";
function Son() {
}
Son.prototype = Father.prototype;
Son.prototype.sex = 'male'; //不能给自己单独添加原型
var father = new Father();
var son = new Son();
console.log(son.lastName); //Chen
console.log(son.sex); //male
console.log(father.lastName); //Chen
console.log(father.sex); //male
特点:能实现继承,但是父类和子类共享同一个原型,不能单独给某一个添加原型属性或方法,因为它们指向同一空间(如上图所示)。
可以简单封装一下:
//3.共享原型的封装
function inherit(Target,Origin){
Target.prototype = Origin.prototype;
}
inherit(Son, Father);
4.1圣杯模式
圣杯模式是目前继承常用的方式。
//4.圣杯模式初
function inherit(Target, Origin) {
function F() {};
F.prototype = Origin.prototype;
Target.prototype = new F();
}
function Father() {
}
Father.prototype.lastName = "Chen";
function Son() {
}
inherit(Son, Father);
var father = new Father();
var son = new Son();
Son.prototype.sex = "male";
console.log(son.sex); //male
console.log(father.sex); //undefined
console.log(son.constructor); //function Father(){...}
问题:constructor指向紊乱。
原因:对象上没有constructor属性,只有原型上有,一直向上找,找到Father。
解决:加一个constructor属性指向自己,并把真实继承的对象挂在自身保存起来(超类)。
//4.1 圣杯模式
function inherit(Target, Origin) {
function F() {};
F.prototype = Origin.prototype;
Target.prototype = new F();
Target.prototype.constructor = Target;
Target.prototype.uber = Origin.prototype;
}
function Father() {
}
Father.prototype.lastName = "Chen";
function Son() {
}
inherit(Son, Father);
var father = new Father();
var son = new Son();
Son.prototype.sex = "male";
console.log(son.sex); //male
console.log(father.sex); //undefined
console.log(son.constructor); //function Son(){...}
特点:用一个中间函数F()来起缓冲,与Father共享原型,再使用原型链的方式实现继承,如下图所示。此时修改son的prototype就不会影响father的prototype了。
4.2 雅虎继承(闭包实现私有化属性)(推荐使用)
var inherit = (function (){
var F = function () {};
return function (Target, Origin){
F.prototype = Origin.prototype;
Target.prototype = new F();
Target.prototype.constructor = Target;
Target.prototype.uber = Origin.prototype;
}
}());
立即执行函数+闭包,实现了一种更好的继承方式,F是私有的。
以上是我学习到的,如有错误,请大家指正。