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

Color constructor should handle argument as immutable #590

Closed
bennyschudel opened this issue Aug 26, 2024 · 8 comments · Fixed by #603
Closed

Color constructor should handle argument as immutable #590

bennyschudel opened this issue Aug 26, 2024 · 8 comments · Fixed by #603
Labels
bug Something isn't working

Comments

@bennyschudel
Copy link

Hey there!

I was surprised to figure out that a call to new Color({ ... }) modifies my provided object by adding the space to it.

image

I guess this is a 🪲

Best
Benny

@LeaVerou
Copy link
Member

Ah yeah, it should copy the object, at least when it needs to be modified. Especially since perf is not a huge consideration with the OOP API.

@LeaVerou LeaVerou added the bug Something isn't working label Aug 27, 2024
@MysteryBlokHed
Copy link
Member

It looks like getColor modifies the passed object, adding the space key:

color.js/src/getColor.js

Lines 40 to 43 in 45ba172

if (!(space instanceof ColorSpace)) {
// Convert string id to color space object
color.space = ColorSpace.get(space);
}

This could be solved by calling structuredClone before getColor in the Color constructor:

color.js/src/color.js

Lines 48 to 56 in 45ba172

if (args.length === 1) {
let parseMeta = {};
color = getColor(args[0], {parseMeta});
if (parseMeta.format) {
// Color actually came from a string
this.parseMeta = parseMeta;
}
}

i.e.:

color = getColor(structuredClone(args[0]), {parseMeta});

@LeaVerou
Copy link
Member

LeaVerou commented Oct 1, 2024

Not sure we need structuredClone(), as this is a flat object, so Object.assign({}, ...) should work?

@MysteryBlokHed
Copy link
Member

Object.assign wouldn't work on Color class instances that get passed afaik, which is why I suggested structuredClone. But after looking at the code, I don't think there's actually a way for a Color instance to have its space property mutated by getColor (since the function only adds the space property if it is missing, and Color instances always have that property). So we could probably just do Object.assign if the passed argument is an object, and leave other inputs alone?

@LeaVerou
Copy link
Member

We can detect if something is a plan object and branch accordingly. Not sure if that would be the best course of action, just throwing it out there as an option.

@MysteryBlokHed
Copy link
Member

That seems like the most performant thing to do—although I don't actually know the best way to check if something is a plain object in JS. Maybe args[0].constructor === Object? Or we could also just check if it's not an instance of Color.

@LeaVerou
Copy link
Member

That seems like the most performant thing to do—although I don't actually know the best way to check if something is a plain object in JS. Maybe args[0].constructor === Object? Or we could also just check if it's not an instance of Color.

This is how I did it in the past: https://github.com/mavoweb/mavo/blob/main/src/util.js#L61 ($.type() just checks Object.protoyype.toString.call(), we must have a similar util here)

@MysteryBlokHed
Copy link
Member

Based on that, I think something like typeof args[0] === 'object' && Object.getPrototypeOf(args[0]).constructor === Object should work.
I should be able to open a PR for the change this evening

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants