简体   繁体   中英

Placeholders for anonymous functions in JavaScript

I've been writing d3 code that ends up having lots of functions like this:

selection.attr('x', function(d) { return d.layout.x; });

Is there any way to simulate Scala's placeholder syntax, which would allow me to write this:

selection.attr('x', _.layout.x);

Obviously getters need to be told a specific parameter name to apply to, or one could define a sort of 'meta-getter' that responds with an anonymous function that gets the desired named attribute.

I'd be interested to know if anything like this exists in, for example, CoffeeScript. ES6 lambda functions are closer, but still not as semantic and clear as placeholder syntax.

You don't say what environment you want this to run in, so, assuming that the bleeding edge is okay, let's use Proxy :

var _ = new Proxy({}, {
    get: function(target, name) {
        return createProxyForPath(name, []);
    }
});

function createProxyForPath(name, path) {
    var newPath = path.slice();
    newPath.push(name);
    return new Proxy({}, {
        get: function(target, name) {
            if (name !== "$") return createProxyForPath(name, newPath);
            return function getter(obj) {
                return newPath.reduce(function(prev, curr) {
                    return prev[curr];
                }, obj);
            };
        },
        apply: function(target, context, args) {
          // TODO: Preserve function calls and args here
        }
    });
}

You would use it like this:

> [{x: 1}, {x: 2}, {x: 3}].map(_.x.$)
[1, 2, 3]

It's not a complete replacement for Scala's magic underscore (it doesn't trap method calls right now, for example, so you can't do _.x.toString().slice(0, 3) to take one example). Also, it requires an explicit $ to signal the end of the chain. But for simple getters it works pretty well.

Alternately, if you need to support browsers that aren't Firefox right now you could write a sweet.js macro to generate the getter instead:

// Via Daniel
macro _ {
  rule { . $m ... } => { function (value) { return value.$m ... } }
}

selection.attr('x', _.layout.x + 1);

Will expand to:

selection.attr('x', function(value) {
  return value.layout.x + 1;
});

(If you use value yourself in the function sweet.js will do the right thing and rename the argument to value$some-integer to avoid any name conflicts inside the anonymous function.)

It does handle method calls, but of course none of these approaches handle using the placeholder as, for example, a function argument:

selection.attr('x', someFunction(_));

This could be emulated with a function instead of an object:

var getter = function(properties) {
  props = properties.split('.');
  return function(d) {
    return props.reduce(function(prev, curr) {
      return prev[curr];
    }, d);
  };
};

selection.attr('x', getter('layout.x'));

Which is... okay, but I wonder if JS can do better.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM