Overriding assignment operator in JS - javascript

var myObject = {"myKey" : "myValue"}
typeof(myObject.myKey) returns `string`
myObject.myKey = "newValue"
console.log(myObject.myKey) prints newValue
This is the expected behavior. But, similar value writes do not work for document.cookie
typeof(document.cookie) returns `string`
But performing document.cookie = "value=123", appends to document.cookie string rather than set its value to value=123
So, how is assignment to document.cookie overridden?

document.cookie is a little magical, but depending on your browser constraints, you an use Object.defineProperty to define properties that have different get and set behavior.
For example:
var obj = {};
Object.defineProperty(obj, "data", {
get: function() {return this.val; },
set: function(val) { this.val = JSON.stringify(val); }
});
obj.data = {a:1}; // Set as an object...
console.log(obj.data) // but retrieve as string '{"a":1}'
For example, to do something similar to the cookie example, you could make a function like:
var mixinExtender = (function mixinExtender(target) {
var rawValue = {};
Object.defineProperty(target, "data", {
get: function() { return JSON.stringify(rawValue); },
set: function(val) {
for(var key in val) {
rawValue[key] = val[key];
}
}
});
})
This will mixin in a data property that will extend the setter value into a private object. The getter will return a serialized version of it. Then you could use it with:
var obj = {};
mixinExtender(obj);
obj.data = {a:1}; // Add "a" key
obj.data = {b:2}; // Add "b" key
console.log(obj.data) // > {"a":1,"b":2}

Browser-supplied host objects behave in ways that are not constrained by the semantics of the language. That is, document looks like a JavaScript object, but it's not. It's part of the runtime environment.
The JavaScript spec is written in terms of various internal "method" descriptions. Host objects like window and document have special versions of those internal methods. Thus, the runtime follows the spec as to how the = assignment process works, but the internal method [[Put]] is simply special.

Related

JavaScript - declaring get property in object literal [duplicate]

