简体   繁体   中英

Replacing __proto__ with prototype

I tend to write my Javascript "classes" in c-style.

In C# (for example) we do this

public class Parent {
    // stuff
}

public class Child : Parent {
    // Protected and public stuff from Parent will be accessible
}

In JS I found the equivalent of this by using proto , in example

var namespace = namespace || {};

namespace.Parent = function() {

    // Public variables
    self.pubVariable = "I am accessible";

    // Private variables
    var priVariable = "I'm not accessible outside of self";

    // ctor
    function self() {}

    return self;
}


namespace.Child = (function() {
    this.__proto__ = new namespace.Parent();

    // ctor
    function self() {}

    self.init = function() {
        // Prints the pubVariable
        console.log(pubVariable);
    };

    return self;

})($);

// Call it (statically)    
namespace.Child.init();

While this works it is Webkit and Mozilla only. I've understood that this could somehow be achievable using prototype but can't figure out how. Any advice is appreciated. Thanks!

For parent/child classes, I would do something like this

// your parent class
var Parent = function() {

  // parent constructor
  console.log("parent constructor!");

  // some public properties
  this.foo = "foo";
  this.bar = "bar";

  // a private data member
  var secret = "123456";
};

// a parent function
Parent.prototype.something = function() {
  console.log("something!");
}

// your child class
var Child = function() {

  // call parent constructor
  Parent.call(this);    

  // child constructor
  console.log("child constructor!");

  // override bar
  this.bar = "override!";
};

// the magic!
// child prototype build from parent prototype
Child.prototype = Object.create(Parent.prototype, {constructor: {value: Child}});

Example usage

var c = new Child();
c.something();
// => parent constructor!
// => child constructor!
// => something!

c.foo //=> "foo"
c.bar //=> "override!"

If you're using "namespacing" the concept is identical.


You can see this pattern in a number of libraries


EDIT

Per your comment, here's and added demonstration

var Foo = function(){};
Foo.prototype.hello = function(){ return "hello!"; };
var foo = new Foo();

// call our hello method
// this calls foo.__proto__.hello
foo.hello(); //=> "hello!"

// override `hello` method for this instance
foo.hello = function(){ return "こんにちは"; };

// call our hello method again
// this calls foo.hello because it is defined directly on our instance
// (it has a higher precedence in the lookup chain)
foo.hello(); //=> "こんにちは"

// remove the override
delete foo.hello;

// call our hello method again
// this goes back to calling foo.__proto__.hello
foo.hello(); //=> "hello!"

// remove the method prototype
delete Foo.prototype.hello

// call our hello method one last time
// spoiler: it's gone!
foo.hello(); //=> TypeError: Object [object Object] has no method 'hello'

As you can see, you lose this functionality by directly defining methods on the instance using this.something = function(){}; . I personally prefer defining methods on the prototype because of the added flexibility. This way, the prototype really works like a blueprint. You get all the pre-defined behavior; you can modify if necessary and revert to the original whenever you want, all on a per-instance basis.


ONE MORE THING

In our last example, we had a prototype method and an instance method override. Is there a way to call the original method too? Let's see!

var Foo = function(){};
Foo.prototype.hello = function(){ return "hello!"; };

var foo = new Foo();
foo.hello = function(){ return "こんにちは!"; }

// call override method
foo.hello(); //=> "こんにちは!"

// call original method
Foo.prototype.hello.call(foo); //=> "hello!"

// japanese just one more time...
foo.hello(); //=> "こんにちは!" 

This would work too, but I never really have the need. I suppose the benefit is you don't need to know the original class this way :)

// call original method from the instance
foo.__proto__.hello.call(foo); //=> "hello!"

PROTOTYPES!

I think, you want this

// namespace
var namespace = namespace || {};

// Parent Class
namespace.Parent = function() {
    this.pubVariable = "I am accessible";
    var priVariable = "I'm not accessible outside of this";
}

// Child class
namespace.Child = function() {
    // namespace.Parent.call(this);
    this.init = function()
    {
        // returns Parent class' pubVariable
        // inherited by namespace.Child.prototype
        return this.pubVariable;
    }
};

// inherit Parent class
namespace.Child.prototype = new namespace.Parent();

var kid = new namespace.Child();
console.log(kid.init()); // I am accessible

If you use namespace.Parent.call(this) then Child class will have it's own copy of pubVariable but now Child class is using Parent's pubVariable .

Also, if you want to share methods from parent class with sub classes then you should add methods in the parent class' prototype, like this

namespace.Parent = function() { //... }
namespace.Parent.prototype.aMethodInParent = function(){ //... };

So, when you will inherit it in a subclass like this

namespace.Child = function() { // ... };
namespace.Child.prototype = new namespace.Parent();

Another Sub/Child Class

namespace.AnotherChild = function() { // ... };
namespace.AnotherChild.prototype = new namespace.Parent();

In this case both sub/child classes will use the same aMethodInParent() method from their parent class.

DEMO.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM