Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaScript (Underscore.js) Extending functionality

Disclaimer: (1) My back ground is in Java/C#, and I only started my deep dive into JavaScript last week. (2) I am aware of the _.mixin() method.

This is not project critical, but rather I'm having trouble getting my head around object inheritance in Javascript.

For instance, tried to augment underscore.js with a _.keyFilter function (I realize I can use map to achieve something similar) to return a list of keys which satisfied the evaluation function. I can achieve the result with _.mixin() method:

Other than dropping the function directly into the sourcecode, _.mixin()

 _.mixin({ filterKey : function(obj, iterator, context) {
    var results = [];
    if (obj == null) return results;
    _.each(obj, function(value, index, list) {
        if (iterator.call(context, value, index, list)){
            results[results.length] = index;
        }
    });
    return results;
}});

However, I am not sure I understand why I can't simply augment underscore with the following in my script file:

_.keyFilter = function(obj, iterator, context) {
        var results = [];
        if (obj == null) return results;
        each(obj, function(value, index, list) {
            if (iterator.call(context, value, index, list)) results[results.length] = index;
        });
        return results;
};

doing this, and then tried calling it with:

_.chain(myList).keyFilter(evalFunction);

I get the following exception:

'Uncaught TypeError: Object [object Object] has no method 'keyFilter' 

Note, that the above method does work when I drop it into the source code of underscore.js.

So then I tried using prototype (Which I still don't completely grasp) back in my script file:

var keyFilter = _.prototype.keyFilter = function(obj, iterator, context) {
        var results = [];
        if (obj == null) return results;
        each(obj, function(value, index, list) {
            if (iterator.call(context, value, index, list)) results[results.length] = index;
        });
        return results;
};

But this threw the same exception. (I thought the var's below were being hiked up to the top of the script which is why I also tried this with and without the transitive assignment above).

Explanations would be greatly appreciated.

like image 481
Rob Leclerc Avatar asked Oct 07 '22 17:10

Rob Leclerc


1 Answers

This is less a question about JavaScript than a question about Underscore, which tends to do things its own way.

  • Adding a method to _ just by adding it as a property (i.e. _.keyFilter = ...) is similar to adding a static method to a class in Java. You are adding the method to the _ object, but it has no relationship to the rest of the functionality in _. This is a reasonable way to add new utility methods, but not if you want to use Underscore's wrapping and chaining functionality.

  • Using .mixin() allows you to hook into Underscore's wrapping and chaining architecture. This is specific to Underscore - it's not a part of JavaScript. Other than editing the source code, this is the only way I'm aware of to tap into these Underscore features, which largely rely on private methods and variables within the Underscore library.

  • While JS supports prototypical inheritance, and every object has a prototype property, many developers use other approaches or other types of inheritance. It's generally not a safe assumption that adding a method to the prototype of something that looks like a constructor will actually work, unless you know the code behind it. Underscore does use prototypical inheritance for its wrapped objects, but in a twisted, slightly magical way, and I don't think adding methods to the prototype wil have the desired effect - for one thing, even if it worked, prototypes are only used in Underscore for wrapped objects, so _.keyFilter(...) definitely wouldn't work.

like image 160
nrabinowitz Avatar answered Oct 10 '22 01:10

nrabinowitz