I've been trying to get my head around getters and setters and its not sinking in. I've read JavaScript Getters and Setters and Defining Getters and Setters and just not getting it.
Can someone clearly state:
What a getter and setter are meant to do, and
Give some VERY simple examples?
In addition to #millimoose's answer, setters can also be used to update other values.
function Name(first, last) {
this.first = first;
this.last = last;
}
Name.prototype = {
get fullName() {
return this.first + " " + this.last;
},
set fullName(name) {
var names = name.split(" ");
this.first = names[0];
this.last = names[1];
}
};
Now, you can set fullName, and first and last will be updated and vice versa.
n = new Name('Claude', 'Monet')
n.first # "Claude"
n.last # "Monet"
n.fullName # "Claude Monet"
n.fullName = "Gustav Klimt"
n.first # "Gustav"
n.last # "Klimt"
Getters and Setters in JavaScript
Overview
Getters and setters in JavaScript are used for defining computed properties, or accessors. A computed property is one that uses a function to get or set an object value. The basic theory is doing something like this:
var user = { /* ... object with getters and setters ... */ };
user.phone = '+1 (123) 456-7890'; // updates a database
console.log( user.areaCode ); // displays '123'
console.log( user.area ); // displays 'Anytown, USA'
This is useful for automatically doing things behind-the-scenes when a property is accessed, like keeping numbers in range, reformatting strings, triggering value-has-changed events, updating relational data, providing access to private properties, and more.
The examples below show the basic syntax, though they simply get and set the internal object value without doing anything special. In real-world cases you would modify the input and/or output value to suit your needs, as noted above.
get/set Keywords
ECMAScript 5 supports get and set keywords for defining computed properties. They work with all modern browsers except IE 8 and below.
var foo = {
bar : 123,
get bar(){ return bar; },
set bar( value ){ this.bar = value; }
};
foo.bar = 456;
var gaz = foo.bar;
Custom Getters and Setters
get and set aren't reserved words, so they can be overloaded to create your own custom, cross-browser computed property functions. This will work in any browser.
var foo = {
_bar : 123,
get : function( name ){ return this[ '_' + name ]; },
set : function( name, value ){ this[ '_' + name ] = value; }
};
foo.set( 'bar', 456 );
var gaz = foo.get( 'bar' );
Or for a more compact approach, a single function may be used.
var foo = {
_bar : 123,
value : function( name /*, value */ ){
if( arguments.length < 2 ){ return this[ '_' + name ]; }
this[ '_' + name ] = value;
}
};
foo.value( 'bar', 456 );
var gaz = foo.value( 'bar' );
Avoid doing something like this, which can lead to code bloat.
var foo = {
_a : 123, _b : 456, _c : 789,
getA : function(){ return this._a; },
getB : ..., getC : ..., setA : ..., setB : ..., setC : ...
};
For the above examples, the internal property names are abstracted with an underscore in order to discourage users from simply doing foo.bar vs. foo.get( 'bar' ) and getting an "uncooked" value. You can use conditional code to do different things depending on the name of the property being accessed (via the name parameter).
Object.defineProperty()
Using Object.defineProperty() is another way to add getters and setters, and can be used on objects after they're defined. It can also be used to set configurable and enumerable behaviors. This syntax also works with IE 8, but unfortunately only on DOM objects.
var foo = { _bar : 123 };
Object.defineProperty( foo, 'bar', {
get : function(){ return this._bar; },
set : function( value ){ this._bar = value; }
} );
foo.bar = 456;
var gaz = foo.bar;
__defineGetter__()
Finally, __defineGetter__() is another option. It's deprecated, but still widely used around the web and thus unlikely to disappear anytime soon. It works on all browsers except IE 10 and below. Though the other options also work well on non-IE, so this one isn't that useful.
var foo = { _bar : 123; }
foo.__defineGetter__( 'bar', function(){ return this._bar; } );
foo.__defineSetter__( 'bar', function( value ){ this._bar = value; } );
Also worth noting is that in the latter examples, the internal names must be different than the accessor names to avoid recursion (ie, foo.bar calling foo.get(bar) calling foo.bar calling foo.get(bar)...).
See Also
MDN get, set,
Object.defineProperty(), __defineGetter__(), __defineSetter__()
MSDN
IE8 Getter Support
You'd use them for instance to implement computed properties.
For example:
function Circle(radius) {
this.radius = radius;
}
Object.defineProperty(Circle.prototype, 'circumference', {
get: function() { return 2*Math.PI*this.radius; }
});
Object.defineProperty(Circle.prototype, 'area', {
get: function() { return Math.PI*this.radius*this.radius; }
});
c = new Circle(10);
console.log(c.area); // Should output 314.159
console.log(c.circumference); // Should output 62.832
(CodePen)
Sorry to resurrect an old question, but I thought I might contribute a couple of very basic examples and for-dummies explanations. None of the other answers posted thusfar illustrate syntax like the MDN guide's first example, which is about as basic as one can get.
Getter:
var settings = {
firstname: 'John',
lastname: 'Smith',
get fullname() { return this.firstname + ' ' + this.lastname; }
};
console.log(settings.fullname);
... will log John Smith, of course. A getter behaves like a variable object property, but offers the flexibility of a function to calculate its returned value on the fly. It's basically a fancy way to create a function that doesn't require () when calling.
Setter:
var address = {
set raw(what) {
var loc = what.split(/\s*;\s*/),
area = loc[1].split(/,?\s+(\w{2})\s+(?=\d{5})/);
this.street = loc[0];
this.city = area[0];
this.state = area[1];
this.zip = area[2];
}
};
address.raw = '123 Lexington Ave; New York NY 10001';
console.log(address.city);
... will log New York to the console. Like getters, setters are called with the same syntax as setting an object property's value, but are yet another fancy way to call a function without ().
See this jsfiddle for a more thorough, perhaps more practical example. Passing values into the object's setter triggers the creation or population of other object items. Specifically, in the jsfiddle example, passing an array of numbers prompts the setter to calculate mean, median, mode, and range; then sets object properties for each result.
Getters and setters really only make sense when you have private properties of classes. Since Javascript doesn't really have private class properties as you would normally think of from Object Oriented Languages, it can be hard to understand. Here is one example of a private counter object. The nice thing about this object is that the internal variable "count" cannot be accessed from outside the object.
var counter = function() {
var count = 0;
this.inc = function() {
count++;
};
this.getCount = function() {
return count;
};
};
var i = new Counter();
i.inc();
i.inc();
// writes "2" to the document
document.write( i.getCount());
If you are still confused, take a look at Crockford's article on Private Members in Javascript.
I think the first article you link to states it pretty clearly:
The obvious advantage to writing JavaScript in this manner is that you can use it obscure values that you don't want the user to directly access.
The goal here is to encapsulate and abstract away the fields by only allowing access to them thru a get() or set() method. This way, you can store the field/data internally in whichever way you want, but outside components are only away of your published interface. This allows you to make internal changes without changing external interfaces, to do some validation or error-checking within the set() method, etc.
Although often we are used to seeing objects with public properties without any access
control, JavaScript allows us to accurately describe properties. In fact, we can use
descriptors in order to control how a property can be accessed and which logic we can
apply to it. Consider the following example:
var employee = {
first: "Boris",
last: "Sergeev",
get fullName() {
return this.first + " " + this.last;
},
set fullName(value) {
var parts = value.toString().split(" ");
this.first = parts[0] || "";
this.last = parts[1] || "";
},
email: "boris.sergeev#example.com"
};
The final result:
console.log(employee.fullName); //Boris Sergeev
employee.fullName = "Alex Makarenko";
console.log(employee.first);//Alex
console.log(employee.last);//Makarenko
console.log(employee.fullName);//Alex Makarenko
You can define instance method for js class, via prototype of the constructor.
Following is the sample code:
// BaseClass
var BaseClass = function(name) {
// instance property
this.name = name;
};
// instance method
BaseClass.prototype.getName = function() {
return this.name;
};
BaseClass.prototype.setName = function(name) {
return this.name = name;
};
// test - start
function test() {
var b1 = new BaseClass("b1");
var b2 = new BaseClass("b2");
console.log(b1.getName());
console.log(b2.getName());
b1.setName("b1_new");
console.log(b1.getName());
console.log(b2.getName());
}
test();
// test - end
And, this should work for any browser, you can also simply use nodejs to run this code.
If you're referring to the concept of accessors, then the simple goal is to hide the underlying storage from arbitrary manipulation. The most extreme mechanism for this is
function Foo(someValue) {
this.getValue = function() { return someValue; }
return this;
}
var myFoo = new Foo(5);
/* We can read someValue through getValue(), but there is no mechanism
* to modify it -- hurrah, we have achieved encapsulation!
*/
myFoo.getValue();
If you're referring to the actual JS getter/setter feature, eg. defineGetter/defineSetter, or { get Foo() { /* code */ } }, then it's worth noting that in most modern engines subsequent usage of those properties will be much much slower than it would otherwise be. eg. compare performance of
var a = { getValue: function(){ return 5; }; }
for (var i = 0; i < 100000; i++)
a.getValue();
vs.
var a = { get value(){ return 5; }; }
for (var i = 0; i < 100000; i++)
a.value;
What's so confusing about it... getters are functions that are called when you get a property, setters, when you set it.
example, if you do
obj.prop = "abc";
You're setting the property prop, if you're using getters/setters, then the setter function will be called, with "abc" as an argument.
The setter function definition inside the object would ideally look something like this:
set prop(var) {
// do stuff with var...
}
I'm not sure how well that is implemented across browsers. It seems Firefox also has an alternative syntax, with double-underscored special ("magic") methods. As usual Internet Explorer does not support any of this.
I was also somewhat confused by the explanation I read, because I was trying to add a property to an existing prototype that I did not write, so replacing the prototype seemed like the wrong approach. So, for posterity, here's how I added a last property to Array:
Object.defineProperty(Array.prototype, "last", {
get: function() { return this[this.length - 1] }
});
Ever so slightly nicer than adding a function IMHO.
You can also use __defineGetter__:
function Vector2(x,y) {
this.x = x;
this.y = y;
}
Vector2.prototype.__defineGetter__("magnitude", function () {
return Math.sqrt(this.x*this.x+this.y*this.y);
});
console.log(new Vector2(1,1).magnitude)
Or, if you prefer:
function Vector2(x,y) {
this.x = x;
this.y = y;
this.__defineGetter__("magnitude", function () {
return Math.sqrt(this.x*this.x+this.y*this.y);
});
}
console.log(new Vector2(1,1).magnitude)
But this function has been flagged as "legacy" recently, being dropped in favor of Object.defineProperty().
There's no example here with ES6 class (which is not even 'new' now, it's the norm):
class Student {
contructor(firstName, lastName){
this.firstName = firstName
this.lastName = lastName
this.secretId = Math.random()
}
get fullName() {
return `${this.firstName} ${this.lastName}`; // this is backtick in js, u can check it out here: https://stackoverflow.com/a/27678299/12056841
}
set firstName(newFirstName) {
// validate that newFirstName is a string (and maybe limit length)
this.firstName = newFirstName
}
get studentId() { return this.secretId }
}
and no setter for secretId because we don't want anyone to change it.
** if secretId shouldn't be changed at all, a nice approach is to declare it as 'private' to this class by adding a '#' to it
(e.g: this.#secretId = Math.random(), and return this.#secretId
Update: about backing fields
You might need to rename your field - or your setter function but it makes more sense to me to change your field name. One option is like I mentioned above (using a # for declaring the field as 'private'). Another way is to just rename it (_firstName, firstName_...)
I've got one for you guys that might be a little ugly, but it does get'er done across platforms
function myFunc () {
var _myAttribute = "default";
this.myAttribute = function() {
if (arguments.length > 0) _myAttribute = arguments[0];
return _myAttribute;
}
}
this way, when you call
var test = new myFunc();
test.myAttribute(); //-> "default"
test.myAttribute("ok"); //-> "ok"
test.myAttribute(); //-> "ok"
If you really want to spice things up.. you can insert a typeof check:
if (arguments.length > 0 && typeof arguments[0] == "boolean") _myAttribute = arguments[0];
if (arguments.length > 0 && typeof arguments[0] == "number") _myAttribute = arguments[0];
if (arguments.length > 0 && typeof arguments[0] == "string") _myAttribute = arguments[0];
or go even crazier with the advanced typeof check: type.of() code at codingforums.com

Unsettable & Unwritable properties are still mutable

I am trying to create a property within a constructor function which is immutable except through a prototype function. I am trying to go off MDN documentation of this: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperties. But there does not seem to be a way to make a property completely immutable. Consider a simple example:
function Test(){
Object.defineProperties(this,{
elems : { value : [] }
})
}
Test.prototype.addElem = function(newElem){
if (this.elems.indexOf(newElem) == -1){
this.elems.push(newElem);
}
};
which works fine in most cases (not assignable):
>a = new Test()
Object { , 1 moreā€¦ }
>a.elems
Array [ ]
>a.elems = 10
10
>a.elems
Array [ ]
Unfortunately, it is still mutable. Consider:
>a.elems.push(10)
1
>a.elems
Array [ 10 ]
I am sure they are other functions (array or object methods?) that will change the value of a non-writeable & non-settable property. Push was just the one I ran into. Is there a way to accomplish this? I know that one possible solution is :
function Test(){
var elems = [];
this.addElem = function(newElem){
if (elems.indexOf(newElem) == -1){
elems.push(newElem);
}
}
}
But I have read this is memory-inefficient especially when there are many instances of the "class". Also, what I am working on may have many methods like this, so I am even more worried about memory considerations.
Any ideas? I am not super knowledgeable about all the intricacies of JS prototyping.
In JavaScript, objects are extensible by default, but if you're able to take advantage of ES5, you should be able to use the Object.seal() or Object.freeze() methods to get immutable properties.
The MDN docs for Object.freeze() have an example that shows how to recursively freeze ("deepFreeze") all of the properties of an object, effectively making it completely immutable.
Here's a proof of concept that combines the code in the question with the code from the docs:
function Test() {
Object.defineProperties(this, {
elems : { value : [] }
})
}
Test.prototype.addElem = function(newElem) {
if (this.elems.indexOf(newElem) == -1) {
this.elems.push(newElem);
}
};
function deepFreeze(obj) {
// Retrieve the property names defined on obj
var propNames = Object.getOwnPropertyNames(obj);
// Freeze properties before freezing self
propNames.forEach(function(name) {
var prop = obj[name];
// Freeze prop if it is an object
if (typeof prop == 'object' && prop !== null)
deepFreeze(prop);
});
// Freeze self (no-op if already frozen)
return Object.freeze(obj);
}
a = new Test();
a.elems.push(1);
console.log(a.elems); // [1]
deepFreeze(a);
a.elems.push(2);
console.log(a.elems); // Error
In FireBug, the a.elems.push() after the object is "deep frozen" returns a TypeError exception, indicating the property is not writable;
TypeError: can't define array index property past the end of an array
with non-writable length
The Safari inspector also returns a TypeError exception:
TypeError: Attempted to assign to readonly property.
You can largely accomplish this with the help of a closure. This is how you achieve privacy in JavaScript.
In a nutshell you create a variable inside of a function and have that function return an object that contains setters/getters.
In my example the foo function contains a _foo variable that can only be set by the methods in the object returned from function foo. You are effectively creating an API to the var held withing the function foo's scope.
var foo = function(config){
if (!config) {
config = {};
}
//enclosed variable
var _foo = {
bar: []
};
if (config.bar) {//All item to be initialized with data
_foo.bar = config.bar;
}
var fooAPI = {
addBarItem: function(val){
_foo.bar.push(val);
return _foo.bar.length - 1;//return idenx of item added
},
removeBarItem: function(index) {
return _foo.bar.slice(index, 1);//return the removed item
},
getBarItem: function(index) {
return _foo.bar[index];//return the removed item
},
emptyBarItems: function() {
return _foo.bar.slice(0, _foo.bar.length);//return all removed
},
getBarItems: function(){
//clone bar do not return reference to it in order to keep private
var newBar = [];
_foo.bar.forEach(function(item){
newBar.push(item);
});
return newBar;
}
};
return fooAPI;
};
var myFoo = new foo({bar: ['alpha', 'beta', 'gamma']});
console.log(myFoo.getBarItems());

why to use accessor ( getters and setters)?

var obj = {
get foo(){//Why can't I use argument ^!^
return 'getter';
},
set foo(value){
console.log('setter: '+value);
}
}
> obj.foo = 'bla'
setter: bla
>obj.foo
'getter'
So, I wanted to get the value of foo when it is set:
>obj.foo = 'bla'
setter: bla
>obj.foo
getter: bla
Using like this:
get foo(value){//but alas, I can't use any argument in getter
return 'getter: '+value;
}
Can we not get the value what we set after? I think I'm not understanding the usage of accessor, why do we use it specially for?
may be this would explain it more to you
var obj = {
get foo(){
console.log('getter');
return this._foo;},
set foo(v){
console.log('setter');
this._foo = v;
}
}
while getting a value from the object passing a parameter wont mean anything.
while setting a value passing a parameter would mean and represent the value that needs to be set.
Chrome console. AFter initializing the object
obj.foo = "UI"
setter
"UI"
obj.foo
getter
"UI"
The getter is there simply to apply additional logic during all assignments to that property. As a getter, it doesn't make sense for it to receive a parameter, since there's no opportunity to pass one during the get operation.
If you want the getter and setter to use the current value of the property, what you need to do is store it somewhere that it is accessible to both getter and setter.
This could be on the same object, on a different object, or via a closure referenced variable.
var obj = {
get foo(){
return this.___foo___
},
set foo(value){
this.___foo___ = value;
}
}
So now the get and set are just a controlled access to the ___foo___ property.
You need setters/getters, when you need implement some non-standart logic for property.
For example, next object accumulate all assignments:
var obj = {
_a:0,
get a(){ return this._a;},
set a(value){this._a+=value;}
}
obj.a = 5;
obj.a = 7;
obj.a
12
Also this useful, when you want to write some proxy object,
for example getter resolves data from remote server, and setter post your data to it.
If you need simple property, just use
{a:0}
instead of
{_a:0,get a(){return this._a;},set a(v){this._a=v;}}
Other snippet, with js 'privates':
function Obj(key) {
var private = 0;
var authorized = false;
return {
set token(v) {authorized = (v===key);},
set data(v) {if(authorized) private = v;}
get data() {return authorized?private:undefined;}
}
}
obj = new Obj('pass');
obj.data = 5; // no effect
obj.token = 'pass';
obj.data = 'Data'; //Now works!
///...

Javascript equivalent function to PHP __call [duplicate]

I am aware of how to create getters and setters for properties whose names one already knows, by doing something like this:
// A trivial example:
function MyObject(val){
this.count = 0;
this.value = val;
}
MyObject.prototype = {
get value(){
return this.count < 2 ? "Go away" : this._value;
},
set value(val){
this._value = val + (++this.count);
}
};
var a = new MyObject('foo');
alert(a.value); // --> "Go away"
a.value = 'bar';
alert(a.value); // --> "bar2"
Now, my question is, is it possible to define sort of catch-all getters and setters like these? I.e., create getters and setters for any property name which isn't already defined.
The concept is possible in PHP using the __get() and __set() magic methods (see the PHP documentation for information on these), so I'm really asking is there a JavaScript equivalent to these?
Needless to say, I'd ideally like a solution that is cross-browser compatible.
This changed as of the ES2015 (aka "ES6") specification: JavaScript now has proxies. Proxies let you create objects that are true proxies for (facades on) other objects. Here's a simple example that turns any property values that are strings to all caps on retrieval, and returns "missing" instead of undefined for a property that doesn't exist:
"use strict";
if (typeof Proxy == "undefined") {
throw new Error("This browser doesn't support Proxy");
}
let original = {
example: "value",
};
let proxy = new Proxy(original, {
get(target, name, receiver) {
if (Reflect.has(target, name)) {
let rv = Reflect.get(target, name, receiver);
if (typeof rv === "string") {
rv = rv.toUpperCase();
}
return rv;
}
return "missing";
}
});
console.log(`original.example = ${original.example}`); // "original.example = value"
console.log(`proxy.example = ${proxy.example}`); // "proxy.example = VALUE"
console.log(`proxy.unknown = ${proxy.unknown}`); // "proxy.unknown = missing"
original.example = "updated";
console.log(`original.example = ${original.example}`); // "original.example = updated"
console.log(`proxy.example = ${proxy.example}`); // "proxy.example = UPDATED"
Operations you don't override have their default behavior. In the above, all we override is get, but there's a whole list of operations you can hook into.
In the get handler function's arguments list:
target is the object being proxied (original, in our case).
name is (of course) the name of the property being retrieved, which is usually a string but could also be a Symbol.
receiver is the object that should be used as this in the getter function if the property is an accessor rather than a data property. In the normal case this is the proxy or something that inherits from it, but it can be anything since the trap may be triggered by Reflect.get.
This lets you create an object with the catch-all getter and setter feature you want:
"use strict";
if (typeof Proxy == "undefined") {
throw new Error("This browser doesn't support Proxy");
}
let obj = new Proxy({}, {
get(target, name, receiver) {
if (!Reflect.has(target, name)) {
console.log("Getting non-existent property '" + name + "'");
return undefined;
}
return Reflect.get(target, name, receiver);
},
set(target, name, value, receiver) {
if (!Reflect.has(target, name)) {
console.log(`Setting non-existent property '${name}', initial value: ${value}`);
}
return Reflect.set(target, name, value, receiver);
}
});
console.log(`[before] obj.example = ${obj.example}`);
obj.example = "value";
console.log(`[after] obj.example = ${obj.example}`);
The output of the above is:
Getting non-existent property 'example'
[before] obj.example = undefined
Setting non-existent property 'example', initial value: value
[after] obj.example = value
Note how we get the "non-existent" message when we try to retrieve example when it doesn't yet exist, and again when we create it, but not after that.
Answer from 2011 (obsoleted by the above, still relevant to environments limited to ES5 features like Internet Explorer):
No, JavaScript doesn't have a catch-all property feature. The accessor syntax you're using is covered in Section 11.1.5 of the spec, and doesn't offer any wildcard or something like that.
You could, of course, implement a function to do it, but I'm guessing you probably don't want to use f = obj.prop("example"); rather than f = obj.example; and obj.prop("example", value); rather than obj.example = value; (which would be necessary for the function to handle unknown properties).
FWIW, the getter function (I didn't bother with setter logic) would look something like this:
MyObject.prototype.prop = function(propName) {
if (propName in this) {
// This object or its prototype already has this property,
// return the existing value.
return this[propName];
}
// ...Catch-all, deal with undefined property here...
};
But again, I can't imagine you'd really want to do that, because of how it changes how you use the object.
Preface:
T.J. Crowder's answer mentions a Proxy, which will be needed for a catch-all getter/setter for properties which don't exist, as the OP was asking for. Depending on what behavior is actually wanted with dynamic getters/setters, a Proxy may not actually be necessary though; or, potentially, you may want to use a combination of a Proxy with what I'll show you below.
(P.S. I have experimented with Proxy thoroughly in Firefox on Linux recently and have found it to be very capable, but also somewhat confusing/difficult to work with and get right. More importantly, I have also found it to be quite slow (at least in relation to how optimized JavaScript tends to be nowadays) - I'm talking in the realm of deca-multiples slower.)
To implement dynamically created getters and setters specifically, you can use Object.defineProperty() or Object.defineProperties(). This is also quite fast.
The gist is that you can define a getter and/or setter on an object like so:
let obj = {};
let val = 0;
Object.defineProperty(obj, 'prop', { //<- This object is called a "property descriptor".
//Alternatively, use: `get() {}`
get: function() {
return val;
},
//Alternatively, use: `set(newValue) {}`
set: function(newValue) {
val = newValue;
}
});
//Calls the getter function.
console.log(obj.prop);
let copy = obj.prop;
//Etc.
//Calls the setter function.
obj.prop = 10;
++obj.prop;
//Etc.
Several things to note here:
You cannot use the value property in the property descriptor (not shown above) simultaneously with get and/or set; from the docs:
Property descriptors present in objects come in two main flavors: data descriptors and accessor descriptors. A data descriptor is a property that has a value, which may or may not be writable. An accessor descriptor is a property described by a getter-setter pair of functions. A descriptor must be one of these two flavors; it cannot be both.
Thus, you'll note that I created a val property outside of the Object.defineProperty() call/property descriptor. This is standard behavior.
As per the error here, don't set writable to true in the property descriptor if you use get or set.
You might want to consider setting configurable and enumerable, however, depending on what you're after; from the docs:
configurable
true if and only if the type of this property descriptor may be changed and if the property may be deleted from the corresponding object.
Defaults to false.
enumerable
true if and only if this property shows up during enumeration of the properties on the corresponding object.
Defaults to false.
On this note, these may also be of interest:
Object.getOwnPropertyNames(obj): gets all properties of an object, even non-enumerable ones (AFAIK this is the only way to do so!).
Object.getOwnPropertyDescriptor(obj, prop): gets the property descriptor of an object, the object that was passed to Object.defineProperty() above.
obj.propertyIsEnumerable(prop);: for an individual property on a specific object instance, call this function on the object instance to determine whether the specific property is enumerable or not.
The following could be an original approach to this problem:
var obj = {
emptyValue: null,
get: function(prop){
if(typeof this[prop] == "undefined")
return this.emptyValue;
else
return this[prop];
},
set: function(prop,value){
this[prop] = value;
}
}
In order to use it the properties should be passed as strings.
So here is an example of how it works:
//To set a property
obj.set('myProperty','myValue');
//To get a property
var myVar = obj.get('myProperty');
Edit:
An improved, more object-oriented approach based on what I proposed is the following:
function MyObject() {
var emptyValue = null;
var obj = {};
this.get = function(prop){
return (typeof obj[prop] == "undefined") ? emptyValue : obj[prop];
};
this.set = function(prop,value){
obj[prop] = value;
};
}
var newObj = new MyObject();
newObj.set('myProperty','MyValue');
alert(newObj.get('myProperty'));
You can see it working here.
I was looking for something and I figured out on my own.
/*
This function takes an object and converts to a proxy object.
It also takes care of proxying nested objectsa and array.
*/
let getProxy = (original) => {
return new Proxy(original, {
get(target, name, receiver) {
let rv = Reflect.get(target, name, receiver);
return rv;
},
set(target, name, value, receiver) {
// Proxies new objects
if(typeof value === "object"){
value = getProxy(value);
}
return Reflect.set(target, name, value, receiver);
}
})
}
let first = {};
let proxy = getProxy(first);
/*
Here are the tests
*/
proxy.name={} // object
proxy.name.first={} // nested object
proxy.name.first.names=[] // nested array
proxy.name.first.names[0]={first:"vetri"} // nested array with an object
/*
Here are the serialised values
*/
console.log(JSON.stringify(first)) // {"name":{"first":{"names":[{"first":"vetri"}]}}}
console.log(JSON.stringify(proxy)) // {"name":{"first":{"names":[{"first":"vetri"}]}}}
var x={}
var propName = 'value'
var get = Function("return this['" + propName + "']")
var set = Function("newValue", "this['" + propName + "'] = newValue")
var handler = { 'get': get, 'set': set, enumerable: true, configurable: true }
Object.defineProperty(x, propName, handler)
this works for me

Is it possible to create some property in JavaScript that contain some methods?

Please look at my required JavaScript.
var someVariable = new SomeDataType();
// I can directly access value of its property.
someVariable.someProperty = "test";
alert(someVariable.someProperty); // <- this command must should "test"
// However, I have some methods in above property
// I want to validate all rule in this property.
someVariable.someProperty.isValid(); // <- this method will return true/false
Is it possible for doing this in current version of JavaScript?
UPDATE
Please look as my answer!
Yes, you can assign Javascript functions as properties like this:
someVariable.someProperty = function (arg1, arg2) {
// function code goes here
};
This is the method using function literals.
Another method is to use function instances like this:
someVariable.someProperty = new Function (arg1, arg2, code);
Note that in the second method, the code goes in as the last parameter and the Function keyword has a capitalized 'F' as against method 1 where the 'f' is small.
Further, creating a function instance inside a loop etc. will create an entire new instance to assign which is inefficient in memory. This problem does not arise while using the function literal method.
You can't (and probably shouldn't) treat objects like that in JavaScript. As someone else mentioned, you can override the toString() method to get half of the functionality (the read portion), but you cannot use the assignment operator on an object like that without overwriting the object.
You should choose a different approach, like using nested objects (as CMS suggested).
Its possible, but with the below change in your code
function SomeDataType(){
var localProperty="";
this.someProperty = function(txt){
if (arguments.length==0)
return localProperty;
else
localProperty=txt;
}
this.someProperty.isValid = function(){
return (localProperty!="") ? true : false;
};
}
instead of defining someProperty as a property, define this as function which sets value to the local property if any value is passed or it ll return that property value if no argument is given.
var someVariable = new SomeDataType();
someVariable.someProperty("test");
alert(someVariable.someProperty());
var isValid = someVariable.someProperty.isValid();
this is how you need to access the SomeDataType object.
someVariable.someProperty = [ test, anotherFunc, yetAnotherFunc];
someVariable.somePropertyAllValid= function() {
for(var prop in someVariable.someProperty) {
if(!prop()) return false;
}
return true;
};
someVariable.somePropertyAllValid();
I just found the answer. It's very simple & clean.
function createProperty(value, defaultValue, ruleCollection)
{
this.value = value;
this.defaultValue = defaultValue;
this.ruleCollection = ruleCollection;
}
createProperty.prototype.toString = function()
{
return this.value;
};
var someVariable =
{
someProperty: new createProperty
(
'currentValue',
'defaultValue',
null
)
};
For testing, you can use something like my following code.
var test = ">>" + someVariable.someProperty + "<<";
// this alert must shows ">> currentValue <<"
alert(test);
someVariable =
{
someProperty: new createProperty
(
7,
5,
null
)
};
test = someVariable.someProperty + 3;
// This alert must shows "10"
alert(test);
I just test it on FF 3.5 & IE 8. It works fine!
Update
Oops! I forget it. Because this technique returns object reference for this property. So, it's impossible to directly set property data. It isn't my final answer.
Perhaps this would be of help:
var SomeVar = {
someProperty : {
value : 7,
add : function (val) {
this.value += parseInt(val, 10);
return this;
},
toString : function () {
return this.value;
}
}
}
alert(SomeVar.someProperty.add(3));

Categories