If I create an Object A:
let A = {};
And want to mix in methods from other Objects B and C:
let B = {
foo() {
alert("Boo!");
}
};
let C = {
bar() {
alert("No!");
}
};
Normally I would call:
Object.assign(A, B, C); Then I change my function foo:
Object.assign(B, {
foo() {
alert("Hooray!");
}
});
Objcect.assign(C, {
bar() {
alert("Yes!");
}
});
After that I call foo or bar:
A.foo(); // Actual output: "Boo!", desired: "Hooray!"
A.bar(); // Actual output: "No!", desired: "Yes!"
So far I found out, that Object.assign
only copies methods in the target, but it doesn't link them.
I already uploaded a question concerning mixing in only one Object, which was solved: Mix in one object, solution: prototypical inheritance
About inheritance and composition I found a useful blogposts here: Understanding Prototypes, Delegation & Composition
I want to mix methods in an Object, but not copy the methods, much more rather I want an assignments to the definitions of the mixed in functions.
How is this possible? (Maybe in ES2015?)
You can use Proxies to create mixins:
This can be optimized by indexing properties to avoid the lookup each time.
Two answers to this:
They're not copied, they're referenced
What you're doing isn't copying the methods (functions; JavaScript doesn't really have methods), it's reusing the ones you already have. Only a single
foo
orbar
function exists; there are just properties onA
andB
, and onA
andC
, that both refer to that same function.You can see that from the fact that
A.foo === B.foo
andA.bar === C.bar
:What your code produces in memory looks like this (with some details omitted):
But if you really want a link
If you want to be able to replace the
B.foo
orC.bar
function with something else, and haveA
see that change, you can do that by makingfoo
andbar
onA
property accessors:Now
A
is linked toB
andC
:You can do that for a dynamic mix of properties using
Object.defineProperty
:In action:
Despite my use of ES2015 arrow functions above, if you convert those to
function
functions, all of the above works in ES5 (but not ES3 or earlier, which didn't have property accessors).Re your comment:
A couple of key things:
In JavaScript, functions are objects. Genuine, real objects that have all of the features of other kinds of objects, and also the ability to contain and run code. (This is frequently not the case in other languages.)
Variables and properties hold values.
A value is either a primitive (like 1 or "foo"), or an object reference. The object reference isn't the object, it's just a reference to the object, which exists elsewhere.
When you assign a value to a variable or property, you're copying the value. If the value is an object reference, that means you're copying the reference, not the object.
JavaScript doesn't have (at the external level) either variable references or property references. (Most languages don't, but some do.)
I like to explain object references, variables, and values like this: Say we have this guy Joe. Joe's really happy because he's turning 42 today, and he's a fan of The Hitchhiker's Guide to the Galaxy. So he writes the number 42 on a piece of paper. The 42 is a value, in this case a number. The piece of paper is a variable or property.
Joe decides to have a party, so he puts up a party announcement on the break room notice board at his job saying he's having a party and writes his home address on it. The announcement is also a variable or property. Joe's house is an object. The address written on the announcement is an object reference, telling us where Joe's house is.
Joe runs into Mohammed in the break room, and knowing he's a fellow HHG fan, shows him his paper with 42 on it, and points out the party announcement. Mohammed gets out a piece of paper and copies the 42 onto it, to remember how old Joe's turning (perhaps to buy the appropriate card). That is, he copies the 42 (the value) onto his piece of paper (the variable/property). Then, because he hasn't been to Joe's house before, he gets another piece of paper and copies the house's address from Joe's party announcement. The house isn't copied, just the address. The object wasn't copied, just the reference to it.
Later, Joe realizes the party's getting too big for his house, and decides to move it to a pub in the city. He crosses out the address on the announcement and writes in the address of the pub. But he forgets to tell Mohammed. So come party time, Mohammed goes to Joe's house instead of the pub.
Back to JavaScript and functions, which are objects:
If you have:
B.foo
doesn't contain thefoo
function, it contains a value (an object reference) that refers to thefoo
function. Then when you do:...you copy the value (the object reference) from
B.foo
intoA.foo
. There is no connection, no link whatsoever betweenA.foo
andB.foo
other than that they happen to contain the same value, just like there's no connection between Joe's party announcement and Mohammed's piece of paper, other than that they happen to both have Joe's address on them (to start with).Later, if you do
B.foo = function() { /*...*/ };
, you're replacing the value inB.foo
with a new value that refers to a different function. This has no effect onA.foo
because, again, there's no link betweenA.foo
andB.foo
. Just like it didn't have any effect on Mohammed's piece of paper when Joe crossed out the address on the party announcement and wrote in the address of the pub.The reason my property accessor mechanism above works is that a property accessor is a function which gets run every time you read the property's value. So when you get the value of
A.foo
, it really runs a function that returns the value ofB.foo
as it is then, not as it was earlier. The analogy would be Mohammed writing down "look at the announcement" on his piece of paper instead of writing down Joe's address, and then when getting ready to go to the party, looking at his piece of paper to remember where to go, seeing "look at the announcement," going back to the break room, and seeing the updated address — and thus going to the pub.