JS的继承 ES5;javascript的继承;

基础概念

说继承之前简单介绍一下,几个概念,有助于更深刻的理解继承。

  1. 能用口述或者文字的形式,说一下什么是函数吗?
    按照犀牛书的说法,函数简单的说就是重复执行的代码块。函数是这样的一段JavaScript 代码,它只定义一次,但可能被执行或调用任意次。

    //使用方式
    //1.直接声明
    function(){}
    //2.函数表达式
    let fun = function(){}
    //3. new Function()
    var fun1 = new Function (arg1 , arg2 ,arg3 ,, argN , body  )
  2. 能用口述或者文字的形式,说一下什么是构造函数吗?
    通过 new 函数名 来实例化对象的函数叫构造函数。任何的函数都可以作为构造函数存在。之所以有构造函数与普通函数之分,主要从功能上进行区别的,构造函数的主要 功能为 初始化对象,特点是和new 一起使用。new就是在创建对象,从无到有,构造函数就是在为初始化的对象添加属性和方法。构造函数定义时首字母大写(规范)。

    // 语法
    funtion SupType(name){
    	this.name = name;
    }
    var P = new SupType ('小张');
    P.name; // '小张'
    
  3. 能用口述或者文字的形式,说一下什么是原型吗?
    在JavaScript高级程序设计中给出的解释是· 每一个构造函数(对应的就是类函数)都有一个prototype属性(强调下是属性),这个prototype属性会指向一个原型对象(强调下是对象)。该原型属性指向的原型对象称之为原型

  4. 能用口述或者文字的形式,说一下什么是原型链吗?
    每一个构造函数的原型属性会链式指向原型对象,每个原型对象都会有个constructor属性会指向构造函数(未定义时默认指向构造函数)其中形成了一种链式结构,我们称之为原型链。

简单的原型链图
在这里插入图片描述

复杂的原型链图

在这里插入图片描述

上面的内容很重要,有助于你更深刻的理解,记忆下面的内容,请仔细看完!

ES5相对来说最完美的继承

什么是寄生组合继承?
寄生组合式继承,实际是通过借用构造函数来继承属性,通过原型链形式来继承方法,通过寄生可以不必为了指定子类的原型而调用超类构造函数,我们只需超类的原型副本即可——使用寄生式继承来继承超类原型,然后将结果(实例)指定给子类原型。

优点总结:
1、属性私有化(构造函数来继承属性);
2、方法公用(原型链形式来继承方法);
3、不调用超类构造函数(寄生特性)。

代码

