JavaScript重难点——继承的发展及其特点

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

特点:

  1. 不能继承借用构造函数的原型
  2. 每次构造函数都要多走一个函数
能实现初步的构造函数属性和方法借用,不能借用原型,不是严格的继承。相当于用别人的方法实现自己的功能,视觉上节省代码,实际操作更麻烦。适用于B完全囊括A的情况,可以借用一下。

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是私有的。

以上是我学习到的,如有错误,请大家指正。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值