JS Proxy & destructuring assignment - javascript

As what i know js Proxies make possible to overload classic object getter with a function call.
So that we can do things like that :
const value = myProxy.value;
All that calling in fact the value getter function inside Proxy.
My question is ... is there a way to use the JS destructuring syntax with JS Proxies ?
So that we could do things like that :
const { value } = myProxy;
Based on my tests, the second way is not working.

It is working with a necessary implemented getter.
const
myProxy = new Proxy({}, {
get: function(obj, prop) {
return 42;
}
}),
{ value } = myProxy;
console.log(myProxy.value);
console.log(value);

Related

Stringify object differently depending on context

I have an object with a toJSON method:
const v = {
foo: 'zoom1',
bar: 'zoom2',
toJSON(){
return {foo: this.foo}
}
}
when I call JSON.stringify(v) I will get {foo:'zoom1'} - my question is - is there a way to stringify it differently depending on who is calling JSON.stringify(), for example, what if I want:
{bar: 'zoom2'}
instead? Maybe there is a good design pattern for this, dunno.
Actually, you could use the replacer parameter of the stringify function:
A function that alters the behavior of the stringification process, or an array of String and Number objects that serve as a whitelist for selecting/filtering the properties of the value object to be included in the JSON string. If this value is null or not provided, all properties of the object are included in the resulting JSON string.
Example:
const v = {
foo: 'zoom1',
bar: 'zoom2'
};
JSON.stringify(v, ['foo']);
// Outputs "{"foo":"zoom1"}
JSON.stringify(v, ['bar']);
// Outputs "{"bar":"zoom2"}
JSON.stringify(v, ['foo', 'bar']);
// Outputs "{"foo":"zoom1", "bar":"zoom2"}
There are a couple deprecated methods that allow you to determine who called a particular function: arguments.callee and function.caller.
However, I would suggest that instead of altering the object you are trying to serialize, or monkey patching a built-in serialization function, you instead just define helper serialization methods that will return the object in different ways (like different views on the same data)
Something like the following idea, if flat and deep are the two different views you want to return of the object:
const myData = {}
const flatSerializer = (data) => {}
const deepSerializer = (data) => {}
const flatString = flatSerializer(myData)
const deepString = deepSerializer(myData)
If you want to introduce some function composition, you could also create different ways of representing the data that are passed into the same serialization function.
Using that same example, you might have something like:
const myData = {}
const serializer = fn => data => fn(data)
const flat = data => {}
const deep = data => {}
const flatSerialize = serializer(flat)
const deepSerialize = serializer(deep)
const flatString = flatSerialize(myData)
const deepString = deepSerialize(myData)
One option could be to use the replacer argument to stringify. It is a function called with the key and value of each property being stringified in the Object, and if you return undefined from it, the property is excluded from stringify result.
You could have a bunch of replacer functions in a library each of which returns undefined for a set of properties you want to exclude i.e. so essentially creating different "views" of the object. Then the caller selects which of the replacers it wants when calling stringify.
Reference
Haven't tried this yet, but I think a custom replacer is the answer, something like this:
const result = JSON.stringify(v, (val) => {
if(val instanceof whatever){
return {bar: val.bar};
}
return val;
});

Possible to modify but not replace a method's prototype?

Let's say I have a method Object.getNumber() and I want to make a small change to the results of that method, for example add 1 to the result, and no I can't access that method's code.
Any way to do this with its prototype? I would need to access the method within the defineProperty call I suppose.
Object.defineProperty(Object.prototype, 'getNumber',{ get: function () { ... } });
You can always replace the original method in the prototype, but keep the original somewhere in the local variable. Something like this:
Object.prototype.getNumber = (function() {
let originalFn = Object.prototype.getNumber;
return function() {
let originalResult = originalFn.apply(this, arguments);
return originalResult + 1;
};
})();
A method is just a function that you can pass around so you can simply apply that function after another:
const getNumber = Object.prototype.getNumber;
Object.prototype.getNumber = function() {
const result = getNumber();
return result + 1;
};
But modifying a native prototype is not good practice, especially Object.prototype as that affects too many things.

