0

Consider this code:

app.mediaLibrary = new function MediaLibrary() {
    var self = this;

    self.media = new Proxy([], {
        set(target, key, value) {
            console.log(`Setting value ${key} as ${value}`)
            if (!(value instanceof self.Media))
                throw new Error(`${value} is not instance of Media.`)
            target[key] = value;
            return true
        },
        get (target, key) {
            console.log(`Getting value ${key} as ${target[key]}`)
            return target[key]
        }
    });

    self.Media = function Media(file, fileReader) {
        this.src = fileReader.result;
        this.file = file;
    }

    return self;
}

Whenever I call app.mediaLibrary.media.push(new app.mediaLibrary.Media("", "")) In console I see this:

Getting value push as function push() { [native code] }
Getting value length as 0
Setting value 0 as [object Object]
Setting value length as 1
Uncaught Error: 1 is not instance of Media.(…)

I understand why I see this but how can I code around it? It seems my traps are triggered by internal(push,length) as well as external calls([0]=...) and I don't know how to differentiate between them. Any ideas?

6
  • 1
    I don't understand your differentiation scheme. If a call can be regarded as "external", then it's definitely push. It in turn calls "get length" and "set 0". So what exactly qualifies the access of a property as internal? Commented Apr 22, 2016 at 7:26
  • I didn't call get length in my code so it's internal/implicit call, not external/explicit. Commented Apr 22, 2016 at 8:15
  • According to that definition every library in the world would consist of only internal calls. Even if someone calls your code in a callback. That doesn't make much sense. Commented Apr 22, 2016 at 8:20
  • Yes, you got it. In my case that library is vanilla implementation of array. Anyway, do you know how could I solve my problem? Commented Apr 22, 2016 at 8:42
  • "how could I solve my problem" I have a vague idea of what you want to achieve - It's not possible for obvious reasons - but I honestly don't see a problem that needs to be solved. Commented Apr 22, 2016 at 8:49

1 Answer 1

1

I think you are asking the wrong question. This isn't about internal vs external calls, this is about the specific object that you are proxying: An array.

You could go with three conditions:

  1. Length can be set to anything (only the array will handle this anyway)
  2. Numeric properties can only be set to Media instances.
  3. All other properties are off limits.

You could write that like this:

app.mediaLibrary = new function MediaLibrary() {
    var self = this;

    self.media = new Proxy([], {
        set(target, key, value) {
            console.log(`Setting value ${key} as ${value}`)

            // Check if this is a valid array index
            // See http://www.ecma-international.org/ecma-262/6.0/#sec-array-exotic-objects
            if (String(key >>> 0) === key && (key >>> 0) != Math.pow(2, 32) - 1) {
                if (!(value instanceof self.Media))
                    throw new Error(`${value} is not instance of Media.`);
            } else if(key !== 'length') {
                throw new Error(`${key} may not be written to.`);
            }
            target[key] = value;
            return true
        },
        get (target, key) {
            console.log(`Getting value ${key} as ${target[key]}`)
            return target[key]
        }
    });

    self.Media = function Media(file, fileReader) {
        this.src = fileReader.result;
        this.file = file;
    }

    return self;
}

If you really need to differentiate between .push and [0]=..., then no, it can't be done.

Sign up to request clarification or add additional context in comments.

5 Comments

Well, you could differentiate .push() by decorating the returned function in the getter, but I agree that it's not necessary when checking the property names like you do.
Instead of using Number(key) == key, you might want to explicitly check for String(key >>> 0) === key && (key >>> 0) != 4294967295
@Bergi good idea with the decorators, thanks. I'm a bit spotty on the bitwise operators, would you mind explaining your second part (the >>> seems to do the type conversion and rounding trick)? Is 4294967295 the highest possible index?
It's quite literally the isArrayIndex algorithm from the spec :-) >>> 0 is the common ToUint32 replication.
Thanks a lot, that makes sense. I integrated it into the answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.