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?
1 Answer 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:
- Length can be set to anything (only the array will handle this anyway)
- Numeric properties can only be set to Media instances.
- 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.
5 Comments
.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.Number(key) == key, you might want to explicitly check for String(key >>> 0) === key && (key >>> 0) != 4294967295>>> seems to do the type conversion and rounding trick)? Is 4294967295 the highest possible index?>>> 0 is the common ToUint32 replication.
push. It in turn calls "get length" and "set 0". So what exactly qualifies the access of a property as internal?