继承小结

//父类
function Father(){  this.width=10;  this.data=[1,2,3];  this.key="this is Father";}

//子类
function Child(){}

//方法1
Child.prototype=new Father();

//方法2
Child.prototype=Father.prototype;

//方法3
Child.prototype=Object.create(Father.prototype);

Child.prototype.say=function(){
    //do something
}

方法1:

  • Child的原型指向Father的一个实例,不管创建多少个Child对象实例,它们的原型都指向同一个实例;
  • 子类会继承父类实例属性,原型链查找效率提高,能为一些属性提供合理的默认值;
  • 父类实例属性为引用类型时,不恰当地修改会导致所有子类被修改
  • 子类实例化对象的constructor属性指向错误。
  • 创建父类实例作为子类原型时,可能无法确定构造函数需要的合理参数,这样提供的参数继承给子类没有实际意义,当子类需要这些参数时应该在构造函数中进行初始化和设置。

分析:Child.prototype=new Father();产生Father的一个实例,同时赋值给Child的原型。也即:Child.prototype相当于对象{width:10,data:[1,2,3],key:"this is Father",[[Prototype]]:Father.prototype},这样就把Father的原型通过Child.prototype.[[Prototype]]这个对象属性保存起来,构成了原型的链接,但这样Child产生的对象的构造函数发生了改变,因为在Child中没有constructor属性,只能从原型链找到Father.prototype,读出constructor:Father

var one = new Child();
console.log(one.constructor);//输出Father()

所以,我们还要人为设回Child本身

Child.prototype.constructor =Child;
//现在Child的原型就变成了{width:10,data:[1,2,3],key:"this is Father",[[Prototype]]:Father.prototype,constructor:Child}
var one=new Child();
console.log(one.constructor);//输出Child()

当我们接着使用one.data.push(4);语句时,直接改变了prototype的data数组(引用)。所以有,

var two=new Child();
console.log(two.data);//输出 [1,2,3,4]

如果我们不想要父类Father自定义的属性,我们可以考虑将其过滤掉

//新建一个空函数
function F(){}
//把空函数的原型指向构造函数Father的原型
F.prototype=Father.prototype;
//这个时候再通过new操作把Child.prototype的原型链指向F的原型
Child.prototype=new F();
//这个时候Child的原型变成了{[[Prototype]] : F.prototype}
//这里F.prototype其实只是一个地址的引用
//但是由Child创建的实例其constructor指向了Father,所以这里要显示设置一下Child.prototype的constructor属性
Child.prototype.constructor=Child;
//这个时候Child的原型变成了{constructor:Child,[[Prototype]]:F.prototype}
//这样就实现了Child对Father的原型继承

总结:继承应该是继承方法而不是属性,为子类设置父类实例属性应该是通过在子类构造函数中调用父类构造函数进行初始化。

方法2:父类构造函数原型与子类相同,修改子类原型添加方法会修改父类

分析:Child.prototype=Father.prototype;相当于把Child的prototype指向了Father的prototype,这样只是继承了Father的prototype方法,Father中的自定义方法则不继承。所以,

Child.prototype.say = "this is Child";//这样也会改变Father的prototype
var f=new Father();
console.log(f.say);//输出this is Child

方法3:避免了方法1、2中的缺点,但是Object.create为EcmaScript 5方法,需要注意兼容性

改进:

所有三种方法应该在子类构造函数中调用父类构造函数实现实例属性初始化

function Child(){
    Father.call(this);
}

用新创建的对象替代子类默认原型,设置Child.prototype.constructor=Child;保证一致性

第三种方法的polyfill:

function create(obj){
    if(Object.create){
        return Object.create(obj);
    }
    function f(){};
    f.prototype=obj;
    return new f();
}
时间: 2024-08-04 20:55:48

继承小结的相关文章

js的原型继承小结

考虑:有一个想要复用的对象,并且想要创建的第二个对象需要从第一个对对象中获取其功能. 实现如下: //要继承的对象 var parent = { name:"Papa" }; //新对象 var child = object(parent); console.log(child.name); object函数实现如下: //原型继承 function object(o){ function F(){}; F.prototype = o; return new F(); }; 讨论: 可