JavaScript: Setter for inner object

I have a simple object and constructor in Traditional JavaScript as follows:
function Test() {
this.data={};
}
Test.prototype={
set value(something) {
},
get value() {
return data[property];
}
};
var test=new Test();
Inside the object is another object, initially with no own properties.
I would like to write a Setter for value which sets a property on the inner object (data).
test.value.whatever=23;
Is there any way I can do this?
I expect that the Setter function could then finish the job with something like this:
set value() {
// how do I get property & value?
data[property]=value;
},
Here you go
function Test() {
this.data = {};
}
Test.prototype={
set value(v) {
this.data.whatever = v;
},
get value() {
return this.data.whatever;
}
};
var test = new Test();
test.value = 'hi';
console.log(test.value) // 'hi'
console.log(test.data) // {whatever: 'hi'}
Remember to put the data property in the constructor. Otherwise, if you put it into the prototype, it will be a shared property among every instance.
OK, I have an answer: as suggested by #Bergi & #damianmr, I used (and had to learn about) a Proxy object.
Here is a working version:
function Test() {
this.dataStore={};
this.data=new Proxy(this,{
set(target,property,value) {
target.dataStore[property]=value;
},
get(target,property) {
return target.dataStore[property];
}
});
}
Test.prototype={
};
var test=new Test();
test.data.whatever=23;
test.data.etc=46;
alert(`${test.data.whatever} ${test.data.etc}`);
As you can see:
I have an object called dataStore, and a proxy called data
The Proxy is set in the constructor
This is a simple abstracted case, but it also works for the more complex project I’m working on.
The only shortcoming is that IE doesn’t support Proxy, and the polyfills I have seen don’t like new properties.
That just means I will need to supply a functional alternative for Legacy Browsers, and wait for them to die out …

Making primitive data types readOnly/nonConfig in JavaScript

Does anyone have any example implementation of making individual object props readOnly/non-configurable? I mean primitive data types. Have tried using ES5 Object API, but hitting a brick wall.
I can't show code, because it's still at that "messy" phase, but basically I'm iterating through an outside object which, itself, holds numeruos objects. Those objects each hold various primitive data types. I have made the outer objects readOnly, non-config, etc, but can't figure out how to do likewise for individual props, the innermost props.
So, if outer.inner.prop === "Hello", I want to make that value readOnly.
Thanks!
UPDATE
I just figured this out, it was all in the for loop I was using to iterate over props. Now I've actually get data descriptors for the props, even the primitive ones. :) Thanks all!
You have to iterate through the inner object, since there is no way to deep-freeze an object using standard ES5 methods.
function deepFreeze(obj) {
Object.keys(obj).forEach(function (key) {
if (typeof obj[key] == 'object')
deepFreeze(obj[key]);
});
Object.freeze(obj);
}
Edit:
Also works for defineProperty if you don't want to freeze:
function deepWriteProtect(obj) {
Object.keys(obj).forEach(function (key) {
if (typeof obj[key] == 'object')
deepWriteProtect(obj[key]);
Object.defineProperty(obj, key, { writable: false });
});
}
I'm not 100% sure I understand your question correctly, but from what I gather you are asking for private variables. If so, that can be easily achieved using closures.
function myClass(){
var mySecretProperty = 10;
this.getMySecretProperty = function(){
return mySecretProperty;
}
this.changeMySecretProperty = function(s){
// whatever logic you need for a setter method
mySecretProperty = s;
}
}
var myObj = new MyClass();
myObj.changeMySecretProperty(120);
myObj.getMySecretProperty(); // will return 120
myObj.mySecretProperty // will return undefined
Would the following (ES5) example help? It creates an empty constructor, with a getter for property a (and no setter, so de facto a is read only):
var Obj = function(){};
Obj.prototype = {
get a() {return 5;}
}
var x = new Obj;
alert(x.a); //=> 5
x.a = 6; //=> TypeError: setting a property that has only a getter
Not using ES5 you can do
var Obj = function(){
var a = 5;
if (!Obj.prototype.getA) {
Obj.prototype.getA = {
toString: function() {
return a;
}
};
}
}
var y = new Obj;
alert(y.getA); //=> 5
But that is not 100% failsafe: Obj.prototype.getA can be overwritten.
Here is a jsfiddle showing how you can use ES5 getter/setter definitions to make a property of an object something that can only be fetched. The code looks like this:
var object = {
get x() {
return 17;
}, set x() {
alert("You cannot set x!");
}
};
Of course the getter could obtain the value of the property ("x") from anywhere, like a closure from a constructor or something. The point is that the setter simply does not change the value, so attempts to change it:
object.x = 100;
will not have any effect.

