Organize prototype javascript while perserving obj

2018-12-31 08:23发布

I have built a large application using JavaScript prototype and inheritance. But I am having a hard time organizing my code. For example I have a class carousel which has many functions like this:

Carousel.prototype.next = function () {...}
Carousel.prototype.prev = function () {..}
Carousel.prototype.bindControls = function () {..}

I would like to organize my code like this :

Carousel.prototype.controls = {
   next: function () { ... } , 
   prev: function() { ... },
   bindControls: function () { .. }
}

But this will cause the value of "this" being lost. I can keep track of it using a global instance but this will cause problems when the class is inherited for example In another file I have something like this to override parent class

BigCarousel.prototype.next = function () {...}

My inheritance is done like this:

Function.prototype.inheritsFrom = function (parentClass) {
    if (parentClass.constructor === Function) {
        //Normal Inheritance
        this.prototype              = $.extend(this.prototype , new parentClass);
        this.prototype.constructor  = this;
        this.prototype.parent       = parentClass.prototype;
    }
    else {
        //Pure Virtual Inheritance
        this.prototype = $.extend(this.prototype, parentClass);
        this.prototype.constructor = this;
        this.prototype.parent = parentClass;
    }
    return this;
};

So I can do:

BigCarousel.inheritsFrom(Carousel)

Does anyone know how can I work around the "this" value ?

3条回答
初与友歌
2楼-- · 2018-12-31 08:37

You can use the .bind method of Function.

In Javascript Functions inherit from Object, so they have their own methods. One of those methods is .bind:

https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/bind

Also you are doing inheritance wrong, the right way with raw Javascript is:

ChildClass= function() {
    ParentClass.apply(this, arguments); //calling parent constructor
    //constructor
};

ChildClass.prototype= new ParentClass();

Then you can simply do this on your constructor:

Courossel= function() {
    ParentClass.apply(this, arguments); //calling parent constructor
    this.controls.next.bind(this);
    this.controls.prev.bind(this);
    this.controls.bindControls.bind(this);
}

But I have to say that Frits suggestion is better, make the controls their own class and instantiate it on Carousel constructor passing a reference to your Carousel instance (the this keyword). Just don't call it ".ref", it's confusing.

查看更多
君临天下
3楼-- · 2018-12-31 08:41

You could make Controls a class of it's own:

var Controls = function (controllable_object) {
    this.ref = controllable_object;
};
Controls.prototype.next = function () {
    this.ref.foo();
}
// ..

var Carousel = function () {
    this.controls = new Controls(this);
};
// ..

This doesn't allow you to override the implementation of Controls though. With more dependency injection you'd get something like:

var Controls = function (controllable_object) {
    this.ref = controllable_object;
};
Controls.prototype.next = function () {
    this.ref.foo();
}
// ..

var Carousel = function () {
        this.controllers = [];
    };
Carousel.prototype.addController = function (controller) {
        this.controllers.push(controller);
    };
// ..

var carousel = new Carousel();
carousel.addController(new Controls(carousel));
查看更多
谁念西风独自凉
4楼-- · 2018-12-31 08:59

My inheritance is done like this:

$.extend(this.prototype , new parentClass);

Ouch. This is not inheritance (with new BigCarousel instanceof Carousel), but just copying properties. Maybe this is enough for you, but then you should call it mixin. Also, you should avoid using new for inheritance.


But this will cause the value of "this" being lost. How can I work around that?

It's impossible to have this point to the parent object with nested properties (as long as you don't want to explicitly set it every time). You have only two choices:

  • Forget it, and organize your methods by prefixing them (controlNext, controlBind, …)
  • Give each of your carousels its own controls object. For inheritance, make them CarouselControls instances for example. This especially fits well if those controls are quite independent from the carousel, and don't need to access the carousel they're attached to everywhere. If they are not, you still can pass a reference to the parent carousel into their constructor for example:

    this.controls = new CarouselControls(this);
    

    Also, for customizing the controls in different carousels, you might have to subclass the CarouselControls as well - or you prepare your Controls object to serve for different carousels in general, so that from BigCarousel you can

    Carousel.call(this); // make this a carousel
    this.controls.activate({big: true, fast: false}); // or something
    
查看更多
登录 后发表回答