虚函数与虚继承小结

  虚函数的作用就是实现多态性,通过指向派生类的基类指针或引用,访问派生类中同名覆盖成员函数:实现方法就是在函数返回值之前加上关键字"virtual":如下: #include <stdio.h> class A { public: void fn() { printf("fn in A\n"); } virtual void v_fn() { printf("virtual fn in A\n"); } }; class B : p

javascript的继承小结

继承是OO语言中最为津津乐道的概念,一般有两种继承方式,接口继承和实现继承,接口继承只继承方法签名,实现继承则是集成真正的方法.由于js中函数没有签名,因此ECMAScript中没有接口继承,只有实现继承,而且实现继承主要依靠原型链实现. 1.继承 2.作用域 3.闭包与匿名函数 (未完待续...)

day24 继承 接口 多态

抽象类与接口类 接口类 继承有两种用途: 一:继承基类的方法,并且做出自己的改变或者扩展(代码重用) 二:声明某个子类兼容于某基类,定义一个接口类Interface,接口类中定义了一些接口名(就是函数名)且并未实现接口的功能,子类继承接口类,并且实现接口中的功能 class Alipay: ''' 支付宝支付 ''' def pay(self,money): print('支付宝支付了%s元'%money) class Applepay: ''' apple pay支付 ''' def pay(

Python全栈之路系列----之-----面向对象4接口与抽象,多继承与多态)

接口类与抽像类 在python中,并没有接口类这种东西,即便不通过专门的模块定义接口,我们也应该有一些基本的概念 编程思想 归一化设计: 1.接口类 不实现具体的方法,并且可以多继承 2.抽象类 可以做一些基础实现,并且不推荐多继承 编程的几类原则: 开放封闭原则:对扩展示开放的,对修改是封闭的依赖倒置原则:高层模块不应该依赖低层模块,二者都应该依赖其抽象:抽象不应该应该依赖细节:细节应该依赖抽象.换言之,要针对接口编程,而不是针对实现编程接口隔离原则:使用多个专门的接口,而不使用单一的总接口.

继承、多态、封装、接口

继承部分 继承(加上封装和多态性)是面向对象的编程的三个主要特性(也称为"支柱")之一. 继承用于创建可重用.扩展和修改在其他类中定义的行为的新类. 其成员被继承的类称为"基类",继承这些成员的类称为"派生类". 派生类只能有一个直接基类. 但是,继承是可传递的. 如果 ClassB 派生出 ClassC,ClassA 派生出 ClassB,则 ClassC 会继承 ClassB 和 ClassA 中声明的成员. 定义一个类从其他类派生时,派生类

DOM笔记(十三):JavaScript的继承方式

在Java.C++.C#等OO语言中,都支持两种继承方式:接口继承和实现继承.接口继承制继承方法签名,实现继承则继承实际的方法和属性.在SCMAScript中,由于函数没有签名,所以无法实现接口继承,只支持实现继承. 实现继承主要依靠原型链来实现. 一.原型链 原型链是利用原型让一个引用类型继承另一个引用类型的方法,在DOM笔记(十二):又谈原型对象中,描述了构造函数.实例和原型之间的关系: 每个构造函数都有一个原型对象,原型对象包含一个指向构造函数的指针,而每个实例都包含一个指向原型对象的内部

ES5和ES6中对于继承的实现方法

在ES5继承的实现非常有趣的,由于没有传统面向对象类的概念,Javascript利用原型链的特性来实现继承,这其中有很多的属性指向和需要注意的地方. 原型链的特点和实现已经在之前的一篇整理说过了,就是通过将子类构造函数的原型作为父类构造函数的实例,这样就连通了子类-子类原型-父类,原型链的特点就是逐层查找,从子类开始一直往上直到所有对象的原型Object.prototype,找到属性方法之后就会停止查找,所以下层的属性方法会覆盖上层. 一个基本的基于原型链的继承过程大概是这样的: //先来个父类

Day7 初识面向对象,面向对象之继承与派生

一.面向对象引言 一.面向对象的程序设计的由来 详述见:http://www.cnblogs.com/linhaifeng/articles/6428835.html 二.面向对象引子 写一个简单程序:人狗大战 角色:人.狗 人角色特性:攻击力.生命值.名字.等级 狗角色特性:攻击力.生命值.名字.品种 1.用函数实现功能:角色和技能分别定义不同的函数 1 def person(attack,life_value,name,level): 2 person_dic = {'attack':att