Question

I want to have my cake and eat it too: I want to have a method that returns this for chaining when it is bound to an object but returns undefined when it is called with a null|undefined scope. This seems to work fine but if you put it into JSLint then you get a Strict Violation errors. I don't need to use strict mode, but it seems like this should be possible and it works (open the console). Is this okay and/or how else could you accomplish this effect?

var o = {}; // or some pre-existing module
o.meth = (function (undefined) {

    // contrived example where you'd want to be able to locally 
    // access `meth` even if the outer `o.meth` was overwritten

    'use strict';

    var hash = Object.create(null); // "object" with no properties

    // global ref - or `null` where strict mode works
    var cantTouchThis = (function () {
        return this; 
    }).call(null);

    function meth (k, v) {
        var n;
        if (typeof k == 'object') { // set multi
            for (n in k) {
                meth(n, k[n]);
            }
        } else {
             if (v === undefined) { return hash[k]; } // get
             hash[k] = v; // set
        }
        if (this == cantTouchThis) { return; }
        return this;
    }

    return meth;

}());

And if you look in the console:

var localM = o.meth; // now scopeless
console.log(  o.meth('key', 'val')  ); // should return `o`
console.log(  localM('key', 'val')  ); // should return `undefined`
Was it helpful?

Solution

This will almost do what you want.

dontTouchThis will be null in environment that supports "use strict" directive, and will reference global object in those that don't support it.

First of all, you can replace that .call(null) chunk with just (function(){return this})(); it will have more or less the same effect.

Second, don't forget that there are 4 ways function can be called, when it comes to this value: as a method, as a standalone (baseless) function, via call/apply, and with new operator. This means that:

  • new (o.meth) will return o
  • o.meth.call(o2, ...) will return o2
  • o.meth.call(null, ...) will return undefined

Finally, if someone aliases meth to a global variable, then in strict-mode-supporting environment meth() will return global object, not undefined since cantTouchThis (being null) won't be equal to this (which will reference global object).

// in global scope

var meth = o.meth;
meth(...); // `this` references global object, will return global object

(function() {

  // in local scope
  var meth = o.meth;

  meth(...); // `this` is `undefined`, will return `undefined`

})();

OTHER TIPS

To be redundantly safe, I ended up using a local function for "grounding" scope resolution:

var gnd = (function () {
    var globe = this || window;
    return function (o) {
        // for grounding (securing) scope
        return o == null || o === globe ? 0 : o;
    };
}());

See usage in the source here.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top