简体   繁体   中英

Javascript Square Bracket Notation Multiple Dynamic Properties

This may sound a bit unusual, I've never needed to use square bracket notation in this way before, and racking my brains I can't think of a way to produce the desired outcome.

I'm implementing a callback wrapper to maintain the reference of this when passing methods as callbacks

eg

foo.prototype.wrap = function(name){
    var wrapper,
        self = this;

    wrapper = function(){
        self[name](arguments);
    };

    return wrapper;
};

// usage

foo.wrap('bar');

// executes foo.bar maintaining 'this' as a reference to foo 

The issue I'm having is that foo has some nested methods

eg

foo.bar.close();

I'm trying to figure out a way to make the wrap method support nested methods

eg

foo.wrap('bar.close')

// or

foo.wrap('bar','close');

So the foo.wrap function would need to dynamically add the square brackets corresponding to the length or the arguments passed in.

eg

self[x][y][z](arguments);

I can't think of a way to do this. Any ideas ?

I have a sneaking suspicion this isn't possible though.

I must be having one of those days where you forget everything :)

While @NilColor's answer is correct, and I did know it I just wasn't thinking with the correct hat on.

Anyway I decided that I still like the idea of having a wrapper that requires a bit less specificity when you attach it to your objects. And is a bit less verbose.

So I wrote it along with my original line of thinking, you might like it.

myObj.wrap = function(path, context){ 
    var wrapper,
        method = ( typeof path != 'string' && context )? path : this,
        context =  (typeof path === 'object' && context === undefined)? 
            path : (context || this);

    if (typeof path === 'string'){
        path = path.split('.');

        for ( var i = 0; i < path.length; i++ ){
            method = method[path[i]];
            if ( context === true  )
                context = ( i === path.length - 2 )? method : context; 
        };
    };

    wrapper = function(){
        method.apply(context, arguments);
    };

    return wrapper;
}

usage:

Bind any number of nested methods to myObj

    myObj.wrap('foo') //binds myObj.foo to myObj

// or

    myObj.wrap('foo.bar') //binds myObj.foo.bar to myObj

//or if myObj is a function

    myFunc.wrap() // binds myFunc to myFunc

Bind a method of myObj to another object

    myObj.wrap('foo.bar', someObj) //binds myObj.foo.bar to someObj

//or if myObj is a function

    myFunc.wrap(someObj) //Binds myFunc to someObj

Bind a nested method to it's parent

    myObj.wrap('foo.bar', true) // binds myObj.foo.bar to myObj.foo

Use as a helper

    myObj.wrap(someFunc, someObj) //binds someFunc to someObj

If you looking for an answer to the original question not in the context of method binding.

myObj.getProps = function(path, context){
var context = context || this;
    path = path.split('.');


for ( var i = 0; i < path.length; i++ ){
            context = context[path[i]];
    };

    return context;
};

Usage:

attach to an object or as a standalone function

Get the properties

myObj.getProps('foo.bar') // returns mayObj.foo.bar

Give it a context object

myObj.getProps('user.name', myAccounts) // returns myAccounts.user.name

to use it as a standalone function replace

myObj.getProps = function(path,context){....}

with

function getProps(path,context){....}

Note

If using it as a standalone function you will need to remember that it will start looking from the scope of this . So if it's defined in the global scope you need to provide full paths.

eg

getProps('myObj.foo.bar')

You can still use the context selector to change the reference object.

A general concept of "binding" this is something like this:

function bind(method, context) {
      var args = Array.prototype.slice.call(arguments, 2);
      return function() {
            var a = args.concat(
                               Array.prototype.slice.call(arguments, 0));
            return method.apply(context, a);
      }
}

This way you'll get a reference to method with linked this ( context ). This way you can bind nested methods like this:

> foo = {}
> foo.a = function(){console.log('a'); console.log(this)}
> bar = {bar: 'yeah'}
> f = bind(foo.a, bar)
> f()
-> a
-> {bar: 'yeah', __proto__: Object}

Is it something you are looking for?

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