Checking if a built in method in JS is called - javascript

I'm trying to check if Object.assign is called on my object, and if that is falsy I do some further work on the object. How do I do this? My code looks like this:
if (Object.assign) {... }

Object.assign just copies properties over to an object. To find out if a property is being set, you can use a Proxy . You can't prevent Object.assign from working only on your object. You can either:
Override Object.assign so it doesn't set properties (bad idea)
Prevent everyone from setting some properties on your obect (maybe closer to what you want)
let validator = {
set: function(obj, prop, value) {
console.log('an attempt to set values has been made', obj, prop, value);
// The default behavior to store the value
obj[prop] = value;
return true;
}
};
let person = new Proxy({}, validator);
Object.assign(person, {a: 2});
I want to stop object.assign to being called on the object
Here's a hack I really don't recommend
var objectThatCannotBeObjectAssigned = {a:1};
var oldAssign = Object.assign;
Object.assign = function(target) {
if (target === objectThatCannotBeObjectAssigned) {
return target;
}
return oldAssign.apply(this, arguments);
}
console.log(Object.assign(objectThatCannotBeObjectAssigned, {a: 2}) );
console.log(Object.assign({a:1}, {a: 2}));

Related

Transform a Javascript object into a proxy (and not its reference)

I can take a Javascript object o and create a new Proxy object from it:
let p = new Proxy(object, { ... })
But is there a way to mutate an existing object reference to track changes on the original object? In particular, is there a way I can track the addition of new keys on the object from exterior sources?
The Proxy spec supports defining a proxy on the prototype of an object as a means for inspecting actions on that object when they do not exist on the instance. While this isn't full parity with .watch() it does allow for your mentioned use case of knowing when new properties are added. Here is an example, with comments about caveats...
// assuming some existing property you didn't create...
const t = { existing: true };
// proxy a new prototype for that object...
const ctr = {};
Object.setPrototypeOf(t, new Proxy(ctr, {
get(target, key) {
console.log('icu get');
return Reflect.get(target, key) || ctr[key];
},
set(target, key, val) {
console.log('icu set');
// setting this container object instead of t keeps t clean,
// and allows get access to that property to continue being
// intercepted by the proxy
Reflect.set(ctr, key, val);
return true;
},
deleteProperty(target, key) {
console.log('icu delete');
delete ctr[key];
return true;
}
}));
// existing properties don't work
console.log('existing');
t.existing; // <nothing>
t.existing = false; // <nothing>
// new properties work
console.log('new');
t.test; // icu get
t.test = 4; // icu set
console.log(t.test); // icu get
// 4
// but this doesn't work (and I think it should be a bug)
console.log('delete');
delete t.test; // icu get
// <missing icu delete>
console.log(t.test); // 4
Just create the object first and keep a reference to it before creating its Proxy.
Now you can modify either of them (the original object or its Proxy) and the other will also receive the changes unless you prevent them on the Proxy:
const o = {};
const p = new Proxy(o, {
set: function(obj, prop, value) {
if (prop === 'd') {
return false;
}
obj[prop] = value;
return true;
},
});
// These operations are forwarded to the target object o:
p.a = 0;
p.b = 1;
// This one is prevented by the Proxy:
p.d = true;
// Both will have two properties, a and b:
console.log(o);
// You can also mutate the original object o and the Proxy will also get those changes:
o.c = false;
// Note that now the Proxy setter is not called, so you can do:
o.d = true;
// But the Proxy still gets the change:
console.log(p);
If you want to be notified when a new property is added, deleted or modified on an object without the possiblity that the original reference is used to mutate the original object directly, the only option you have is to create that object directly as a Proxy or overwrite the original one:
// Created from an empty object without a reference to it:
// const p = new Proxy({}, { ... });
// Overwrite the original reference:
let myObject = { a: 1, b: 2 };
myObject = new Proxy(myObject, {
set: function(obj, prop, value) {
if (prop in obj) {
console.log(`Property ${ prop } updated: ${ value }`);
} else {
console.log(`Property ${ prop } created: ${ value }`);
}
obj[prop] = value;
return true;
},
deleteProperty(obj, prop) {
console.log(`Property ${ prop } deleted`);
delete obj[prop];
}
});
// Now there's no way to access the original object we
// passed in as the Proxy's target!
myObject.a = true;
myObject.a = false;
delete myObject.a;
There used to be an Object.prototype.watch(), but it has been deprecated.

