Reputation: 2891
I need to copy an object and its methods. Hence i stringify the object and then parse it and add the methods from the original object (but bound to this new copy).
// Taken from: https://stackoverflow.com/questions/31054910/get-functions-methods-of-a-class
function getAllMethods(obj) {
let props = [];
do {
const l = Object.getOwnPropertyNames(obj)
.concat(Object.getOwnPropertySymbols(obj).map(s => s.toString()))
.sort()
.filter((p, i, arr) =>
typeof obj[p] === 'function' && //only the methods
p !== 'constructor' && //not the constructor
(i == 0 || p !== arr[i - 1]) && //not overriding in this prototype
props.indexOf(p) === -1 //not overridden in a child
)
props = props.concat(l)
}
while (
(obj = Object.getPrototypeOf(obj)) && //walk-up the prototype chain
Object.getPrototypeOf(obj) //not the the Object prototype methods (hasOwnProperty, etc...)
)
return props;
}
function copyObject(obj) {
var copy = JSON.parse(JSON.stringify(obj));
// Copy all methods
getAllMethods(obj)
.filter(prop => typeof obj[prop] === 'function')
.forEach(prop => copy[prop] = obj[prop].bind(copy));
return copy;
}
And here some tests:
var foo = { bar:2, f: function() { this.bar = 5 } }
var bar = copyObject(foo);
var baz = copyObject(bar);
bar.f();
bar.bar; // 5
baz.f();
baz.bar; // 2 instead of 5..?!
baz.f.apply(baz); // not working either, baz.bar still 2
Why does a copy of a copy not work as i would expect?
EDIT: In baz.f
the this
reference is still bound to bar
for some reason.
Upvotes: 3
Views: 39
Reputation: 101690
It doesn't work because you can only .bind()
the this
value on a function once. The this
value is basically set in stone the first time you use bind()
and after that, you're just "painting another layer" on top of the already-bound function:
function myFunc() {
console.log(this.a);
}
var f1 = myFunc.bind({
a: 5
});
var f2 = f1.bind({
a: 6
});
f1();
f2();
Also keep in mind that you can't rebind this
on arrow functions at all:
var a = 2;
var myFunc = () => {
console.log(this.a);
}
var f1 = myFunc.bind({
a: 5
});
var f2 = f1.bind({
a: 6
});
f1();
f2();
Upvotes: 5