Javascript: constant properties

In javascript, can I declare properties of an object to be constant?
Here is an example object:
var XU = {
Cc: Components.classes
};
or
function aXU()
{
this.Cc = Components.classes;
}
var XU = new aXU();
just putting "const" in front of it, doesn't work.
I know, that i could declare a function with the same name (which would be also kind of constant), but I am looking for a simpler and more readable way.
Browser-compatibility is not important. It just has to work on the Mozilla platform, as it is for a Xulrunner project.
Thank you a lot!
Cheers.
Since you only need it to work on the Mozilla platform, you can define a getter with no corresponding setter. The best way to do it is different for each of your examples.
In an object literal, there is a special syntax for it:
var XU = {
get Cc() { return Components.classes; }
};
In your second exampe, you can use the __defineGetter__ method to add it to either aXU.prototype or to this inside the constructor. Which way is better depends on whether the value is different for each instance of the object.
Edit: To help with the readability problem, you could write a function like defineConstant to hide the uglyness.
function defineConstant(obj, name, value) {
obj.__defineGetter__(name, function() { return value; });
}
Also, if you want to throw an error if you try to assign to it, you can define a setter that just throws an Error object:
function defineConstant(obj, name, value) {
obj.__defineGetter__(name, function() { return value; });
obj.__defineSetter__(name, function() {
throw new Error(name + " is a constant");
});
}
If all the instances have the same value:
function aXU() {
}
defineConstant(aXU.prototype, "Cc", Components.classes);
or, if the value depends on the object:
function aXU() {
// Cc_value could be different for each instance
var Cc_value = return Components.classes;
defineConstant(this, "Cc", Cc_value);
}
For more details, you can read the Mozilla Developer Center documentation.
UPDATE: This works!
const FIXED_VALUE = 37;
FIXED_VALUE = 43;
alert(FIXED_VALUE);//alerts "37"
Technically I think the answer is no (Until const makes it into the wild). You can provide wrappers and such, but when it all boils down to it, you can redefine/reset the variable value at any time.
The closest I think you'll get is defining a "constant" on a "class".
// Create the class
function TheClass(){
}
// Create the class constant
TheClass.THE_CONSTANT = 42;
// Create a function for TheClass to alert the constant
TheClass.prototype.alertConstant = function(){
// You can’t access it using this.THE_CONSTANT;
alert(TheClass.THE_CONSTANT);
}
// Alert the class constant from outside
alert(TheClass.THE_CONSTANT);
// Alert the class constant from inside
var theObject = new TheClass();
theObject.alertConstant();
However, the "class" TheClass itself can be redefined later on
If you are using Javascript 1.5 (in XUL for example), you can use the const keyword instead of var to declare a constant.
The problem is that it cannot be a property of an object. You can try to limit its scope by namespacing it inside a function.
(function(){
const XUL_CC = Components.classes;
// Use the constant here
})()
To define a constant property, you could set the writable attribute to false in the defineProperty method as shown below:
Code snippet:
var XU = {};
Object.defineProperty(XU, 'Cc', {
value: 5,
writable: false
});
XU.Cc = 345;
console.log(XU.Cc);
Result:
5 # The value hasn't changed

Categories

Resources