es6 proxy safe deep object

I wrote a small wrapper to return undefined in place of typeError when accessing properties that don't exist using a Proxy. Here is the code:
function proxify(event) {
var proxy = new Proxy(event, {
get: function (target, property) {
if (property in target) {
return target[property];
} else {
return '';
}
}
}
});
return proxy;
}
This works when a property is missing 1 level deep.
For example, assuming obj.something does not exist:
obj.something.else
will return undefined
But if the object property is deep nested
obj.something.else.deeper
I receive a typeError
My question is how do I extend the function above to work on deep nested objects?
Thx
You need to wrap the return value in your proxify function:
function proxify(event) {
return isPrimitive(event) ? event : new Proxy(event, { get: getProp });
}
function isPrimitive(v) {
return v == null || (typeof v !== 'function' && typeof v !== 'object');
}
function getProp (target, property) {
if (property in target) {
return proxify(target[property]);
} else {
return proxify({});
}
}
In all honesty, you're probably better off using something like lodash's _.get() or ramda's R.path(). There's no way to know from the first getProp() call how many layers deep the evaluation is going to go, so it has to always return a primitive value or a "truthy" Proxy instance so that the next property access can be intercepted (if one happens). The _.get() method on the other hand takes a string so it can immediately know from the initial invocation that you're only trying to access down so many levels.
I published a library on GitHub (Observable Slim) that enables you to proxy an object and any nested children of that object. It also has a few extra features:
Reports back to a specified callback whenever changes occur.
Will prevent user from trying to Proxy a Proxy.
Keeps a store of which objects have been proxied and will re-use existing proxies instead of creating new ones (very significant performance implications).
Written in ES5 and employs a forked version of the Proxy polyfill so it can be deployed in older browsers fairly easily and support Array mutation methods.
It works like this:
var test = {testing:{}};
var p = ObservableSlim.create(test, true, function(changes) {
console.log(JSON.stringify(changes));
});
p.testing.blah = 42; // console: [{"type":"add","target":{"blah":42},"property":"blah","newValue":42,"currentPath":"testing.blah",jsonPointer:"/testing/blah","proxy":{"blah":42}}]
As idbehold said, you must return a primitive value or new proxy.
I think you dont need loDash to do that, and Yes, you can manage the depth of the nested proxy ! (see my last point, deepProxy.
But, since I'm not sure what you want to do, i would like focus your attention to those few points:
First usage (simulated chain):
function proxify(event){
return isPrimitive(event) ? event : new Proxy(event,{ get: getProp });
}
function isPrimitive(v){
return v == null || (typeof v !== 'function' && typeof v !== 'object');
}
function getProp(target, property){
return (property in target)
? proxify(target[property])
: proxify({});
}
this code as it stands, will simulate nested properties,
but not confer any members dependency (because no assignment, then no preservation).
obj.something.else = 99;
console.log(obj.something.else) // returning a new Proxy()
IMO, it doesn't make sense, or reserved to very specific cases.
Again, it depend of what is your purpose.
Second usage (deep assignment):
Basic not extensible assignment
function proxify(defprop={})
{
return new Proxy(defprop, handler);
};
var handler =
{
get: function(target, property, receiver)
{
if(!(property in target))
target[property] = proxify();
return Reflect.get(target, property, receiver);
},
};
obj.something.else = 99;
console.log(obj.something.else) // returning 99
Extensible to assignment
By using basic chainable proxy, if you try to set a new object (or nested object) you will only trap the root of this new property.
There is no more possible chainning, this is not suitable and not safe.
Remember the following case:
Cyph:
But if the object property is deep nested
obj.something.else.deeper
(Bad way) Assuming this assignment:
var deeper = {toto: "titi"};
obj.something.else = deeper;
console.log(obj.something.else.toto)
// will return "titi"
obj.something.else.toto.something.else = {tutu: "tata"};
// will return a typeError Again
(Good way) To propagate the chain ability
You need to check for the type of value as a true object in the setter trap, and proxify each root properties. The rest of the depth will be converted naturally by the getter trap.
var handler =
{
get: function(target, property, receiver)
{
if(!(property in target))
target[property] = proxify();
return Reflect.get(target, property, receiver);
},
set: function(target, property, value, receiver)
{
// extend proxify to appended nested object
if(({}).toString.call(value) === "[object Object]")
value = deepApply(receiver, property, value);
return Reflect.set(target, property, value, receiver);
},
};
var deepApply = function(receiver,property, data)
{
var proxy = proxify();
var props = Object.keys(data);
var size = props.length;
for(var i = 0; i < size; i++)
{
property = props[i];
proxy[property] = data[property];
}
return proxy;
};
Manage the depth
I invite you to read my actual solution : deepProxy
The level can be auto-defined via the proto declaration
when the object structure is setted. Then you can easily know the current floor and add conditional instructions for each level.
The path of property can be reach from his accessor.

List All Prototype Properties of a Javascript Object

Is there any other way to look up for the prototype properties of an javascript object. Lets say I have like this.
function proton() {
this.property1 = undefined;
this.property2 = undefined;
};
proton.prototype = {
sample1 : function() {
return 'something';
},
sample2 : function() {
return 'something';
}
};
var my_object = new proton();
console.log(Object.keys(my_object));
returns ["property1", "property2"]
console.log(Object.getOwnPropertyNames(my_object));
returns ["property1", "property2"]
But what i want to print is the prototype properties of the object my_object.
['sample1', 'sample2']
In order for me to see the prototype properties of that object i need to console.log(object) and from developer tools i can look up for the properties of that object.
But since I am using third party libraries like phaser.js, react.js, create.js
so i don't know the list of the prototype properties of a created object from this libraries.
Is there any prototype function of Object to list down all the prototpye properties of a javascript object?
Not a prototype method, but you can use Object.getPrototypeOf to traverse the prototype chain and then get the own property names of each of those objects.
function logAllProperties(obj) {
if (obj == null) return; // recursive approach
console.log(Object.getOwnPropertyNames(obj));
logAllProperties(Object.getPrototypeOf(obj));
}
logAllProperties(my_object);
Using this, you can also write a function that returns you an array of all the property names:
function props(obj) {
var p = [];
for (; obj != null; obj = Object.getPrototypeOf(obj)) {
var op = Object.getOwnPropertyNames(obj);
for (var i=0; i<op.length; i++)
if (p.indexOf(op[i]) == -1)
p.push(op[i]);
}
return p;
}
console.log(props(my_object)); // ["property1", "property2", "sample1", "sample2", "constructor", "toString", "toLocaleString", "valueOf", "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable"
function prototypeProperties(obj) {
var result = [];
for (var prop in obj) {
if (!obj.hasOwnProperty(prop)) {
result.push(prop);
}
}
return result;
}
EDIT: This will grab all the properties that were defined on any ancestor. If you want a more granular control of what is defined where, Bergi's suggestion is good.
The quick and dirty one-liner solution would be:
console.log(Object.getOwnPropertyNames(Object.getPrototypeOf({ prop1: 'val1' })))
If you want something more precise, go with the accepted answer!

How to set class name for JavaScript object

I have some test class
TestKlass = (function() {
function TestKlass(value) {
this.value = value != null ? value : 'hello world';
console.log(this.value);
}
return TestKlass;
})();
x = new TestKlass;
x instanceof TestKlass; (gives true)
I have new empty object
y = {}
y instanceof Object
Can I find any ways to set any properties for y, something like this
y.__proto__ = x.__proto__
y.constructor.prototype = x.constructor.prototype
for to have this result
y instanceof TestKlass => true
====================================================
UPD:
So. My main aim - it's to create CLONE function. Now my solution works for me. Please look at this code:
JavaScript JS object clone
Object._clone = function(obj) {
var clone, property, value;
if (!obj || typeof obj !== 'object') {
return obj;
}
clone = typeof obj.pop === 'function' ? [] : {};
clone.__proto__ = obj.__proto__;
for (property in obj) {
if (obj.hasOwnProperty(property)) {
value = obj.property;
if (value && typeof value === 'object') {
clone[property] = Object._clone(value);
} else {
clone[property] = obj[property];
}
}
}
return clone;
};
CoffeeScript JS object clone
# Object clone
Object._clone = (obj) ->
return obj if not obj or typeof(obj) isnt 'object'
clone = if typeof(obj.pop) is 'function' then [] else {}
# deprecated, but need for instanceof method
clone.__proto__ = obj.__proto__
for property of obj
if obj.hasOwnProperty property
# clone properties
value = obj.property
if value and typeof(value) is 'object'
clone[property] = Object._clone(value)
else
clone[property] = obj[property]
clone
Now you can try to do that
A = new TestKlass
B = Object._clone(A)
B instanceof TestKlass => true
It's work fine with Moz FF 13. But I think it's not cross-browser. and proto is deprecated.
I think there is no universal solution. But maybe It's will be helpful for somebody.
Perhaps you should read the following answer first. What you are trying to achieve is really simple. However before I explain let's look at your solutions:
Solution 1
y.__proto__ = x.__proto__;
I wouldn't recommend doing this as the __proto__ property is now deprecated. The above code won't work in Rhino.
Solution 2
y.constructor.prototype = x.constructor.prototype;
This is a very bad solution. The reason is that y.constructor is actually y.__proto__.constructor and since y is an object y.__proto__ is the same as Object.prototype. This means that y.constructor is the same as Object and you're essentially changing the prototype property of Object. This could potentially break other code on your page. I strongly discourage this practice.
Solution 3 (my solution)
What you want to do is simply change the internal [[proto]] property of an object. Since there's no cross platform way to do so we need to employ a hack. A simple solution would be to create a new object with the desired __proto__ property and set getters and setters on it to change the original object. It would be as follows:
function setPrototypeOf(object, prototype) {
var constructor = function () {
for (var key in object) {
if (object.hasOwnProperty(key)) {
(function (key) {
Object.defineProperty(this, key, {
get: function () {
return object[key];
},
set: function (value) {
object[key] = value;
},
enumerable: true
});
}).call(this, key);
}
}
};
constructor.prototype = prototype;
return new constructor;
}
After this all you need to do is:
y = setPrototypeOf(y, TestKlass.prototype);
Here is a working fiddle.
Edit:
The problem with your clone function is that it only clones objects and arrays. You forgot to account for functions which are also passed by reference. Thus when you clone an object which has methods which close over the internal state of the object, the object and its clone will share the same internal state. See the following fiddle:
function Value(value) {
this.get = function () {
alert(value);
};
this.set = function (newValue) {
value = newValue;
};
}
var a = new Value(5);
var b = Object._clone(a);
b.set(10); // setting b to 10 also sets a to 10
a.get(); // alerts 10, should alert 5
There's absolutely no way in JavaScript to clone the internal state of an object so cloning objects and arrays in JavaScript will only work for objects which do not expose closures as methods.
To know more about the problems with cloning objects in JavaScript read this answer. Click on the following link to read John Resig's answer.
obj instanceof SomeConstructor checks whether SomeConstructor is found anywhere in the prototype chain of obj.
If you want inheritance, you need to set the .prototype of FirstConstructor to a newly created SomeConstructor object.
SomeConstructor = function() {}
FirstConstructor = function() {}
FirstConstroctor.prototype = new SomeConstructor();
var y = new FirstConstroctor();
y instanceof FirstConstructor; // true
y instanceof SomeConstructor ; // true, it bubbles from FirstConstructor.__proto__ to SomeConstructor

How can I define a default getter and setter using ECMAScript 5?

How can I specify a default getter for a prototype?
With default getter I mean a function that is called if obj.undefinedProperty123 is called.
I tried Object.prototype.get = function(property) {..} but this is not called in this case.
In ECMAScript 5, you can only intercept get/set operations on specific named properties (not universally all properties) via Object.defineProperty:
Object.defineProperty(someObj, "someProp", {
get: function() {
console.log("you tried to get someObj.someProp");
return "foo";
}
});
Here, the get function will run any time code tries to read someObj.someProp.
In the upcoming ECMAScript 6 draft, this will be possible via proxies. A proxy has an underlying target object and set/get functions. Any time a set or get operation happens on any of a proxy's properties, the appropriate function runs, taking as arguments the proxy's target object, property name used, and the value used in a set attempt.
var proxyHandler = {
get: function(obj, name){
console.log("you're getting property " + name);
return target[name];
},
set: function(obj, name, value) {
console.log("you're setting property " + name);
target[name] = value;
}
}
var underlyingObj = {};
// use prox instead of underlyingObj to use get/set interceptor functions
var prox = new Proxy(underlyingObj, proxyHandler);
Here, setting to getting property values on prox will cause the set/get functions to run.
What Gareth said, except it's __noSuchMethod__.
Or maybe you were thinking of PHP?
Here's a very good article on the recently standardized property getters/setters, highlighting some previous non-standard incarnations.
http://whereswalden.com/2010/04/16/more-spidermonkey-changes-ancient-esoteric-very-rarely-used-syntax-for-creating-getters-and-setters-is-being-removed/
summary: there's no standard 'catch-all' getter / setter (yet), but Object.defineProperty is the future.
You need to wait for the implementation of the ECMA6 "Proxy" system, designed to do exactly this. See http://wiki.ecmascript.org/doku.php?id=harmony:direct_proxies.
You want to create a Proxy:
const data = {};
const proxy = new Proxy(data, {
get: (target, prop) => {
console.log({ target, prop });
return "My Value";
},
set: (target, prop, value) => {
console.log({ target, prop, value });
return true;
},
});
proxy["foo"] = "bar";
const bar = proxy["foo"];
Firefox it's possible with non-standard noSuchMethod:-
({__noSuchMethod__:function(){alert(1);}}).a();
I am not sure about what you are asking. But If you want a method to be called when the user attempts to Access object.nonExistingProperty . I dont think there is any way to do that.
maybe late to ther party, let just add simple ES5 friendly "class creation": both string props and getters - i needed it for some ancient rewrite, to temporarily support IE (yes, that hurts, still found someone relying on ActiveX)
var MyObj = function () {
var obj = {
url: 'aabbcc',
a: function(){ return this.url;}
}
Object.defineProperty(obj, "urltoo", { get: function () { return this.url; } })
return obj;
}
var X = new MyObj();
x.url // aabbcc
x.urltoo // aabbcc
x.urltoo() // error - not a function
x.a() // aabbcc
x.a // ƒ (){ return this.url;}
I ran into this question because I wanted this behavior: if object property is undefined, return some default value instead.
const options = {
foo: 'foo',
bar: 'bar',
baz: 'baz'
};
function useOptionValue(optionName) {
// I want `options` to return a default value if `optionName` does not exist
const value = options[optionName];
// etc...
}
The simple way to do this (without Proxy or Object.defineProperty overkill for this use case) is like so:
function useOptionValue(optionName) {
const value = options[optionName] || defaultValue;
}
Or, if you want to get fancy, you could use a Symbol:
const default = Symbol.for('default');
const options = {
foo: 'foo',
bar: 'bar',
baz: 'baz',
[default]: 'foobarbaz'
};
function useOptionValue(optionName) {
const value = options[optionName] || options[default];
}

Categories

Resources