Emulate super in javascript

后端 未结 11 1105
生来不讨喜
生来不讨喜 2020-12-08 10:18

Basically is there a good elegant mechanism to emulate super with syntax that is as simple as one of the following

  • this.$super.prop()
相关标签:
11条回答
  • 2020-12-08 10:49

    For those who do not understand the recursion problem the OP presents, here is an example:

    function A () {}
    A.prototype.foo = function (n) {
        return n;
    };
    
    function B () {}
    B.prototype = new A();
    B.prototype.constructor = B;
    B.prototype.$super = A.prototype;
    B.prototype.foo = function (n) {
        if (n > 100) return -1;
        return this.$super.foo.call(this, n+1);
    };
    
    function C () {}
    C.prototype = new B();
    C.prototype.constructor = C;
    C.prototype.$super = B.prototype;
    C.prototype.foo = function (n) {
        return this.$super.foo.call(this, n+2);
    };
    
    
    alert(new C().foo(0)); // alerts -1, not 3
    

    The reason: this in Javascript is dynamically bound.

    0 讨论(0)
  • 2020-12-08 10:50

    The main difficulty with super is that you need to find what I call here: the object that contains the method that makes the super reference. That is absolutely necessary to get the semantics right. Obviously, having the prototype of here is just as good, but that doesn’t make much of a difference. The following is a static solution:

    // Simulated static super references (as proposed by Allen Wirfs-Brock)
    // http://wiki.ecmascript.org/doku.php?id=harmony:object_initialiser_super
    
    //------------------ Library
    
    function addSuperReferencesTo(obj) {
        Object.getOwnPropertyNames(obj).forEach(function(key) {
            var value = obj[key];
            if (typeof value === "function" && value.name === "me") {
                value.super = Object.getPrototypeOf(obj);
            }
        });
    }
    
    function copyOwnFrom(target, source) {
        Object.getOwnPropertyNames(source).forEach(function(propName) {
            Object.defineProperty(target, propName,
                Object.getOwnPropertyDescriptor(source, propName));
        });
        return target;
    };
    
    function extends(subC, superC) {
        var subProto = Object.create(superC.prototype);
        // At the very least, we keep the "constructor" property
        // At most, we preserve additions that have already been made
        copyOwnFrom(subProto, subC.prototype);
        addSuperReferencesTo(subProto);
        subC.prototype = subProto;
    };
    
    //------------------ Example
    
    function A(name) {
        this.name = name;
    }
    A.prototype.method = function () {
        return "A:"+this.name;
    }
    
    function B(name) {
        A.call(this, name);
    }
    // A named function expression allows a function to refer to itself
    B.prototype.method = function me() {
        return "B"+me.super.method.call(this);
    }
    extends(B, A);
    
    var b = new B("hello");
    console.log(b.method()); // BA:hello
    
    0 讨论(0)
  • 2020-12-08 10:51

    Note that for the following implementation, when you are inside a method that is invoked via $super, access to properties while working in the parent class never resolve to the child class's methods or variables, unless you access a member that is stored directly on the object itself (as opposed to attached to the prototype). This avoids a slew of confusion (read as subtle bugs).

    Update: Here is an implementation that works without __proto__. The catch is that using $super is linear in the number of properties the parent object has.

    function extend (Child, prototype, /*optional*/Parent) {
        if (!Parent) {
            Parent = Object;
        }
        Child.prototype = Object.create(Parent.prototype);
        Child.prototype.constructor = Child;
        for (var x in prototype) {
            if (prototype.hasOwnProperty(x)) {
                Child.prototype[x] = prototype[x];
            }
        }
        Child.prototype.$super = function (propName) {
            var prop = Parent.prototype[propName];
            if (typeof prop !== "function") {
                return prop;
            }
            var self = this;
            return function () {
                var selfPrototype = self.constructor.prototype;
                var pp = Parent.prototype;
                for (var x in pp) {
                    self[x] = pp[x];
                }
                try {
                    return prop.apply(self, arguments);
                }
                finally {
                    for (var x in selfPrototype) {
                        self[x] = selfPrototype[x];
                    }
                }
            };
        };
    }
    

    The following implementation is for browsers that support the __proto__ property:

    function extend (Child, prototype, /*optional*/Parent) {
        if (!Parent) {
            Parent = Object;
        }
        Child.prototype = Object.create(Parent.prototype);
        Child.prototype.constructor = Child;
        for (var x in prototype) {
            if (prototype.hasOwnProperty(x)) {
                Child.prototype[x] = prototype[x];
            }
        }
        Child.prototype.$super = function (propName) {
            var prop = Parent.prototype[propName];
            if (typeof prop !== "function") {
                return prop;
            }
            var self = this;
            return function (/*arg1, arg2, ...*/) {
                var selfProto = self.__proto__;
                self.__proto__ = Parent.prototype;
                try {
                    return prop.apply(self, arguments);
                }
                finally {
                    self.__proto__ = selfProto;
                }
            };
        };
    }
    

    Example:

    function A () {}
    extend(A, {
        foo: function () {
            return "A1";
        }
    });
    
    function B () {}
    extend(B, {
        foo: function () {
            return this.$super("foo")() + "_B1";
        }
    }, A);
    
    function C () {}
    extend(C, {
        foo: function () {
            return this.$super("foo")() + "_C1";
        }
    }, B);
    
    
    var c = new C();
    var res1 = c.foo();
    B.prototype.foo = function () {
        return this.$super("foo")() + "_B2";
    };
    var res2 = c.foo();
    
    alert(res1 + "\n" + res2);
    
    0 讨论(0)
  • 2020-12-08 10:55

    JsFiddle:

    What is wrong with this?

    'use strict';
    
    function Class() {}
    Class.extend = function (constructor, definition) {
        var key, hasOwn = {}.hasOwnProperty, proto = this.prototype, temp, Extended;
    
        if (typeof constructor !== 'function') {
            temp = constructor;
            constructor = definition || function () {};
            definition = temp;
        }
        definition = definition || {};
    
        Extended = constructor;
        Extended.prototype = new this();
    
        for (key in definition) {
            if (hasOwn.call(definition, key)) {
                Extended.prototype[key] = definition[key];
            }
        }
    
        Extended.prototype.constructor = Extended;
    
        for (key in this) {
            if (hasOwn.call(this, key)) {
                Extended[key] = this[key];
            }
        }
    
        Extended.$super = proto;
        return Extended;
    };
    

    Usage:

    var A = Class.extend(function A () {}, {
        foo: function (n) { return n;}
    });
    var B = A.extend(function B () {}, {
        foo: function (n) {
            if (n > 100) return -1;
            return B.$super.foo.call(this, n+1);
        }
    });
    var C = B.extend(function C () {}, {
        foo: function (n) {
            return C.$super.foo.call(this, n+2);
        }
    });
    
    var c = new C();
    document.write(c.foo(0) + '<br>'); //3
    A.prototype.foo = function(n) { return -n; };
    document.write(c.foo(0)); //-3
    

    Example usage with privileged methods instead of public methods.

    var A2 = Class.extend(function A2 () {
        this.foo = function (n) {
            return n;
        };
    });
    var B2 = A2.extend(function B2 () {
        B2.$super.constructor();
        this.foo = function (n) {
            if (n > 100) return -1;
            return B2.$super.foo.call(this, n+1);
        };
    });
    var C2 = B2.extend(function C2 () {
        C2.$super.constructor();
        this.foo = function (n) {
            return C2.$super.foo.call(this, n+2);
        };
    });
    
    //you must remember to constructor chain
    //if you don't then C2.$super.foo === A2.prototype.foo
    
    var c = new C2();
    document.write(c.foo(0) + '<br>'); //3
    
    0 讨论(0)
  • 2020-12-08 10:55

    I think I have a more simple way....

    function Father(){
      this.word = "I'm the Father";
    
      this.say = function(){
         return this.word; // I'm the Father;
      }
    }
    
    function Sun(){
      Father.call(this); // Extend the Father
    
      this.word = "I'm the sun"; // Override I'm the Father;
    
      this.say = function(){ // Override I'm the Father;
        this.word = "I was changed"; // Change the word;
        return new Father().say.apply(this); // Call the super.say()
      }
    }
    
    var a = new Father();
    var b = new Sun();
    
    a.say() // I'm the father
    b.ay() // I'm the sun
    b.say() // I was changed
    
    0 讨论(0)
  • 2020-12-08 11:02

    John Resig posted an ineherence mechanism with simple but great super support. The only difference is that super points to the base method from where you are calling it.

    Take a look at http://ejohn.org/blog/simple-javascript-inheritance/.

    0 讨论(0)
提交回复
热议问题