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

Make Color.js proxy-friendly, resolves #305 #306

Merged
merged 3 commits into from
May 1, 2023
Merged

Make Color.js proxy-friendly, resolves #305 #306

merged 3 commits into from
May 1, 2023

Conversation

LeaVerou
Copy link
Member

No description provided.

Copy link
Member

@jgerigmeyer jgerigmeyer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@LeaVerou LGTM! I agree that comparing this.id === space.id isn't ideal... but I can't think of an obviously better alternative?

src/space.js Show resolved Hide resolved
@@ -57,7 +57,12 @@ export default class ColorSpace {
this.referred = options.referred;

// Compute ancestors and store them, since they will never change
this.#path = this.#getPath().reverse();
Object.defineProperty(this, "path", {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this have some @private jsdoc to let IDEs know they should treat it as private ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn’t really need to be private, as long as it’s immutable.

@stof
Copy link

stof commented Apr 28, 2023

This will also close #220 due to not using private methods anymore

@LeaVerou
Copy link
Member Author

LeaVerou commented May 1, 2023

Ok, merging this with a heavy heart 💔

@LeaVerou LeaVerou merged commit c161c1f into main May 1, 2023
@bakkot
Copy link

bakkot commented May 2, 2023

@LeaVerou There's a simple way to opt in to being transparent to proxies if you explicitly want that: access private fields by deferring to the actual object, instead of the current value of this. Similarly, compare equality by the actual object, not by the current value of this.

class Example {
  _ = this;
  #private = 42;
  method() {
    console.log(this._.#private);
  }
}

let proxiedExample = new Proxy(new Example, {});
console.log(proxiedExample.method()); // 42

That won't solve your problems with Vue specifically because it's not just a simple Proxy - it recursively Proxy's every single property (including _ in this example). (This incidentally requires a bunch of effort to work with built-in types and only works with a small handful they've special-cased.)

But you can interop with Vue specifically with a little more work, if you really want that:

import { createApp } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js'

function makeVueNotProxyBox(value) {
  return {
    [Symbol.toStringTag]: 'box', // using a custom toStringTag will prevent Vue from trying to proxy this thing
    _: value,
  };
}
class Foo {
  #bar = 1;
  _ = makeVueNotProxyBox(this);
  
  get bar() {
    return this._._.#bar;
  }
}

createApp({
  data() {
    return {
      foo: new Foo()
    }
  }
}).mount("#app");

(This assumes you want to make a thing work with Vue without actually depending on Vue. There's easier ways if you're specifically designing Vue components.)

@redzzy30
Copy link

redzzy30 commented Jun 7, 2023

@LeaVerou There's a simple way to opt in to being transparent to proxies if you explicitly want that: access private fields by deferring to the actual object, instead of the current value of this. Similarly, compare equality by the actual object, not by the current value of this.

class Example {
  _ = this;
  #private = 42;
  method() {
    console.log(this._.#private);
  }
}

let proxiedExample = new Proxy(new Example, {});
console.log(proxiedExample.method()); // 42

That won't solve your problems with Vue specifically because it's not just a simple Proxy - it recursively Proxy's every single property (including _ in this example). (This incidentally requires a bunch of effort to work with built-in types and only works with a small handful they've special-cased.)

But you can interop with Vue specifically with a little more work, if you really want that:

import { createApp } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js'

function makeVueNotProxyBox(value) {
  return {
    [Symbol.toStringTag]: 'box', // using a custom toStringTag will prevent Vue from trying to proxy this thing
    _: value,
  };
}
class Foo {
  #bar = 1;
  _ = makeVueNotProxyBox(this);
  
  get bar() {
    return this._._.#bar;
  }
}

createApp({
  data() {
    return {
      foo: new Foo()
    }
  }
}).mount("#app");

(This assumes you want to make a thing work with Vue without actually depending on Vue. There's easier ways if you're specifically designing Vue components.)

It's really sad that we have to resort to this. This should be a feature natively supported by JS, instead of having developers take care of it. If I knew private properties would be implemented like this, I'd rather not have them at all and just stick with the good old _private_property

@bakkot
Copy link

bakkot commented Jun 7, 2023

@redzzy30 You have to write code like in my example anyway if you want === to work. That's a consequence of the design of Proxies.

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

Successfully merging this pull request may close these issues.

5 participants