Suppose I am trying to assign a variable in a way that causes an exception
I am going to access a non-existent key of a dictionary:
myObject.property = dictionary['NO_KEY'][0];
Now, because 'NO_KEY' does not exist on dictionary, my program will catch an exception when trying to subscript 0 of undefined - and crash. Is it possible to execute this line above as a no-op so that my script can continue running? I know there is try-catch syntex, but is there a more elegant syntax with ESMA6?
You can use an if condition and statement, Object.hasOwnProperty() or as suggested by #Ryan in operator
if (dictionary.hasOwnProperty("NO_KEY")) {
myObject.property = dictionary["NO_KEY"][0];
}
if ("NO_KEY" in dictionary) {
myObject.property = dictionary["NO_KEY"][0];
}
Object.defineProperty(Object.prototype,
'accessWithSilentFail', {
configurable: false,
enumerable: false,
writable: false,
value: function(key) {
return this[key] ? this[key] : {};
}});
myObject.property = dictionary
.accessWithSilentFail('NO_KEY')
.accessWithSilentFail(0);
That way you get an empty object if at any point the chain fails. You need to get an object so the chain doesn't fail halfway. You can call the function something shorter if you're going to use it a lot.
Although this works, it has many, many limitations, and it changes the Object prototype, which is usually frowned upon. You really should consider just checking for undefined, which is the idiomatic way to do it.
If you ever need to check if the access chain failed, you can use:
function chainFailed(result) {
return Object.keys(result).length === 0;
}
So you could do
myObject.property = dictionary
.accessWithSilentFail('NO_KEY')
.accessWithSilentFail(0);
if (!chainFailed(myObject.property)) {
//keep on
} else {
//handle failure
}
This works as long as your expected return isn't an empty object, on which case chainFailed will always return true. But I'm assuming you really want to fail silently, because if you wanted to handle errors you could just use an exception.
Use ternary operator
myObject.property = dictionary['NO_KEY'] ? dictionary['NO_KEY'][0] : null;
While I believe this is a bad idea that will come back to bite you later, here is a solution for modern browsers using proxies. Yes, you are still checking for the property existence, but it is hidden from your code accessing the dictionary keys.
var dictionary = {a: 42};
dictionary = new Proxy(dictionary, {
get: (target, property) => {
if (target.hasOwnProperty(property)) {
return target[property];
}
return {};
}
});
// Existing properties are passed through unchanged
console.log(dictionary.a);
// Missing properties result in an empty object
console.log(dictionary.b);
// Original test
var lost = dictionary['NO_KEY'][0];
console.log(lost);
Related
I am getting this fun error when using the Proxy class:
TypeError: 'set' on proxy: trap returned truish for property 'users' which exists in the proxy target as a non-configurable and non-writable data property with a different value
I have a library which creates proxy object properties recursively, where any non-primitive property is a Proxy object itself, etc etc.:
let mcProxy = function (target) {
const mirrorCache = {};
return new Proxy(target, {
set: function (target, property, value, receiver) {
if (mirrorCache[property]) {
throw new Error(`property ${property} has already been set`);
}
mirrorCache[property] = true;
Object.defineProperty(target, property, {
writable: false,
value: (value && typeof value === 'object') ? mcProxy(value) : value
});
return true;
}
});
};
exports.create = function (val) {
val && assert.equal(typeof val, 'object', 'val must be an object');
return mcProxy(val || {});
};
actual usage of the above library code:
//bash
$ npm install proxy-mcproxy
// nodejs
let McProxy = require('proxy-mcproxy');
let val = McProxy.create();
val.users = [];
val.users = 3; // kaaaboom..error!
but when I set the users property the first time, I get the error in title of this question!
In my library code above, mirrorCache is a way to check if the property has been previously set. Want I want to do, is to throw an error, even if we aren't in strict mode, so mirrorCache appears to be necessary so that I do my own bookkeeping.
Perhaps there is a different or better way to achieve what I want to achieve? Here are my goals:
Throw an error even if not in strict mode.
Throw an error anytime the developer re-assigns a property. Each assigned property should be immutable.
Take a look at the following, section 9.5.9 of the ECMA spec:
http://www.ecma-international.org/ecma-262/6.0/#sec-proxy-object-internal-methods-and-internal-slots-set-p-v-receiver
A riveting read I'm sure you'll agree.
I believe the two keys lines are:
Let booleanTrapResult be ToBoolean(Call(trap, handler, «target, P, V, Receiver»)).
and the equally esoteric:
If targetDesc is not undefined, then
a. If IsDataDescriptor(targetDesc) and targetDesc.[[Configurable]] is false and targetDesc.[[Writable]] is false, then
i. If SameValue(V, targetDesc.[[Value]]) is false, throw a TypeError exception.
There is this relevant comment in the NOTE section:
Cannot change the value of a property to be different from the value of the corresponding target object property if the corresponding target object property is a non-writable, non-configurable own data property.
That note tries to put it into English but it doesn't indicate the key detail, which is the timing of the steps. Point 9 is the bit where your setter (trap) gets called. Unfortunately the bit where it checks whether the property is writable is point 14. So by the time the check is performed the property is indeed non-writable and non-configurable.
One way to fix this is to make the property configurable by chucking in a configurable: true in your defineProperty. I don't entirely follow your use case so I can't tell whether that would be an acceptable compromise.
I'm also wondering why you need to set these properties to be non-writable in the first place. If the underlying objects will always be accessed via their proxies then you have total control over all the set calls. I'm not even really sure why you need the mirrorCache rather than just checking whether the property is already in the target object. If you can't assume that the objects will always be accessed via their proxies then it would seem you've already lost the battle as properties can be changed without you ever knowing a thing about it.
Something like this seems close to what you want:
let mcProxy = function (target) {
return new Proxy(target, {
set: function (target, property, value) {
if (Object.prototype.hasOwnProperty.call(target, property)) {
throw new Error(`property ${property} has already been set`);
}
target[property] = (value && typeof value === 'object') ? mcProxy(value) : value;
return true;
}
});
};
It needs a bit more tweaking to work with arrays properly but I'm unclear which array methods you would expect to support.
I think the issue has to do with the options you passed to your Object.defineProperty method. Change your writable option from false to true and I think your issue should be resolved.
MDN has the following description for the writable property.
writable
true if and only if the value associated with the property may be changed with an assignment operator.
Defaults to false.
So technically Object.defineProperty is the first setting of the property. But that's a side note. As we can see from MDN's description writable set to false disallows us from changing the property via an assignment operator = is an example of an assignment operator.
MDN Link: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
Provided that the object MAY contain own property called "hasOwnProperty":
> a={abc: 123};
{ abc: 123 }
> a.hasOwnProperty("abc");
true
> a['hasOwnProperty'] = 1;
1
> a.hasOwnProperty("abc");
TypeError: a.hasOwnProperty is not a function
...
This works, kinda ugly interface, if you think about Object.keys(), Object.assign() ETC.. So, is there a better way?
> Object.hasOwnProperty.call(a, "abc");
true
> Object.hasOwnProperty.call(a, "hasOwnProperty");
true
And why shouldn't the solution be the only recommended way? Using methods directly from an object seems like a recipe for a failure, especially if it is containing external data (not in one's control)
The appropriate/recommended way to use hasOwnProperty is as a filter, or a means to determine whether an object... well, has that property. Just they way you are using it in your second command a.hasOwnProperty('abc').
By overwriting the Object hasOwnProperty property with a['hasOwnProperty'] = 1, while it's safe and valid, just removes the ability to use the hasOwnProperty function on that Object.
Am I missing your true question here? It seems like you already knew this from your example.
By
'using methods directly from an object seems like a recipe for a failure
are you referring to something like this:
> dog = {speak: function() {console.log('ruff! ruff!')}};
> dog.speak(); // ruff! ruff!
Because that is extremely useful in many ways as you can imagine.
If you can use ECMAScript 2015 you can try Reflect.getOwnPropertyDescriptor.
It returns a property descriptor of the given property if it exists on the object, undefined otherwise.
To simplify you can create this function:
var hasOwnProp = (obj, prop) => Reflect.getOwnPropertyDescriptor(obj, prop) !== undefined;
var obj = new Object();
obj.prop = 'exists';
console.log('Using hasOwnProperty')
console.log('prop: ' + obj.hasOwnProperty('prop'));
console.log('toString: ' + obj.hasOwnProperty('toString'));
console.log('hasOwnProperty: ' + obj.hasOwnProperty('hasOwnProperty'));
var hasOwnProp = (obj, prop) => Reflect.getOwnPropertyDescriptor(obj, prop) !== undefined;
console.log('Using getOwnPropertyDescriptor')
console.log('prop: ' + hasOwnProp(obj, 'prop'));
console.log('toString: ' + hasOwnProp(obj, 'toString'));
console.log('hasOwnProperty: ' + hasOwnProp(obj, 'hasOwnProperty'));
obj['hasOwnProperty'] = 1;
console.log('hasOwnProperty: ' + hasOwnProp(obj, 'hasOwnProperty'));
Any built-in can be overridden in JS - it's generally considered best practice to avoid overriding any native methods where possible. If the original functionality is preserved it's OK as it will still behave as expected and even could possibly extended further if overridden correctly again.
As that's considered best practice I recommend either remapping the keys to avoid overriding them. If remapping the keys is not an option then you can maybe make it feel a little less messy by either locally referencing/wrapping Object.hasOwnProperty or Object.prototype.hasOwnProperty. In the case of hasOwnProperty you could possibly implement an iterator (as iterating over enumerable non-inherited properties is a very common use of hasOwnProperty) method to reduce the likelihood of its use. There's always still the risk of someone less familiar with your object attempting to directly iterate so I really feel that key mapping is the safer bet even if it does cause a slight difference in between server-side keys and local ones.
A key mapping could be as simple as a suffix using hasOwnProperty_data instead of hasOwnProperty this would mean objects would behave as expected and your IDE's autocomplete likely will still be close enough to know what the property represents.
A mapping function might look like the following:
function remapKeys(myObj){
for(var key in myObj){
if(Object.prototype.hasOwnProperty.call(myObj, key)){
if((key in Object) && Object[key] !== myObj[key]){ // Check key is present on Object and that it's different ie an overridden property
myObj[key + "_data"] = myObj[key];
delete myObj[key]; // Remove the key
}
}
}
return myObj; // Alters the object directly so no need to return but safer
}
// Test
var a = {};
a.hasOwnProperty = function(){ return 'overridden'; };
a.otherProp = 'test';
remapKeys(a);
console.log(a); // a { hasOwnProperty_data : function(){ return 'overridden';}, otherProp: 'test' }
console.log(a.hasOwnProperty('otherProp')); // true
Here is the situation: I have checking on existing class like:
('Promise' in window) // true/false`
And I wanna force return false or true on it, can I do it?
Yes, I can check it by some other way, like `
window.Promise = undefined;
window.Promise === undefined;
Or something like this, but can I somehow delete this object or simulate something for 'in' operator?
I check specification and v8 code, there is 'in' operator just call 'HasProperty' operator, which realization on c++.. I know 'hack' with faking toString/valueOf methods:
obj = {
toString: function(){ return 'myName'; }
},
obj2 = {};
obj2[obj] = 1; // Object {myName: 1}
May be I can use it in some way? But, as I send string 'Promise' I can't just fake it like this way.. may be exist some way to fake 'HasProperty'?
In javascript, it is possible to define an object's property as a getter/setter rather than just a "plain" value:
Object.defineProperty( obj, 'complex_property', {
get: function getter() { return this.protected; },
set: function setter( value ) { this.protected = value; }
} );
Is there any way to return a non-value property of an object without first having its getter function evaluated so that this (or the like) is possible?
obj.property = function( name ) { return this.[ name ]; };
// Doesn't work:
// ReferenceError: invalid assignment left-hand side
obj.property( 'complex_property' ) = 5;
The last line effectively reads 6 = 5 - obviously an error - since this.complex_property is evaluated first, then returned. Combining closures with getter-setter properties is an effective way to simulate "private" properties and validate assignment values without having to use actual get/set functions: one of the nicer features of modern javascript. It'd be even nicer if there was a way to return an unevaluated getter/setter property: is there a way I've missed or is it just not possible? Are we stuck using set( prop, value ) functions?
Unfortunately, this isn't possible with JavaScript functions. All JavaScript functions created by user code can only return values, not references (and this won't be possible by code within the browser too, starting from ES6).
A ReturnStatement is evaluated as follows (bold my emphasis):
If the Expression is not present, return (return, undefined, empty).
Let exprRef be the result of evaluating Expression.
Return (return, GetValue(exprRef), empty).
The only way to do so is either using a set(property, value) function or creating an actual setter.
You can get a property. Well, a property descriptor at least. And it ain't pretty.
var prop = Object.getOwnPropertyDescriptor(obj /* from question */, 'complex_property');
prop.set.call(obj, 5);
edit:
obj.property = function (name) { return Object.getOwnPropertyDescriptor(this, name); }
obj.property('complex_property').set.call(obj, 5);
I like how Ruby's .tap method works. It lets you tap into any method chain without breaking the chain. I lets you operate an object then returns the object so that the methods chain can go on as normal. For example if you have foo = "foobar".upcase.reverse, you can do:
"foo = foobar".upcase.tap{|s| print s}.reverse
It will print the upcased (but not reversed) string and proceed with the reversing and assignment just like the original line.
I would like to have a similar function in JS that would serve a single purpose: log the object to console.
I tried this:
Object.prototype.foo = function() {
console.log(this);
return this;
};
Generally, it works (though it outputs Number objects for numbers rather than their numeric values).
But when i use some jQuery along with this, it breaks jQuery and stops all further code execution on the page.
Errors are like this:
Uncaught TypeError: Object foo has no method 'push'
Uncaught TypeError: Object function () { window.runnerWindow.proxyConsole.log( "foo" ); } has no method 'exec'
Here's a test case: http://jsbin.com/oFUvehAR/2/edit (uncomment the first line to see it break).
So i guess that it's not safe to mess with objects' prototypes.
Then, what is the correct way to do what i want? A function that logs current object to console and returns the object so that the chain can continue. For primitives, it should log their values rather than just objects.
You correctly figured out how a method can be safely added anywhere in a chain, but your adding it to the Object.prototype is intrusive and can break code easily. Looks like jQuery code is the one that breaks for you.
A much safer way is:
Object.defineProperty(Object.prototype, 'foo', {
value : function() { console.log( "foo" ); return this; },
enumerable : false
});
DEMO: http://jsbin.com/oFUvehAR/7/edit
Finally, something generic could look like this:
Object.defineProperty(Object.prototype, 'tap', {
value : function(intercept) {
intercept.call(this);
return this;
},
enumerable : false
});
// Usage:
var x = { a:1 };
x.tap(function(){ console.log(this); });
As for the primitives part of your question, that is a bit trickier. When you call the tap method on a primitive, an Object wrapper is created and the tap method is called on it. The primitive value is still available, via the valueOf() method of that Object wrapper, so you could log it. The tricky part is that you have no way of knowing if the "thing" that you wanted to call the tap method on was initially a primitive or an Object wrapper. Assuming you never want to work with Object wrappers (that is quite reasonable), you could do the better tap method posted below.
Object.defineProperty(Object.prototype, 'tap', {
value : function(intercept) {
var val = (this instanceof Number || this instanceof String || this instanceof Boolean) ? this.valueOf() : this;
intercept(val);
return val;
},
enumerable : false
});
var log = console.log.bind(console);
var x = { a : 1 };
x.tap(log);
2.0.tap(log);
Notice that while in the first version of the tap function, the function passed to it had the useful information in this, in the second version it is mandatory to pass it as a parameter.
If you want a specialized logger for it, you can do something like this:
Object.defineProperty(Object.prototype, 'log', {
value : function(){
return this.tap(console.log.bind(console));
},
enumerable : false,
writable : true /* You want to allow objects to overwrite the log method */
});