// 继承-寄生函数
function inheritPrototype(subType, superType) {
	// 通过 Object.create 复制父类构造函数
    var prototype = Object.create(superType.prototype);
    // 把构造函数复制为子类
    prototype.constructor = subType;
    // 把构造函数复制为子类后影像了子类原型,子类原型更变为父类原型,所以需要改正
    subType.prototype = prototype;
}
// 父类构造函数
function SuperType(name) {
    this.name = name;
    this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function () {
    alert(this.name);
};
// 子类构造函数
function SubType(name, age) {
    //构造函数式继承--子类构造函数中执行父类构造函数
    SuperType.call(this, name);
    this.age = age;
}
// 核心:因为是对父类原型的复制,所以不包含父类的构造函数,也就不会调用两次父类的构造函数造成浪费
inheritPrototype(SubType, SuperType);
SubType.prototype.sayAge = function () {
    alert(this.age);
}
var instance = new SubType("lichonglou");
console.log(instance.name)
// 指向SubType 如果没有修正原型的构造函数,则会指向父类构造函数
console.log(instance.constructor) 

ES5其他继承方法

构造函数继承

也有2种继承方式,冒充继承,绑定this方式继承

冒充继承

function Person(name,age){
  this.name = name ;
  this.age = age;
  this.showName = function(){
      console.log('我是'+name);
  }
}
/** 
 *  @description 以自身运行环境运行函数,这时函数内的this均指向Child,
 *  因此父类的属性全部移植到子类中
 */
function Child(){
  //这三句代码最关键
  this.temp = Person; //创建一个自身缓存函数并将父类构造赋值
  this.temp('李端','26');
  delete this.temp;//删除缓存函数
}
var child = new Child();
child.showName();//我是李端

绑定this方式继承

function Person(name,age){
  this.name = name ;
  this.age = age;
  this.showName = function(){
       console.log('我是'+name);
  }
}

/** 
 *  @description 以自身运行环境运行函数,这时函数内的this均指向Child,
 *  因此父类的属性全部移植到子类中
 */
function Child(){
   // 改变this指向的方法有3个,下面分别是 bind,call,apply的用法
   Person.bind(this)('李端','26'); //绑定this到Person运行环境执行函数
   // Person.call(this,'李端','26');
   // Person.apply(this,['李端','26']);
}
var child = new Child();
child.showName(); //我是李端

优点:
属性私有化,在创建Child的实例时,可以向Parent传递参数

缺点:
因为方法和属性只能写在构造函数中,因此不能实现函数复用 只能继承父类的实例属性和方法,不能继承原型属性/方法

原型继承
function Person(name,age){
  this.name = name;
  this.age = age;
}
Person.prototype.sayHello = function(){
  alert('使用原型得到'+this.name);
}
var per = new Person('李端','26');
per.sayHello();
//创建新对象并实现继承
function Student(){};
Student.prototype = new Person('端瑞','23')
Student.constructor = Student
var stu = new Student();
stu.sayHello();

优点:
父类原型被继承,方法可复用
缺点:
1.引用值共享问题
2.不能传参、在创建Child的实例时,不能向Parent传递参数;如果传递也不会有作用

寄生继承

寄生式(parasitic)继承是与原型式继承紧密相关的一种思路,并且同样也是由克罗克福德推而广之的。寄生式继承的思路与寄生构造函数和工厂模式类似,即创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再像真地是它做了所有工作一样返回对象。以下代码示范了寄生式继承模式。
在这个例子中,createAnother()函数接收了一个参数,也就是将要作为新对象基础的对象。然后,把这个对象(original)传递给 object()函数,将返回的结果赋值给 clone。再为 clone 对象添加一个新方法 sayHi(),最后返回 clone 对象。可以像下面这样来使用 createAnother()函数:

function createAnother(original){
 var clone = Object(original); //通过调用函数创建一个新对象
 clone.sayHi = function(){ //以某种方式来增强这个对象
 alert("hi");
 };
 return clone; //返回这个对象
}
var person = {
 name: "Nicholas",
 friends: ["Shelby", "Court", "Van"]
};
var anotherPerson = createAnother(person);
anotherPerson.sayHi(); //"hi" 

重点:
就是给原型式继承外面套了个壳子。

优点:
没有创建自定义类型,因为只是套了个壳子返回对象(这个),这个函数顺理成章就成了创建的新对象。

缺点:
没用到原型,无法复用。

组合继承

组合继承其实就是,取构造函数的继承的优点(属性私有化) + 原型的优点(方法公用),让其构造继承属性,原型继承方法。

实现的方法很多,但是大同小异,下面展示一种:

function SuperType(name){
 this.name = name;
 this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){
 alert(this.name);
}; 

function SubType(name, age){
 //继承属性
 SuperType.call(this, name);
 this.age = age;
} 
//继承方法
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function(){
 alert(this.age);
};
var instance1 = new SubType("Nicholas", 29);
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
instance1.sayName(); //"Nicholas";
instance1.sayAge(); //29
var instance2 = new SubType("Greg", 27);
alert(instance2.colors); //"red,blue,green"
instance2.sayName(); //"Greg";
instance2.sayAge(); //27 

优点(结合了原型链继承和构造函数继承的优点):
1.Parent上的原型可以被继承
2.解决了引用值共享的问题
3.可以通过Child向Parent传参

缺点:
函数多执行了一次call
组合继承最大的缺点是会调用两次父构造函数。

一次是设置子类型实例的原型的时候:

SubType.prototype = new SuperType();
一次在创建子类型实例的时候:

var instance1 = new SubType(“Nicholas”, 29);
回想下 new 的模拟实现,其实在这句中,我们会执行:

SuperType.call(this, name);
在这里,我们又会调用了一次 Parent 构造函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值