Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Virtual properties (accessors) as well as virtual methods #33

Open
andyearnshaw opened this issue May 11, 2016 · 9 comments
Open

Virtual properties (accessors) as well as virtual methods #33

andyearnshaw opened this issue May 11, 2016 · 9 comments

Comments

@andyearnshaw
Copy link

I'm a huge fan of this proposal, I'd really like to see it advance to Stage 1 and beyond. I'd also like to get some thoughts on providing virtual accessors as well as virtual methods. For example, lets assume some kind of Reflect.createLooseAccessor() function:

const
    wm = new WeakMap(),
    data = Reflect.createLooseAccessor({
        get: () => wm.get(this),
        set: (v) => wm.set(this, v)
    });

let foo = document.getElementById('foo');
foo::data = 'bar';
console.log(foo::data);
// -> "bar"

This reminds me of jQuery's data(), but doesn't change the target object, making it possible to use it on immutable objects. Another real-world example:

import { should } from 'chai';

const foo = {};
test('A test', function () {
    foo.bar::should.be.a('function');
});

Chai's should sits on Object.prototype is generally a bad idea if the value being tested is potentially undefined or has no [[Prototype]] (e.g. an object created with Object.create(null)). As a virtual accessor, it doesn't infect anything and can be used on any value type.

I realise there are significant conflicts with the current proposal (such as foo::data behaving differently for a virtual accessor), but my goal is just to spark a discussion about it. I also realise that esdiscuss might be a better place to raise this, but thought it a good idea to get some feedback from proponents of this proposal first.

@Alxandr
Copy link

Alxandr commented May 11, 2016

WRT Chai's should (imho) that is rather just implemented as a function. It'll behave (mostly) like C#'s extension methods, in that you could do foo.bar::should().be.a('function');. No need for prototype modifications, nor changing this proposal at all.

@andyearnshaw
Copy link
Author

Yeah, I thought that would be one of the first things mentioned 😉 The same could be said of my first example, that foo::data('bar') could be preferred to foo::data = 'bar'. I think it's the aesthetics of virtual accessors I like the most, it looks more natural.

@zloirock
Copy link

It's the most interesting part of abstract references which we lost after withdrawing this proposal.

https://github.com/zenparsing/es-abstract-refs
zenparsing/es-abstract-refs#12 (comment)

@Igorbek
Copy link

Igorbek commented Jul 15, 2016

That wouldn't work, because of ambiguity being impossible to resolve. Say we have this:

const fn = { a: () => 1 };
const prop = createAccessor({
  get: function () { return ({ a: () => 2 }); }
});

x::fn.a() // 1
// desugars to
fn.a.call(x);

// on the other hand,
// if we desugared this
x::prop
// to
prop.get.call(x)
// then would this
x::prop.a()
// be desugared to
prop.get.call(x).a() // ? is that what you wanted?
// however, if align with prev example, we'll get this
prop.a.call(x)

@benjamingr
Copy link

Note that we can get close with a proxy:

const datas = new WeakMap();
function data() {
  let map = datas.get(this);
  if(!map) {
     map = new Map();
     datas.set(this, map)
  }
  return new Proxy(this, {
    set(target, prop, value, receiver) {
        map.set(prop, value);
        return value;
    },
    get(target, prop, receiver) {
      return map.get(prop);
    }
  });
};
var obj = {};
obj::data.x = 15;
obj.x; // undefined
obj::data.x; // 15

@Igorbek
Copy link

Igorbek commented Jul 15, 2016

I was pointing that it would be impossible to disambiguate:

obj::data.fn();

whether it is a binded property accessor data that returns { fn: () => { ... } }
or it is a data = { fn: () => { ... } } so that data.fn is actually being binded.

@Igorbek
Copy link

Igorbek commented Jul 15, 2016

or more clear, with precedence in place:

obj::data.fn() == (obj::(data.fn))(); // '.' has higher precedence that '::', as it stands now
obj::data.fn() == (obj::data).fn();   // '::' would have priority

And note, latter is highly undesirable.

@WebReflection
Copy link

FWIW agreed '::' should have lower priority than '.' or it would lose most
of its use cases, first of all: borrowing methods

Needing a reference upfront or parenthesis to explicit make it lower
priority is undesired indeed.

On Friday, 15 July 2016, Igor Oleinikov [email protected] wrote:

or more clear, with precedence in place:

obj::data.fn() == (obj::(data.fn))(); // '.' has higher precedence that '::', as it stands now
obj::data.fn() == (obj::data).fn(); // '::' would have priority

And note, latter is highly undesirable.


You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
#33 (comment),
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAFO9Qag8CZgAFUX9UKeFE3QEUt8NYheks5qWAOngaJpZM4Ib43M
.

@dead-claudia
Copy link
Contributor

This sounds like something better suited to suggest in the private fields repo. I don't think any suggestion here would gain much traction.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants