精华内容
下载资源
问答
  • ECMAScript 继承机制实现

    千次阅读 2010-09-02 14:28:00
    继承机制的实现要用 ECMAScript 实现继承机制,您可以从要继承的基类入手。所有开发者定义的类都可作为基类。出于安全原因,本地类和宿主类不能作为基类,这样可以防止公用访问编译过的浏览器级的代码,因为这些代码...

    继承机制的实现

    要用 ECMAScript 实现继承机制,您可以从要继承的基类入手。所有开发者定义的类都可作为基类。出于安全原因,本地类和宿主类不能作为基类,这样可以防止公用访问编译过的浏览器级的代码,因为这些代码可以被用于恶意攻击。

    选定基类后,就可以创建它的子类了。是否使用基类完全由你决定。有时,你可能想创建一个不能直接使用的基类,它只是用于给子类提供通用的函数。在这种情况下,基类被看作抽象类。

    尽管 ECMAScript 并没有像其他语言那样严格地定义抽象类,但有时它的确会创建一些不允许使用的类。通常,我们称这种类为抽象类。

    创建的子类将继承超类的所有属性和方法,包括构造函数及方法的实现。记住,所有属性和方法都是公用的,因此子类可直接访问这些方法。子类还可添加超类中没有的新属性和方法,也可以覆盖超类的属性和方法。

    继承的方式

    和其他功能一样,ECMAScript 实现继承的方式不止一种。这是因为 JavaScript 中的继承机制并不是明确规定的,而是通过模仿实现的。这意味着所有的继承细节并非完全由解释程序处理。作为开发者,你有权决定最适用的继承方式。

    下面为您介绍几种具体的继承方式。

    对象冒充

    构想原始的 ECMAScript 时,根本没打算设计对象冒充(object masquerading)。它是在开发者开始理解函数的工作方式,尤其是如何在函数环境中使用 this 关键字后才发展出来。

    其原理如下:构造函数使用 this 关键字给所有属性和方法赋值(即采用类声明的构造函数方式)。因为构造函数只是一个函数,所以可使 ClassA 构造函数成为 ClassB 的方法,然后调用它。ClassB 就会收到 ClassA 的构造函数中定义的属性和方法。例如,用下面的方式定义 ClassA 和 ClassB:

    function ClassA(sColor) {
        this.color = sColor;
        this.sayColor = function () {
            alert(this.color);
        };
    }
    
    function ClassB(sColor) {
    }
    

     

    还记得吗?关键字 this 引用的是构造函数当前创建的对象。不过在这个方法中,this 指向的所属的对象。这个原理是把 ClassA 作为常规函数来建立继承机制,而不是作为构造函数。如下使用构造函数 ClassB 可以实现继承机制:

    function ClassB(sColor) {
        this.newMethod = ClassA;
        this.newMethod(sColor);
        delete this.newMethod;
    }
    

     

    在这段代码中,为 ClassA 赋予了方法 newMethod(请记住,函数名只是指向它的指针)。然后调用该方法,传递给它的是 ClassB 构造函数的参数 sColor。最后一行代码删除了对 ClassA 的引用,这样以后就不能再调用它。

    所有新属性和新方法都必须在删除了新方法的代码行后定义。否则,可能会覆盖超类的相关属性和方法:

    function ClassB(sColor, sName) {
        this.newMethod = ClassA;
        this.newMethod(sColor);
        delete this.newMethod;
    
        this.name = sName;
        this.sayName = function () {
            alert(this.name);
        };
    }
    

     

    为证明前面的代码有效,可以运行下面的例子:

    var objA = new ClassA("blue");
    var objB = new ClassB("red", "John");
    objA.sayColor();	//输出 "blue"
    objB.sayColor();	//输出 "red"
    objB.sayName();	//输出 "John"
    

    对象冒充可以实现多重继承

    有趣的是,对象冒充可以支持多重继承。也就是说,一个类可以继承多个超类。用 UML 表示的多重继承机制如下图所示:

     

    例如,如果存在两个类 ClassX 和 ClassY,ClassZ 想继承这两个类,可以使用下面的代码:

    function ClassZ() {
        this.newMethod = ClassX;
        this.newMethod();
        delete this.newMethod;
    
        this.newMethod = ClassY;
        this.newMethod();
        delete this.newMethod;
    }
    

     

    这里存在一个弊端,如果存在两个类 ClassX 和 ClassY 具有同名的属性或方法,ClassY 具有高优先级,因为它从后面的类继承。除这点小问题之外,用对象冒充实现多重继承机制轻而易举。

    由于这种继承方法的流行,ECMAScript 的第三版为 Function 对象加入了两个方法,即 call() 和 apply()。

    call() 方法

    call() 方法是与经典的对象冒充方法最相似的方法。它的第一个参数用作 this 的对象。其他参数都直接传递给函数自身。例如:

    function sayColor(sPrefix,sSuffix) {
        alert(sPrefix + this.color + sSuffix);
    };
    
    var obj = new Object();
    obj.color = "blue";
    
    sayColor.call(obj, "The color is ", "a very nice color indeed.");
    

     

    在这个例子中,函数 sayColor() 在对象外定义,即使它不属于任何对象,也可以引用关键字 this。对象 obj 的 color 属性等于 blue。调用 call() 方法时,第一个参数是 obj,说明应该赋予 sayColor() 函数中的 this 关键字值是 obj。第二个和第三个参数是字符串。它们与 sayColor() 函数中的参数 sPrefix 和 sSuffix 匹配,最后生成的消息 "The color is blue, a very nice color indeed." 将被显示出来。

    要与继承机制的对象冒充方法一起使用该方法,只需将前三行的赋值、调用和删除代码替换即可:

    function ClassB(sColor, sName) {
        //this.newMethod = ClassA;
        //this.newMethod(color);
        //delete this.newMethod;
        ClassA.call(this, sColor);
    
        this.name = sName;
        this.sayName = function () {
            alert(this.name);
        };
    }
    

     

    这里,我们需要让 ClassA 中的关键字 this 等于新创建的 ClassB 对象,因此 this 是第一个参数。第二个参数 sColor 对两个类来说都是唯一的参数。

    apply() 方法

    apply() 方法有两个参数,用作 this 的对象和要传递给函数的参数的数组。例如:

    function sayColor(sPrefix,sSuffix) {
        alert(sPrefix + this.color + sSuffix);
    };
    
    var obj = new Object();
    obj.color = "blue";
    
    sayColor.apply(obj, new Array("The color is ", "a very nice color indeed."));
    

     

    这个例子与前面的例子相同,只是现在调用的是 apply() 方法。调用 apply() 方法时,第一个参数仍是 obj,说明应该赋予 sayColor() 函数中的 this 关键字值是 obj。第二个参数是由两个字符串构成的数组,与 sayColor() 函数中的参数 sPrefix 和 sSuffix 匹配,最后生成的消息仍是 "The color is blue, a very nice color indeed.",将被显示出来。

    该方法也用于替换前三行的赋值、调用和删除新方法的代码:

    function ClassB(sColor, sName) {
        //this.newMethod = ClassA;
        //this.newMethod(color);
        //delete this.newMethod;
        ClassA.apply(this, new Array(sColor));
    
        this.name = sName;
        this.sayName = function () {
            alert(this.name);
        };
    }
    

     

    同样的,第一个参数仍是 this,第二个参数是只有一个值 color 的数组。可以把 ClassB 的整个 arguments 对象作为第二个参数传递给 apply() 方法:

    function ClassB(sColor, sName) {
        //this.newMethod = ClassA;
        //this.newMethod(color);
        //delete this.newMethod;
        ClassA.apply(this, arguments);
    
        this.name = sName;
        this.sayName = function () {
            alert(this.name);
        };
    }
    

     

    当然,只有超类中的参数顺序与子类中的参数顺序完全一致时才可以传递参数对象。如果不是,就必须创建一个单独的数组,按照正确的顺序放置参数。此外,还可使用 call() 方法。

    原型链(prototype chaining)

    继承这种形式在 ECMAScript 中原本是用于原型链的。上一章介绍了定义类的原型方式。原型链扩展了这种方式,以一种有趣的方式实现继承机制。

    在上一章学过,prototype 对象是个模板,要实例化的对象都以这个模板为基础。总而言之,prototype 对象的任何属性和方法都被传递给那个类的所有实例。原型链利用这种功能来实现继承机制。

    如果用原型方式重定义前面例子中的类,它们将变为下列形式:

    function ClassA() {
    }
    
    ClassA.prototype.color = "blue";
    ClassA.prototype.sayColor = function () {
        alert(this.color);
    };
    
    function ClassB() {
    }
    
    ClassB.prototype = new ClassA();
    

     

    原型方式的神奇之处在于突出显示的粉红色代码行。这里,把 ClassB 的 prototype 属性设置成 ClassA 的实例。这很有意思,因为想要 ClassA 的所有属性和方法,但又不想逐个将它们 ClassB 的 prototype 属性。还有比把 ClassA 的实例赋予 prototype 属性更好的方法吗?

    注意:调用 ClassA 的构造函数,没有给它传递参数。这在原型链中是标准做法。要确保构造函数没有任何参数。

    与对象冒充相似,子类的所有属性和方法都必须出现在 prototype 属性被赋值后,因为在它之前赋值的所有方法都会被删除。为什么?因为 prototype 属性被替换成了新对象,添加了新方法的原始对象将被销毁。所以,为 ClassB 类添加 name 属性和 sayName() 方法的代码如下:

    function ClassB() {
    }
    
    ClassB.prototype = new ClassA();
    
    ClassB.prototype.name = "";
    ClassB.prototype.sayName = function () {
        alert(this.name);
    };
    

     

    可通过运行下面的例子测试这段代码:

    var objA = new ClassA();
    var objB = new ClassB();
    objA.color = "blue";
    objB.color = "red";
    objB.name = "John";
    objA.sayColor();
    objB.sayColor();
    objB.sayName();
    

     

    此外,在原型链中,instanceof 运算符的运行方式也很独特。对 ClassB 的所有实例,instanceof 为 ClassA 和 ClassB 都返回 true。例如:

    var objB = new ClassB();
    alert(objB instanceof ClassA);	//输出 "true"
    alert(objB instanceof ClassB);	//输出 "true"
    

     

    在 ECMAScript 的弱类型世界中,这是极其有用的工具,不过使用对象冒充时不能使用它。

    原型链的弊端是不支持多重继承。记住,原型链会用另一类型的对象重写类的 prototype 属性。

    混合方式

    这种继承方式使用构造函数定义类,并非使用任何原型。对象冒充的主要问题是必须使用构造函数方式,这不是最好的选择。不过如果使用原型链,就无法使用带参数的构造函数了。开发者如何选择呢?答案很简单,两者都用。

    在前一章,我们曾经讲解过创建类的最好方式是用构造函数定义属性,用原型定义方法。这种方式同样适用于继承机制,用对象冒充继承构造函数的属性,用原型链继承 prototype 对象的方法。用这两种方式重写前面的例子,代码如下:

    function ClassA(sColor) {
        this.color = sColor;
    }
    
    ClassA.prototype.sayColor = function () {
        alert(this.color);
    };
    
    function ClassB(sColor, sName) {
        ClassA.call(this, sColor);
        this.name = sName;
    }
    
    ClassB.prototype = new ClassA();
    
    ClassB.prototype.sayName = function () {
        alert(this.name);
    };
    

     

    在此例子中,继承机制由两行突出显示的粉红色代码实现。在第一行突出显示的代码中,在 ClassB 构造函数中,用对象冒充继承 ClassA 类的 sColor 属性。在第二行突出显示的代码中,用原型链继承 ClassA 类的方法。由于这种混合方式使用了原型链,所以 instanceof 运算符仍能正确运行。

    下面的例子测试了这段代码:

    var objA = new ClassA("blue");
    var objB = new ClassB("red", "John");
    objA.sayColor();	//输出 "blue"
    objB.sayColor();	//输出 "red"
    objB.sayName();	//输出 "John"
    

     

     

    转载声明: 本文转自 http://www.w3school.com.cn/js/pro_js_inheritance_implementing.asp (W3School)

    展开全文
  • PHP 多继承实现

    千次阅读 2019-11-15 14:58:38
    结合设计模式合理设计实现代码 2.学习/操作 1.引言 记得有一道面试题问php是否支持多继承? //不支持的原因跟其他语言的原因相同: 如java 都是多继承带来的菱形继承问题(又叫钻石...

    1.应用场景

    结合设计模式合理设计实现代码

    2.学习/操作

    1.引言

    记得有一道面试题问php是否支持多继承?

    //不支持的原因跟其他语言的原因相同: 如java  都是多继承带来的菱形继承问题(又叫钻石问题)

    详细解释:

    假设类 B 和类 C 继承自类 A,且都重写了类 A 中的同一个方法,而类 D 同时继承了类 B 和类 C,那么此时类 D 会继承 B、C 的方法,那对于 B、C 重写的 A 中的方法,类 D 会继承哪一个呢?这里就会产生歧义。

    这个问题: 可以{java jdk1.8}通过子类强制重写父类/或者来解决


    答案:不可以,只支持单继承。
    如何实现多继承呢?
    答案:可以使用 interface 或 trait 实现 。

    //和使用__call魔术方法去实现多继承 见下面
    为什么会想到这个问题, 因为想到如果类继承多个接口,然后他们之间还有相同的属性和方法会引用谁的方法或属性,谁又会被覆盖?
    总结:
    1.使用 interface 声明类不能被实例化,并且属性必须是常量,方法不能有方法体 
    2.trait 声明的类不能被实例化,由use引入,会覆盖父类的相同属性及方法,如果有多个use,那么按顺序下面的覆盖最上面的相同的属性及方法

    2.接口与trait

    接口是什么?

    使用接口(interface),可以指定某个类必须实现哪些方法,但不需要定义这些方法的具体内容。

    接口是通过 interface 关键字来定义的,就像定义一个标准的类一样,但其中定义所有的方法都是空的。

    接口中定义的所有方法都必须是公有,这是接口的特性。

    trait是什么?

    看上去既像类又像接口,其实都不是,Trait可以看做类的部分实现,可以混入一个或多个现有的PHP类中,

    其作用有两个:表明类可以做什么;提供模块化实现。

    Trait是一种代码复用技术,为PHP的单继承限制提供了一套灵活的代码复用机制。

    3.使用接口与trait

    什么时候用接口?

    1、定规范,保持统一性;

    2、多个平级的类需要去实现同样的方法,只是实现方式不一样

    接口使用规范

    接口不能实例化

    接口的属性必须是常量

    接口的方法必须是public【默认public】,且不能有函数体

    类必须实现接口的所有方法

    一个类可以同时实现多个接口,用逗号隔开

    接口可以继承接口【用的少】

     

    interface usb{
        const brand = 'siemens';    // 接口的属性必须是常量
        public function connect();  // 接口的方法必须是public【默认public】,且不能有函数体
    }
    // new usb();  // 接口不能实例化

    // 类实现接口
    class Android implements usb{
        public function connect(){  // 类必须实现接口的所有方法
            echo '实现接口的connect方法';
        }
    }

    interface usbA{
        public function connect();
    }

    interface usbB{
        public function contact();
    }

    // 类可以同时实现多个接口
    class mi implements usbA,usbB{
        public function connect(){

        }
        public function contact(){

        }
    }

    php中trait的使用
    1、php中的trait是啥?

    看上去既像类又像接口,其实都不是,Trait可以看做类的部分实现,可以混入一个或多个现有的PHP类中,其作用有两个:表明类可以做什么;提供模块化实现。Trait是一种代码复用技术,为PHP的单继承限制提供了一套灵活的代码复用机制。

    2、PHP版本要求:
    php5.4开始引入trait,其目的就是在于减少代码的重复,增加代码的复用性。

    3、trait的使用场景:
    试想这样一种情况,当有一个方法需要在很多的类中使用时,该怎么处理?
    通常一般的处理方式会是,写一个基础类,在基类中实现这个方法,然后所有类都继承这个基类。

    这是一种处理方法,但不是最好的处理方式。通常采用继承的情况是:几个类具有很大的相似性。比如人作为一个基类,学生、工人、等继承“人”这个基类来扩展。

    由此,trait的作用就出来了,trait 可以在多个类中使用。

    4、trait如何使用:
    引用PHP手册中的例子:

    例子一

    <?php

    trait ezcReflectionReturnInfo {

        function getReturnType() { /*1*/ }

        function getReturnDescription() { /*2*/ }

    }

     

    class ezcReflectionMethod extends ReflectionMethod {

        use ezcReflectionReturnInfo;

        /* ... */

    }

     

    class ezcReflectionFunction extends ReflectionFunction {

        use ezcReflectionReturnInfo;

        /* ... */

    }

    ?>

    1.先声明一个trait;
    2.在类中使用use将该trait引入。
    是不是非常简单(手动逃)?需要注意的是trait的优先级。

    trait的优先级
    从基类继承的成员会被 trait 插入的成员所覆盖。优先顺序是来自当前类的成员覆盖了 trait 的方法,而 trait 则覆盖了被继承的方法。
    优先级:自身方法>trait的方法>继承的方法(就是这样子的。)

    看例子

    <?php

    trait HelloWorld {

        public function sayHello() {

            echo 'Hello World!';

        }

    }

     

    class TheWorldIsNotEnough {

        use HelloWorld;

        public function sayHello() {

            echo 'Hello Universe!';

        }

    }

     

    $o new TheWorldIsNotEnough();

    $o->sayHello();//输出是 Hello Universe!

    ?>

     

    还有一点需要注意的是:多个trait的使用。

    <?php

    trait Hello {

        public function sayHello() {

            echo 'Hello ';

        }

    }

     

    trait World {

        public function sayWorld() {

            echo 'World';

        }

    }

     

    class MyHelloWorld {

        use Hello, World;

        public function sayExclamationMark() {

            echo '!';

        }

    }

     

    $o new MyHelloWorld();

    $o->sayHello();

    $o->sayWorld();

    $o->sayExclamationMark();

    ?>

    4.总结

    Trait是一种代码复用技术,为PHP的单继承限制提供了一套灵活的代码复用机制。
    附:以上是trait的基本使用,其还有几个比较高级的使用注意点,详细的使用方法可以参见PHP手册,这里给出链接:PHP手册中trait的使用!以上内容中的例子都是来自于该手册。

    5.PHP通过_call实现多继承

    备注:

    _call:php的一个魔术方法,当调用类中不存在的method时,会自动调用_call.

    class One{

        function method_1(){
            echo '11<br/>';
        }

        function method_2(){
            echo '22<br/>';
        }

    }

    class Two{

        function method_3(){
            echo '33<br/>';
        }

        function method_4(){
            echo '44<br/>';
        }

    }

    class StaticDemo{

        protected $Class = array();

        public function __construct(array $class = array()){
            $this->Class = $class;
        }

        public function __call($name, $arguments)
        {
            // TODO: Implement __call() method.
            foreach ($this->Class as $v){
                if (is_callable(array($v, $name))) {
                    //call_user_func_array在上篇文章中已作出理解
                    return call_user_func_array(array($v, $name), $arguments);
                }
            }
            return call_user_func_array(array($this, $name), $arguments);
        }

    }

    $obj = new StaticDemo(array(new One(), new Two()));
    $obj->method_1();
    $obj->method_3();

    后续补充

    ...

    3.问题/补充

    TBD

    4.参考

     

    http://php.net/manual/zh/language.oop5.traits.php
    http://laravelacademy.org/post/4281.html
    http://www.jianshu.com/p/47f0cdbe9b2c

    https://www.cnblogs.com/minigrasshopper/p/7754512.html  //接口

    https://blog.csdn.net/lemony521/article/details/78322652  //trait

    https://cloud.tencent.com/developer/article/1406124  //PHP通过_call实现多继承

    https://time.geekbang.org/column/article/161114  //极客时间-设计模式之美-05 | 理论二:封装、抽象、继承、多态分别可以解决哪些编程问题?

    后续补充

    ...

    展开全文
  • [C++]接口继承实现继承

    万次阅读 2007-12-07 11:45:00
    接口继承实现继承MoaKap所谓接口...虚函数:虚函数是指一个类中你希望重载的成员函数,当你用一个基类指针或引用指向一个继承类对象的时候,你调用一个虚函数,实际调用的是继承类的版本。——MSDN虚函数用来表现基类
      
    
    接口继承与实现继承
    MoaKap
    所谓接口继承,就是派生类只继承函数的接口,也就是声明;而实现继承,就是派生类同时继承函数的接口和实现。
    我们都很清楚C++中有几个基本的概念,虚函数、纯虚函数、非虚函数。
    虚函数:
    虚函数是指一个类中你希望重载的成员函数,当你用一个基类指针或引用指向一个继承类对象的时候,你调用一个虚函数,实际调用的是继承类的版本。——MSDN

    虚函数用来表现基类和派生类的成员函数之间的一种关系.
    +`%K i;l"Z @2J-^5/0
    虚函数的定义在基类中进行,在需要定义为虚函数的成员函数的声明前冠以关键字 virtual. 航大博客-中国民航大学个人门户N-D&{'d t0/
    基类中的某个成员函数被声明为虚函数后,此虚函数就可以在一个或多个派生类中被重新定义. 航大博客-中国民航大学个人门户;P,q4B%Q2g
    在派生类中重新定义时,其函数原型,包括返回类型,函数名,参数个数,参数类型及参数的先后顺序,都必须与基类中的原型完全相同.
    Alz&P­Z Y7m0
    虚函数是重载的一种表现形式,是一种动态的重载方式.

    纯虚函数:
    纯虚函数在基类中没有定义,它们被初始化为0。
    任何用纯虚函数派生的类,都要自己提供该函数的具体实现。
    定义纯虚函数
    virtual void fun(void) = 0;
    非虚函数:
    一般成员函数,无virtual关键字修饰。
     
    至于为什么要定义这些函数,我们可以将虚函数、纯虚函数和非虚函数的功能与接口继承与实现继承联系起来:
    声明一个纯虚函数( pure virtual )的目的是为了让派生类只继承函数接口,也就是上面说的接口继承。
    纯虚函数一般是在不方便具体实现此函数的情况下使用。也就是说基类无法为继承类规定一个统一的缺省操作,但继承类又必须含有这个函数接口,并对其分别实现。但是,在C++中,我们是可以为纯虚函数提供定义的,只不过这种定义对继承类来说没有特定的意义。因为继承类仍然要根据各自需要实现函数。
    通俗说,纯虚函数就是要求其继承类必须含有该函数接口,并对其进行实现。是对继承类的一种接口实现要求,但并不提供缺省操作,各个继承类必须分别实现自己的操作。
    声明非纯虚函数( impure virtual )的目的是让继承类继承该函数的接口和缺省实现。
    与纯虚函数唯一的不同就是其为继承类提供了缺省操作,继承类可以不实现自己的操作而采用基类提供的默认操作。
    声明非虚函数( non-virtual )的目的是为了令继承类继承函数接口及一份强制性实现。
    相对于虚函数来说,非虚函数对继承类要求的更为严格,继承类不仅要继承函数接口,而且也要继承函数实现。也就是为继承类定义了一种行为。
     
    总结:
    纯虚函数: 要求继承类必须含有某个接口,并对接口函数实现。
    虚函数:继承类必须含有某个接口,可以自己实现,也可以不实现,而采用基类定义的缺省实现。
    非虚函数:继承类必须含有某个接口,必须使用基类的实现。
     
     


    展开全文
  • es6类和继承实现原理

    千次阅读 2019-01-08 22:53:15
    在阅读文章之前,您至少需要对...javascript使用的是原型式继承,我们可以通过原型的特性实现类的继承, es6为我们提供了像面向对象继承一样的语法糖。 class Parent { constructor(a){ this.filed1 = a; } ...

    在阅读文章之前,您至少需要对JavaScript原型继承有一定了解,如果觉得有所欠缺,可以先了解下我这篇文章:https://blog.csdn.net/qq_34149805/article/details/86105227

    1.es6 class 使用

    javascript使用的是原型式继承,我们可以通过原型的特性实现类的继承,
    es6为我们提供了像面向对象继承一样的语法糖。

    class Parent {
      constructor(a){
        this.filed1 = a;
      }
      filed2 = 2;
      func1 = function(){}
    }
    
    class Child extends Parent {
        constructor(a,b) {
          super(a);
          this.filed3 = b;
        }
      
      filed4 = 1;
      func2 = function(){}
    }
    

    下面我们借助babel来探究es6类和继承的实现原理。

    1.类的实现

    转换前:

    class Parent {
      constructor(a){
        this.filed1 = a;
      }
      filed2 = 2;
      func1 = function(){}
    }
    
    

    转换后:

    function _classCallCheck(instance, Constructor) {
      if (!(instance instanceof Constructor)) {
        throw new TypeError("Cannot call a class as a function");
      }
    }
    
    var Parent = function Parent(a) {
      _classCallCheck(this, Parent);
    
      this.filed2 = 2;
    
      this.func1 = function () { };
    
      this.filed1 = a;
    };
    

    可见class的底层依然是构造函数:

    1.调用_classCallCheck方法判断当前函数调用前是否有new关键字。

    构造函数执行前有new关键字,会在构造函数内部创建一个空对象,将构造函数的proptype指向这个空对象的_proto_,并将this指向这个空对象。如上,_classCallCheck中:this instanceof Parent 返回true。

    若构造函数前面没有new则构造函数的proptype不会不出现在this的原型链上,返回false。

    2.将class内部的变量和函数赋给this

    3.执行constuctor内部的逻辑

    4.return this (构造函数默认在最后我们做了)。

    2.继承实现

    转换前:

    class Child extends Parent {
        constructor(a,b) {
          super(a);
          this.filed3 = b;
        }
      
      filed4 = 1;
      func2 = function(){}
    }
    

    转换后:

    我们先看Child内部的实现,再看内部调用的函数是怎么实现的:

    var Child = function (_Parent) {
      _inherits(Child, _Parent);
    
      function Child(a, b) {
        _classCallCheck(this, Child);
    
        var _this = _possibleConstructorReturn(this, (Child.__proto__ || Object.getPrototypeOf(Child)).call(this, a));
    
        _this.filed4 = 1;
    
        _this.func2 = function () {};
    
        _this.filed3 = b;
        return _this;
      }
    
      return Child;
    }(Parent);
    

    1.调用_inherits函数继承父类的proptype。

    _inherits内部实现:

    function _inherits(subClass, superClass) {
      if (typeof superClass !== "function" && superClass !== null) {
        throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
      }
      subClass.prototype = Object.create(superClass && superClass.prototype, {
        constructor: { value: subClass, enumerable: false, writable: true, configurable: true }
      });
      if (superClass)
        Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
    }
    

    (1) 校验父构造函数。

    (2) 典型的寄生继承:用父类构造函数的proptype创建一个空对象,并将这个对象指向子类构造函数的proptype。

    (3) 将父构造函数指向子构造函数的_proto_(这步是做什么的不太明确,感觉没什么意义。)

    2.用一个闭包保存父类引用,在闭包内部做子类构造逻辑。

    3.new检查。

    4.用当前this调用父类构造函数。

    var _this = _possibleConstructorReturn(this, (Child.__proto__ || Object.getPrototypeOf(Child)).call(this, a));
    

    这里的Child.proto || Object.getPrototypeOf(Child)实际上是父构造函数(_inherits最后的操作),然后通过call将其调用方改为当前this,并传递参数。(这里感觉可以直接用参数传过来的Parent)

    function _possibleConstructorReturn(self, call) {
      if (!self) {
        throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
      }
      return call && (typeof call === "object" || typeof call === "function") ? call : self;
    }
    

    校验this是否被初始化,super是否调用,并返回父类已经赋值完的this。

    5.将行子类class内部的变量和函数赋给this。

    6.执行子类constuctor内部的逻辑。

    可见,es6实际上是为我们提供了一个“组合寄生继承”的简单写法。

    3. super

    super代表父类构造函数。

    super.fun1() 等同于 Parent.fun1() 或 Parent.prototype.fun1()。

    super() 等同于Parent.prototype.construtor()

    当我们没有写子类构造函数时:

    var Child = function (_Parent) {
      _inherits(Child, _Parent);
    
      function Child() {
        _classCallCheck(this, Child);
    
        return _possibleConstructorReturn(this, (Child.__proto__ || Object.getPrototypeOf(Child)).apply(this, arguments));
      }
    
      return Child;
    }(Parent);
    
    

    可见默认的构造函数中会主动调用父类构造函数,并默认把当前constructor传递的参数传给了父类。

    所以当我们声明了constructor后必须主动调用super(),否则无法调用父构造函数,无法完成继承。

    典型的例子就是Reatc的Component中,我们声明constructor后必须调用super(props),因为父类要在构造函数中对props做一些初始化操作。

    展开全文
  • Java 继承底层实现原理

    千次阅读 2019-09-21 19:46:48
    不知道大家是否有这样的思考:在java面向对象的语言的开发过程中,子类是如何继承调用到父类的方法的? 1.示例 Animal 动物父类 package animal; import animal.Impl.Fly; import animal.Impl.Walk; /** * ${...
  • JS继承方法和实现原理

    千次阅读 2020-09-02 21:33:50
    1.js中继承实现的方法 ...把父类的实例作为子类的原型(利用原型让一个引用类型继承另一个引用类型的属性和方法) 优点:简单易于实现,父类新增的实例与属性子类都能访问到 缺点: ♢ 可以在子类中增加实例属性,
  • js多继承实现

    千次阅读 2017-02-19 19:22:56
    JavaScript中继承是依赖prototype链实现的,只有一条原型链,所以理论上是不能继承多个父类的。 然而JavaScript是灵活的,通过一些技巧方法你却可以继承多个对象的属性来实现类似多继承。 讲解多继承之前先说...
  • js继承实现方法

    千次阅读 2021-04-13 14:56:06
    父类 function Animal (name = 'animal') { //实例方法和属性 this.name = name; this.age = '30'; this.PrintAge = function () { console.log(this.age) } ... Animal.prototype.PrintName = ...原型链继承 funct
  • 首先给出以下继承关系,以便描述虚继承原理: class AAA { public: int age; }; class BBB:virtual public AAA//变为虚继承,A变为虚基类 { }; class CCC:virtual public AAA { }; class DDD:public BBB, ...
  • UML图形表示之继承实现,关联

    千次阅读 2019-04-06 16:59:24
    类A实现接口B:(带空心箭头的虚线) 类C继承类D:(带空心箭头的实线) 直线表示关联,相互知道。 单向箭头E知道F,而F不知道E。如E是一个类,类里引用了F对象。 ...
  • JS实现继承的6种方式

    万次阅读 多人点赞 2018-05-30 01:51:02
    第一个问题是,当实现继承后,另一个原型的实例属性,变成了现在这个原型的原型属性,然后该原型的引用类型属性会被所有的实例共享,这样继承原型引用类型属性的实例之间不再具有自己的独特性了。 第二个问题是...
  • 父类和子类的问题困扰了我许久。 相信各位没有学过java课程的孩子也有相同的感受。...其中edge没有实现也没有定义ForwordConnetc();方法 假如我们的类有ForwordConnection继承Edge呢? 我...
  • maven是如何实现继承

    千次阅读 2017-10-27 19:24:38
    一 首先需要创建两个maven项目 1:superclass(父类) 2:subclass (子类) 二 (父类)子类继承父类需要在父类的pom.xml中定义 pom 如下: ...实现继承需要在subclass(子类)中通过相对路径引用super
  • 继承

    千次阅读 2017-08-25 20:10:47
    许多面向对象语言都支持两种继承方式:接口继承实现继承。接口继承继承方法签名,而实现继承继承实际的方法。由于ECMAScript 的函数没有签名,所以无法实现接口继承。ECMAScript 只支持实现继承,而且其实现...
  • js构造函数实现继承

    千次阅读 2018-04-23 17:33:33
    一、原型链的缺点1.1 单纯的原型链继承最大的一个缺点,来自于原型中包含引用类型的值。本来,我们没有通过原型链实现继承前,子类的所有实例都共享着原型上的所有属性和方法。通过子类实例,可以访问原型上的属性,...
  • JS实现继承的几种方式

    千次阅读 2017-03-31 13:56:41
    JS作为面向对象的弱类型语言,继承也是其非常强大的特性之一。那么如何在JS中实现继承呢?让我们拭目以待。JS继承实现方式既然要实现继承,那么首先我们得有一个父类,代码如下:......
  • js继承的几种实现方式

    千次阅读 2020-06-23 19:39:28
    其实js的继承本质上是通过原型链机制实现的扩展。不管是哪种继承方式,都是通过操作父类原型链和子类原型链形成关联关系实现的。只是不同实现中需要考虑不同的问题。在实际项目开发中,建议尽可能使用ES6的class ...
  • 前言大多 OO 语言都支持两种继承方式 接口继承实现继承 而 ECMAScript 中 无法实现接口继承 ECMAScript 只支持实现继承而且其实现继承主要是依靠 原型链 来 实现 1.原型链 基本思想利用原型让一个引用类型继承另外...
  • 许多OO 语言都支持两种继承方式:接口继承实现继承。接口继承继承方法签名,而实现继承继承实际的方法。如前所述,由于函数没有签名,在ECMAScript 中无法实现接口继承。ECMAScript 只支持实现继承,而且其...
  • 纯虚函数 、抽象类 、多重继承 、二义性问题 、菱形继承 、虚基类 、从内存布局看虚继承的底层实现原理
  • 条款36: 区分接口继承实现继承 作为类的设计者,有时希望派生类只继承成员函数的接口(声明)(也即是纯虚函数);有时希望派生类同时继承函数的接口和实现,但允许派生类改写实现(即虚函数);有时希望同时...
  • 项目中有用到过这个方法,看到别人的讲解很详细,做下笔记,以便以后用到 在Linearlayout的构造函数中通过使用映射机制加载布局文件,即通过Inflater方法,在使用该方法自定义的控件时,由于所有的子元素都是在...
  • C语言实现继承和多态

    万次阅读 2016-08-13 16:17:46
    C语言实现继承和多态 继承和多态是面向对象语言最强大的功能。有了继承和多态,我们可以完成代码重用。在C中有许多技巧可以实现多态。本文的目的就是演示一种简单和容易的技术,在C中应用继承和多态。通过创建一个...
  • jQuery.js中继承实现

    千次阅读 2006-11-19 12:01:00
    就拿继承来说吧,传统面向对象的语言都是在语言层面上支持了,而JavaScript就要自己来实现继承这个功能了,下面来看看jQuery.js是怎么实现这个功能的。 在jQuery.js中一个jQuery.extend()的静态方法,它提供了...
  • Java 实现“多继承

    万次阅读 多人点赞 2019-07-20 12:49:19
    一次编译,到处运行(JVM实现跨平台运行) 可靠性 安全性 支持多线程(对比C++没有内置多线程机制) etc... 二、Java为什么不支持多继承? 多继承:指一个子类同时继承多个父类,从而具备多个父类的特征 多...
  • js实现继承的方式

    千次阅读 2020-03-07 20:20:12
    继承,不包含ES6的
  • 说说 JavaScript 如何实现对象继承

    千次阅读 2017-03-23 14:55:07
    1 原型链可以利用 JavaScript 原型,让一个引用类型继承另一个引用类型的属性和方法://父类 function SuperType() { this.property = true; }SuperType.prototype.getSuperValue = function () { return this....
  • 继承 指的是一个类(称为子类、子接口)继承另外的一个类(称为父类、父接口)的功能,并可以增加它自己的新功能的能力,继承是类与类或者接口与接口之间最常见的关系;在Java中此类关系通过关键字extends明确标识...
  • JavaScript实现继承的6种方式

    千次阅读 2018-04-04 08:50:00
    第一个问题是,当实现继承后,另一个原型的实例属性,变成了现在这个原型的原型属性,然后该原型的引用类型属性会被所有的实例共享,这样继承原型引用类型属性的实例之间不再具有自己的独特性了。 第二个问题是,在...
  • jQuery继承实现(extends)

    万次阅读 2008-10-09 10:14:00
    jQuery中的extend方法通过不同的参数实现了几种不同的继承方法:1.extend(src) 将src对象的属性和方法逐一复制给jQuery或jQuery对象2.extend(dest,src1,src2,src3...srcN) 将src1、src2...对象的属性和方法逐一...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 616,739
精华内容 246,695
关键字:

继承引用实现