精华内容
下载资源
问答
  • js继承图解

    2020-12-05 02:32:49
    继承是面向对象编程中讨论最多的话题。很多面向对象语言都支持两种继承:接口继承和实现继承。前者只继承方法签名,后者继承实际的方法。接口继承在 ECMAScript 中是不可能的,因为函数没有签名。实现继承是 ...

    语雀地址排版好一些:继承

    继承是面向对象编程中讨论最多的话题。很多面向对象语言都支持两种继承:接口继承和实现继承。前者只继承方法签名,后者继承实际的方法。接口继承在 ECMAScript 中是不可能的,因为函数没有签名。实现继承是 ECMAScript 唯一支持的继承方式,而这主要是通过原型链实现的。

     

    原型知识前置

    var Person = function(name){
      this.name = name; // tip: 当函数执行时这个 this 指的是谁?
    };
    Person.prototype.getName = function(){
      return this.name;  // tip: 当函数执行时这个 this 指的是谁?
    }
    var person1 = new Person('Mick');

     

    image.png

     

    继承

    原型链继承

    原型链定义为ECMA的主要继承方式。

    function Parent () {
        this.name = 'kevin';
    }
    
    Parent.prototype.getName = function () {
        console.log(this.name);
    }
    
    function Child () {
    
    }
    
    Child.prototype = new Parent();
    
    var child1 = new Child();
    
    console.log(child1.getName()) 
    

     

    原先是这样:

    image.png

     

    现在是这样:

    image.png

     

    Child创建的对象的__proto__指向new Parent对象。contructor指向Parent构造函数。这里有一个问题是没有将原型对象的contructor指向Child。加一行代码就可以了。
     

    Child.prototype.constructor = Child;
     

    缺点

    • 引用类型的属性被所有实例共享,值类型不会被共享,是因为当我们在执行[[get]]操作的时候会去查询原型链。执行[[put]]的时候,会自动给当前的对象创建一个属性
    • 在创建 Child 的实例时,不能向Parent传参

     

    子类型在实例化时不能给父类型的构造函数传参。事实上,我们无法在不影响所有对象实例的情况下把参数传进父类的构造函数。再加上之前提到的原型中包含引用值的问题,就导致原型链基本不会被单独使用。

     

    借用构造函数继承【经典继承】

    为了解决原型包含引用值导致的继承问题,一种叫作“盗用构造函数”(constructor stealing)的技术在开发社区流行起来(这种技术有时也称作“对象伪装”或“经典继承”)。基本思路很简单:在子类构造函数中调用父类构造函数。因为毕竟函数就是在特定上下文中执行代码的简单对象,所以可以使用apply()和 call()方法以新创建的对象为上下文执行构造函数。

    function Parent (name) {
        this.name = name;
    }
    
    function Child (name) {
        Parent.call(this, name);
    }
    
    var child1 = new Child('kevin');
    
    console.log(child1.name); // kevin
    
    var child2 = new Child('daisy');
    
    console.log(child2.name); // daisy
    原先是这样:

    image.png

     

    现在是这样:

    image.png

     

    从上图可以清楚看到:

    当前的继承方式是通过new操作的时候,绑定child的this执行Parent的方法,去执行一次Parent内部的方法。和原型链啥的没有任何关系。

     

    缺点

    盗用构造函数的主要缺点,也是使用构造函数模式自定义类型的问题:必须在构造函数中定义方法,因此函数不能重用【因为二者之间的函数是两个函数,本质是不相等的函数】。此外,子类也不能访问父类原型上定义的方法,因此所有类型只能使用构造函数模式。由于存在这些问题,盗用构造函数基本上也不能单独使用。

     

     

    组合继承

    组合继承(有时候也叫伪经典继承)综合了原型链和盗用构造函数,将两者的优点集中了起来。

    function Parent (name) {
        this.name = name;
        this.colors = ['red', 'blue', 'green'];
    }
    
    Parent.prototype.getName = function () {
        console.log(this.name)
    }
    
    function Child (name, age) {
        Parent.call(this, name);
        this.age = age;
    }
    
    Child.prototype = new Parent();
    Child.prototype.constructor = Child;
    
    var child1 = new Child('kevin', '18');
    
    child1.colors.push('black');
    
    console.log(child1.name); // kevin
    console.log(child1.age); // 18
    console.log(child1.colors); // ["red", "blue", "green", "black"]
    
    var child2 = new Child('daisy', '20');
    
    console.log(child2.name); // daisy
    console.log(child2.age); // 20
    console.log(child2.colors); // ["red", "blue", "green"]

     

    image.png

     

    组合继承弥补了原型链和盗用构造函数的不足,是 JavaScript 中使用最多的继承模式。而且组合继承也保留了 instanceof 操作符和 isPrototypeOf()方法识别合成对象的能力。

     

    原型式继承

    2006 年,Douglas Crockford 写了一篇文章:《JavaScript 中的原型式继承》(“Prototypal Inheritance in JavaScript”)。这篇文章介绍了一种不涉及严格意义上构造函数的继承方法。他的出发点是即使不自定义类型也可以通过原型实现对象之间的信息共享。

    function object(o) { 
     function F() {} 
     F.prototype = o; 
     return new F(); 
    }
    
    let person = { 
     name: "Nicholas", 
     friends: ["Shelby", "Court", "Van"] 
    }; 
    
    let anotherPerson = object(person); 
    anotherPerson.name = "Greg"; 
    anotherPerson.friends.push("Rob"); 
    
    let yetAnotherPerson = object(person); 
    yetAnotherPerson.name = "Linda"; 
    yetAnotherPerson.friends.push("Barbie"); 
    console.log(person.friends); // "Shelby,Court,Van,Rob,Barbie"

     

    image.png

     

    原型式继承虽然说是不自定义类型来实现原型对象之间的信息共享,但是我们可以发现object里面还是自定义了一个类型,去实现原型链继承的方式。

     

    特定场景:原型式继承非常适合不需要单独创建构造函数,你有一个对象,想在它的基础上再创建一个新对象。

     

    ECMAScript 5 通过增加 Object.create()方法将原型式继承的概念规范化了。这个方法接收两个参数:作为新对象原型的对象,以及给新对象定义额外属性的对象(第二个可选)。在

     

    寄生式继承

    与原型式继承比较接近的一种继承方式是寄生式继承(parasitic inheritance),也是 Crockford 首倡的一种模式。寄生式继承背后的思路类似于寄生构造函数和工厂模式:创建一个实现继承的函数,以某种方式增强对象,然后返回这个对象。

     

    这种模式依赖于上面的object方法,这里直接使用Obect.create()方法

    function createObj (o) {
        var clone = Object.create(o);
        clone.sayName = function () {
            console.log('hi');
        }
        return clone;
    }

    这里的图和上面的基本相同,就不画了。这里说的是一种思想,在createObj的方法里统一对我们创建的对象做一些处理。

     

    寄生组合式继承

    组合继承其实也存在效率问题。最主要的效率问题就是父类构造函数始终会被调用两次:一次在是创建子类原型时调用,另一次是在子类构造函数中调用。本质上,子类原型最终是要包含超类对象的所有实例属性,子类构造函数只要在执行时重写自己的原型就行了。

     

    可以翻到组合继承的地方,看到Parent会被调用两次,call一次,原型对象一次。

     

    image.png

     

    寄生式组合继承通过盗用构造函数继承属性,但使用混合式原型链继承方法。基本思路是不通过调用父类构造函数给子类原型赋值,而是取得父类原型的一个副本。说到底就是使用寄生式继承来继承父类原型,然后将返回的新对象赋值给子类原型。

     

    function inheritPrototype(subType, superType) {
        let 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() {
        console.log(this.name);
    };
    
    function SubType(name, age) {
        SuperType.call(this, name);
        this.age = age;
    }
    
    inheritPrototype(SubType, SuperType);
    SubType.prototype.sayAge = function() {
        console.log(this.age);
    };
    
    var subTupe1 = new SubType('name1', 1)
    var subTupe2 = new SubType('name2', 2)

    image.png

     

     

    总结继承

    image.png

     

    image.png

     

     

     

    展开全文
  • (上图 来自 大话设计模式) 合成/聚合复用原则,在实现代码重用时,首先考虑的是合成/聚合,而不是类的继承。 类继承性是严格的,如果父类的实现发生改变,则子类的实现也得跟着发生变化。 继承破坏类封装性。子类...

    image

    (上图 来自 大话设计模式)

     

    合成/聚合复用原则,在实现代码重用时,首先考虑的是合成/聚合,而不是类的继承。

    类继承性是严格的,如果父类的实现发生改变,则子类的实现也得跟着发生变化。

    继承破坏类封装性。子类必须和父类关联在一起。

    不合理的继承,带来混乱的语义。

    转载于:https://www.cnblogs.com/thomastao/archive/2009/11/29/1612922.html

    展开全文
  • 于javascript原型链的层层递进查找规则,以及原型对象(prototype)的共享特性,实现继承是非常简单的事情 一、把父类的实例对象赋给子类的原型对象(prototype),可以实现继承 1 function Person(){ 2 this....

    于javascript原型链的层层递进查找规则,以及原型对象(prototype)的共享特性,实现继承是非常简单的事情

    一、把父类的实例对象赋给子类的原型对象(prototype),可以实现继承

     1         function Person(){
     2             this.userName = 'ghostwu';
     3         }
     4         Person.prototype.showUserName = function(){
     5             return this.userName;
     6         }
     7         function Teacher (){}
     8         Teacher.prototype = new Person();
     9 
    10         var oT = new Teacher(); 
    11         console.log( oT.userName ); //ghostwu
    12         console.log( oT.showUserName() ); //ghostwu

    通过把父类(Person)的一个实例赋给子类Teacher的原型对象,就可以实现继承,子类的实例就可以访问到父类的属性和方法

    如果你不会画这个图,你需要去看下我的这篇文章:[js高手之路]一步步图解javascript的原型(prototype)对象,原型链

    第11行,执行oT.userName, 首先去oT对象上查找,很明显oT对象上没有任何属性,所以就顺着oT的隐式原型__proto__的指向查找到Teacher.prototype,

    发现还是没有userName这个属性,继续沿着Teacher.prototype.__proto__向上查找,找到了new Person() 这个实例上面有个userName,值为ghostwu

    所以停止查找,输出ghostwu.

    第12行,执行oT.showUserName前面的过程同上,但是在new Person()这个实例上还是没有查找到showUserName这个方法,继续沿着new Person()的

    隐式原型__proto__的指向( Person.prototype )查找,在Person.prototype上找到了showUserName这个方法,停止查找,输出ghostwu.

    二、把父类的原型对象(prototype)赋给子类的原型对象(prototype),可以继承到父类的方法,但是继承不到父类的属性

     1         function Person(){
     2             this.userName = 'ghostwu';
     3         }
     4         Person.prototype.showUserName = function(){
     5             return 'Person::showUserName方法';
     6         }
     7         function Teacher (){}
     8         Teacher.prototype = Person.prototype;
     9 
    10         var oT = new Teacher(); 
    11         console.log( oT.showUserName() ); //ghostwu
    12         console.log( oT.userName ); //undefined, 没有继承到父类的userName

    因为Teacher.prototype的隐式原型(__proto__)只指向Person.prototype,所以获取不到Person实例的属性

    三、发生继承关系后,实例与构造函数(类)的关系判断

    还是通过instanceof和isPrototypeOf判断

     1         function Person(){
     2             this.userName = 'ghostwu';
     3         }
     4         Person.prototype.showUserName = function(){
     5             return this.userName;
     6         }
     7         function Teacher (){}
     8         Teacher.prototype = new Person();
     9         
    10         var oT = new Teacher();
    11         console.log( oT instanceof Teacher ); //true
    12         console.log( oT instanceof Person ); //true
    13         console.log( oT instanceof Object ); //true
    14         console.log( Teacher.prototype.isPrototypeOf( oT ) ); //true
    15         console.log( Person.prototype.isPrototypeOf( oT ) ); //true
    16         console.log( Object.prototype.isPrototypeOf( oT ) ); //true

    四,父类存在的方法和属性,子类可以覆盖(重写),子类没有的方法和属性,可以扩展

     1         function Person() {}
     2         Person.prototype.showUserName = function () {
     3             console.log('Person::showUserName');
     4         }
     5         function Teacher() { }
     6         Teacher.prototype = new Person();
     7         Teacher.prototype.showUserName = function(){
     8             console.log('Teacher::showUserName');
     9         }
    10         Teacher.prototype.showAge = function(){
    11             console.log( 22 );
    12         }
    13         var oT = new Teacher();
    14         oT.showUserName(); //Teacher::showUserName
    15         oT.showAge(); //22

    五、重写原型对象之后,其实就是把原型对象的__proto__的指向发生了改变

    原型对象prototype的__proto__的指向发生了改变,会把原本的继承关系覆盖(切断)

     1         function Person() {}
     2         Person.prototype.showUserName = function () {
     3             console.log('Person::showUserName');
     4         }
     5         function Teacher() {}
     6         Teacher.prototype = new Person();
     7         Teacher.prototype = {
     8             showAge : function(){
     9                 console.log( 22 );
    10             }
    11         }
    12         var oT = new Teacher();
    13         oT.showAge(); //22
    14         oT.showUserName();

    上例,第7行,Teacher.prototype重写了Teacher的原型对象(prototype),原来第6行的原型对象的隐式原型(__proto__)指向就没有作用了

    所以在第14行,oT.showUserName() 就会发生调用错误,因为Teacher的原型对象(prototype)的隐式原型(__proto__)不再指向父类(Person)的实例,继承关系被破坏了.

    六、在继承过程中,小心处理实例的属性上引用类型的数据

     1         function Person(){
     2             this.skills = [ 'php', 'javascript' ];
     3         }
     4         function Teacher (){}
     5         Teacher.prototype = new Person();
     6 
     7         var oT1 = new Teacher();
     8         var oT2 = new Teacher();
     9         oT1.skills.push( 'linux' );
    10         console.log( oT2.skills ); //php, java, linux

    oT1的skills添加了一项linux数据,其他的实例都能访问到,因为其他实例中共享了skills数据,skills是一个引用类型

    七、借用构造函数

    为了消除引用类型影响不同的实例,可以借用构造函数,把引用类型的数据复制到每个对象上,就不会相互影响了

     1         function Person( uName ){
     2             this.skills = [ 'php', 'javascript' ];
     3             this.userName = uName;
     4         }
     5         Person.prototype.showUserName = function(){
     6             return this.userName;
     7         }
     8         function Teacher ( uName ){
     9             Person.call( this, uName );
    10         }
    11         var oT1 = new Teacher();
    12         oT1.skills.push( 'linux' );
    13         var oT2 = new Teacher();
    14         console.log( oT2.skills ); //php,javascript
    15         console.log( oT2.showUserName() );

     虽然oT1.skills添加了一项Linux,但是不会影响oT2.skills的数据,通过子类构造函数中call的方式,去借用父类的构造函数,把父类的属性复制过来,而且还能

    传递参数,如第8行,但是第15行,方法调用错误,因为在构造中只复制了属性,不会复制到父类原型对象上的方法

    八、组合继承(原型对象+借用构造函数)

    经过以上的分析, 单一的原型继承的缺点有:

    1、不能传递参数,如,

     Teacher.prototype = new Person();

    有些人说,小括号后面可以跟参数啊,没错,但是只要跟了参数,子类所有的实例属性,都是跟这个一样,说白了,还是传递不了参数

    2、把引用类型放在原型对象上,会在不同实例上产生相互影响

    单一的借用构造函数的缺点:

    1、不能复制到父类的方法

    刚好原型对象方式的缺点,借用构造函数可以弥补,借用构造函数的缺点,原型对象方式可以弥补,于是,就产生了一种组合继承方法:

     1         function Person( uName ){
     2             this.skills = [ 'php', 'javascript' ];
     3             this.userName = uName;
     4         }
     5         Person.prototype.showUserName = function(){
     6             return this.userName;
     7         }
     8         function Teacher ( uName ){
     9             Person.call( this, uName );
    10         }
    11         Teacher.prototype = new Person();
    12 
    13         var oT1 = new Teacher( 'ghostwu' );
    14         oT1.skills.push( 'linux' );
    15         var oT2 = new Teacher( 'ghostwu' );
    16         console.log( oT2.skills ); //php,javascript
    17         console.log( oT2.showUserName() ); //ghostwu

    子类实例oT2的skills不会受到oT1的影响,子类的实例也能调用到父类的方法.

    展开全文
  • UML中关系图解

    2012-09-03 19:56:57
    UML中关系图解 继承关系(Generalization); 实现关系(Realization); 依赖关系(Dependency); 关联关系(Association); 有方向的关联(DirectedAssociation); 聚合关系(Aggregation); ...

     

    1. 继承关系(Generalization);
    2. 实现关系(Realization);
    3. 依赖关系(Dependency);
    4. 关联关系(Association);
    5. 有方向的关联(DirectedAssociation);
    6. 聚合关系(Aggregation);
    7. 组合关系(Composition);

    继承关系(Generalization):

    Class B继承与Class A

    继承指的是一个类(称为子类、子接口)继承另外的一个类(称为父类、父接口)的功能,并可以增加它自己的新功能的能力,继承是类与类或者接口与接口 之间最常见的关系之一;在Java中此类关系通过关键字extends明确标识,在设计时一般没有争议性;

    实现关系(Realization):

    Class A实现了Interface A

     

    实现指的是一个class类实现interface接口(可以是多个)的功能;实现是类与接 口之间最常见的关系之一;在Java中此类关系通过关键字implements明确标识,在设计时一般没有争议性;

    依赖关系(Dependency):

    ClassA依赖于ClassB

     

    可以简单的理解,就是一个类A使用到了另一个类B,而这种使用关系是具有偶然性的、临时性 的、非常弱的,但是B类的变化会影响到A;表现在代码层面,为类B作为参数被类A在某个method方法中使用;

    关联关系(Association):

    ClassA与ClassB相互关联

     

    这里的关联关系分的比较细,把相互关联和有方向的关联区分开了,相互他体现的是两个类、或者 类与接口之间语义级别的一种强依赖关系,是一种长期的稳定的关系;表现在代码层面,为被关联类以类属性的形式出现在关联类中,也可能是关联类引用了一个类 型为被关联类的全局变量;

    有方向的关联(DirectedAssociation):

    ClassA关联于ClassB

     

    是关联的一种特别形式,是单向的;表现在代码层面,为被关联类B以类属性的形式出现在关联类 A中,也可能是关联类A引用了一个类型为被关联类B的全局变量;

    聚合关系(Aggregation):

    计算机 has-a cpu

     

    聚合是关联关系的一种特例,他体现的是整体与部分、拥有的关系,即has-a的关系,此时整 体与部分之间是可分离的,他们可以具有各自的生命周期,部分可以属于多个整体对象,也可以为多个整体对象共享;比如计算机与CPU;表现在代码层面,和关 联关系是一致的,只能从语义级别来区分;

    组合关系(Composition):

    孕妇 contains-a 胎儿

     

    组合也是关联关系的一种特例,他体现的是一种contains-a的关系,这种关系比聚合更 强,也称为强聚合;他同样体现整体与部分间的关系,但此时整体与部分是不可分的,整体的生命周期结束也就意味着部分的生命周期结束;孕妇死了胎儿自然也就 死了;表现在代码层面,和关联关系是一致的,只能从语义级别来区分.

    展开全文
  • 关系运算子在计算机科学的编程语言中,是测试或定义两个实体之间某种关系的构造或操作符。这些包括数值等式和不等式(例如 5 = 5 和 4≥3)。在具备布尔型别的编程语言中(如 Pascal,Ada 或 Java),这些运算符通常根据...
  • Java-组合继承

    2019-02-24 16:52:23
    1.组合继承都是Java代码复用的方式。 2.组合:指在新类中创建原有类对象,重复利用已有类的方法 3.继承:面向对象的主要特性之一,允许... 若非严格的 “子类is a父类“ 关系,就用组合。有更好的可扩展性。 ...
  • UML关系图解

    2011-05-31 15:32:00
    UML定义的关系主要有六种:继承,实现,依赖关联,聚合和组合。这些类间关系的理解和使用是掌握和应用UML的关键,而也就是这几种关系,往往会让初学者迷惑。这里给出这六种主要UML关系的说明和类图描述,一看之下,...
  • 集合继承关系图解

    2021-07-14 07:28:10
    java集合继承关系图  面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,就对对象进行存储,集合就是存储对象最常用的一种方式。  数组虽然也可以存储对象,但长度是固定的;集合长度是...
  • 图解js的继承

    2020-12-06 03:04:21
    让类与类之间产生了关系,是多态的前提 1.2 继承的类型: 单继承继承 不同类继承同一个类 多继承 2. js的继承 js本身最开始的设计只是为了实现网页提交表单时做个表单验证等简单功能 现在web端越来越重,导致...
  • UML中关系图解

    千次阅读 2011-03-17 14:51:00
    关系列表: 继承关系(Generalization); 实现关系(Realization); 依赖关系(Dependency);... 组合关系(Composition); 继承关系(Generalization): <br />Class B继承与Class A
  • UML六大关系图解

    千次阅读 2015-06-26 11:29:00
    UML定义的关系主要有六种:依赖、类属、关联、实现、聚合和组合。这些类间关系的理解和使用是掌握和应用UML的关键,而也就是这几种关系,往往会让初学者迷惑。这里给出这六种主要UML关系的说明和类图描述,一看之下...
  • 简单实用UML关系图解

    2017-06-06 09:43:00
    一句话UML,再记不住就要DPP了: 关系 图解 代码 备注 1:继承关系(Generalization) 2:实现关系(Realization) 3:依赖关系(Dependency) 方法的参数、局部变量、返回值 4:关联关系(Association) 互为类属性 5:方向...
  • JavaScript图解继承(多图)

    千次阅读 2016-04-08 13:47:54
    在JavaScript中,继承主要是通过原型链来实现的。原型链和前文所说的原型对象密切相关。原型对象可以参考JavaScript构造函数和原型对象。为了彻底搞清楚JavaScript的继承,我们先搞清楚原型链是什么。原型链继承我们...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,661
精华内容 1,064
关键字:

组合关系继承关系图解