Reputation: 4516
How to bind methods when destructuring an object in JavaScript?
const person = {
getName: function() {
console.log(this);
}
};
var a = person.getName;
var b = person.getName.bind(person);
var {getName: c} = person;
person.getName(); //=> {getName: [Function]}
a(); //=> window or global
b(); //=> {getName: [Function]}
c(); //=> window or global
I want c
to log in the console its "parent" object {getName:
[Function]}
.
Is there any way to bind all methods when destructuring an object in one destructuring line?
Upvotes: 19
Views: 5898
Reputation: 9315
Just use an arrow method:
const person = {
getName: () => console.log(this),
};
Upvotes: 5
Reputation: 606
Yes. We can do this with a single line of code, assuming I understood your question correctly.
This certainly isn't the most readable approach, but it's a method that I use often in my personal projects:
const person = {
getName: function() {
console.log(this);
}
};
// IIFE for destructuring and applying changes inline
const { a, b, c } = (({ getName }) => ({ a: getName.bind(person), b: getName.bind(person), c: getName.bind(person) }))(person);
Now we call the destructured props:
a(); // { getName: [Function: getName] }
b(); // { getName: [Function: getName] }
c(); // { getName: [Function: getName] }
What's happening here? If that line was confusing, read on...
We are wrapping everything in an IIFE which accepts as input the person
object; we pull the getName
prop inline.1
Finally, we are explicitly mapping each call to bind
using the getName
prop - as pulled from our sole argument, person
- to correspond to a, b, c
, which we are destructuring away from the resolved IIFE.
1OK so I've arguably cheated here in that we have two destructuring "statements" but which are ultimately resolved in the same expression
Upvotes: 4
Reputation: 191946
You can use a getter or a proxy to bind a method whenever you get
it, even using destructuring.
Both solutions check if method is already bound, by looking for bound
at the start of the name using String.startsWith()
. If not bound, that method will be bound before returning it.
const person = {
prop: 5,
_getName: function() {
console.log(this.prop);
},
get getName() {
// if not bound, bind the method
if(!this._getName.name.startsWith('bound ')) {
this._getName = this._getName.bind(this);
}
return this._getName;
}
};
var a = person.getName;
var b = person.getName.bind(person);
var {getName: c} = person;
person.getName(); //=> 5
a(); //=> 5
b(); //=> 5
c(); //=> 5
var handler = {
get: function(target, prop, receiver) {
// if method, and not bound, bind the method
if(typeof target[prop] === 'function' && !target[prop].name.startsWith('bound ')) {
target[prop] = target[prop].bind(target);
}
return target[prop];
}
};
const person = new Proxy({
prop: 5,
getName: function() {
console.log(this.prop);
}
}, handler);
var a = person.getName;
var b = person.getName.bind(person);
var {getName: c} = person;
person.getName(); //=> 5
a(); //=> 5
b(); //=> 5
c(); //=> 5
Upvotes: 7
Reputation: 1511
There is a simple workaround using ES6 classes. You can use bind
in the class constructor to manually set the context of the function.
In the example below, getName()
will "survive" the destructuring :
class Person {
constructor() {
this.getName = this.getName.bind(this);
}
getName() {
console.log(this);
}
}
const {
getName
} = new Person();
getName(); // Person { getName: [Function: bound getName] }
Upvotes: 9
Reputation: 30453
No, there is no way. Functions detached from objects lose the original context. And destructing in JavaScript has no syntax to do something with extracted values on the fly.
Upvotes: 19