Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaScript bind method does not work on getter property

Tags:

javascript

Trying to do this:

var c = {
  x: 'other context'
};

var o = {
  x: 'this context',
  get otherContext () {
    alert(this.x);
  }.bind(c)
};

o.otherContext;

But I get a Syntax Error:

Uncaught SyntaxError: Unexpected token .

A use-case

You may ask, why I may want to change context on a getter. The use case that led me here dealt with creating a helper object that would retrieve DOM properties. The helper was defined inside another object that held a reference to the DOM element I needed properties from. So, this helper object is really just a proxy for some DOM methods, really, and I would do something like:

var ViewObject = function () {
  this.el = document.getElementById('id');
  
  var proxy = {
    ...
    get left() {
      return this.el.offsetLeft
    }.bind(this)
    ...
  };
};

IMHO this is a pretty valid use-case. And one where having the parent object's this context would be useful. (Maybe proxy functions may be more adequate? Hmm...I didn't think about that. Still getters are ES5 and proxies are ES6 and only implemented by evergreens and Edge 13+, I think in the scope of ES5 the question still very much applies.)

Can someone give me a logical explanation (pref citing the spec) why changing the context of a getter is illegal.

like image 836
seebiscuit Avatar asked Jun 22 '16 16:06

seebiscuit


People also ask

How does bind work in JavaScript?

Summary. The bind() method creates a new function, when invoked, has the this sets to a provided value. The bind() method allows an object to borrow a method from another object without making a copy of that method. This is known as function borrowing in JavaScript.

Should you use BIND in JavaScript?

. bind() is used when you need to pass a callback (e.g. some sort of function reference), but you want the caller to call your function with a specific this value.

What does .get do in JavaScript?

get() method in JavaScript is used to allow users to get the property from an object as a function. This method always returns the value of the property.

How do you bind variables in JavaScript?

We use the Bind() method to call a function with the this value, this keyword refers to the same object which is currently selected . In other words, bind() method allows us to easily set which object will be bound by the this keyword when a function or method is invoked.


2 Answers

The problem is that method syntax doesn't use function expressions. It's a defined syntactic structure.

MethodDefinition[Yield] :

PropertyName[?Yield] ( StrictFormalParameters ) { FunctionBody }

GeneratorMethod[?Yield]

get PropertyName[?Yield] ( ) { FunctionBody }

set PropertyName[?Yield] ( PropertySetParameterList ) { FunctionBody }

PropertySetParameterList :

FormalParameter

Since it isn't a function expression, you don't have access to the functions methods or properties.

You can accomplish what you want with Object.defineProperty.

var proxy = { ... };
Object.defineProperty(proxy, 'left', {
  get: function() {
    return this.el.offsetLeft;
  }.bind(this)
});
like image 110
Mike Cluck Avatar answered Sep 18 '22 12:09

Mike Cluck


For class proxies you might want to use something like this:

class Main {
    constructor() {
        this._adapter = new Adapter();
        return this._createProxy();
    }

    _createProxy() {
        return new Proxy(this, {
            get(me, propertyName) {
                if (typeof me._adapter[propertyName] === 'function') {
                    return me._adapter[propertyName].bind(me._adapter);
                }
                return (function () {
                    return me._adapter[propertyName];
                }.bind(me._adapter))();
            }
        });
    }
}

class Adapter {
    constructor() {
        this._foo = true;
        this._yuk = 2;
    }

    get foo() {
        return this._foo;
    }

    baz() {
        return 4*this._yuk;
    }
}

This way both, the getter and the method will be wrapped within the right context:

let main = new Main();
console.log(main.foo);   // -> true
console.log(main.baz()); // -> 8
like image 20
Marco Kerwitz Avatar answered Sep 18 '22 12:09

Marco Kerwitz