John John - 6 months ago 10
Javascript Question

How to create a function that creates a function to constantly return an object's key

I'm trying to create a function that creates a function to constantly return an object's key, but I am unsure of how to write it.

for example, I can do the below:

var parent = {data: 123}
var child = {}

child.data = function() {
return parent.data
}

child.data() // 123
parent = {data: 456}
child.data() // 456


This works fine, but I want to create a function that creates the data function, something like:

var parent = {data: 123}
var child = {}
create_data_function(parent, child, "data")

child.data() // 123
parent = {data: 456}
child.data() // 456


The below would not work, since the "from" variable is reassigned

function create_data_function(from, to, key) {
to[key] = function() {
return from[key]
}
}


Is creating the create_data_function possible?
I am unsure what to search for to find the solution for this.

Thanks for reading :)

Answer

Let's refer to the initial object that parent points to as Object 1, and the new object you assign to parent as Object 2.

The value in your parent variable is an object reference, initially a reference to Object 1. When you do:

 create_data_function(parent, child, "data");

the value of parent is read and passed into create_data_function. That value is a reference to Object 1. Nothing, at all, related to the parent variable is passed into create_data_function. The function it creates closes over the from argument from the create_data_function call that created it, which will always contain a reference to Object 1.

To create a function that does what you describe, the function has to have access to the parent variable itself. JavaScript doesn't have any form of pass-by-reference (which lets you pass a reference to a variable into a function), so we have to find another way to give create_data_function access to the parent variable.

The simplest way is to ensure that create_data_function closes over parent, by defining it in a scope that has access to parent, like this:

var parent = {data: 123};
var child = {};
create_data_function(child, "data");
//                   ^---- Note we aren't passing in `parent`

snippet.log(child.data()); // 123
parent = {data: 456};
snippet.log(child.data()); // 456

function create_data_function(to, key) {
// Note no `from` ------------^
    to[key] = function() {
        return parent[key];
//             ^^^^^^------ instead we use `parent` directly
    };
};
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="//tjcrowder.github.io/simple-snippets-console/snippet.js"></script>

That's possible because create_data_function is created in a place where you could use the parent variable directly.

But what it if weren't? We'd solve it by passing an accessor function into create_data_function, like this:

// Note that because `parent` is a local within `test`,
// and `create_data_function` isn't, `create_data_function`
// doesn't have direct access to `parent` (it doesn't *close over*
// `parent`).
function test() {
  var parent = {data: 123};
  var child = {};
  create_data_function(function() {   // So we give it a function to call
    return parent;                    // to get `parent`'s current value
  }, child, "data");

  snippet.log(child.data()); // 123
  parent = {data: 456};
  snippet.log(child.data()); // 456
}

function create_data_function(fromAccessor, to, key) {
// Accept the accessor -------^
    to[key] = function() {
        return fromAccessor()[key];
// Call it ----^^^^^^^^^^^^^^
    };
};

test();
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="//tjcrowder.github.io/simple-snippets-console/snippet.js"></script>

That's a bit less clunky with ES2015 syntax, because of the new arrow functions:

// THIS EXAMPLE REQUIRES ES2015 SUPPORT IN YOUR BROWSER

function test() {
  var parent = {data: 123};
  var child = {};
  create_data_function(() => parent, child, "data");
  // The accessor func ^^^^^^^^^^^^

  snippet.log(child.data()); // 123
  parent = {data: 456};
  snippet.log(child.data()); // 456
}

function create_data_function(fromAccessor, to, key) {
    to[key] = function() {
        return fromAccessor()[key];
    };
};

test();
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="//tjcrowder.github.io/simple-snippets-console/snippet.js"></script>