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());
Related
I'm working with a data object lit, then trying to create a new object that changes properties in the data property just for that instance, here's some test code from jsbin
data = {
innerData : 1
}
-------------
'This works :-)'
construct = function(d){
this.data = Object.create(d);
};
construct.prototype.c = function(n){
this.data.innerData = n;
};
construct.prototype.d = function(){
console.log(this.data.innerData)
};
--------------
'This does not :-{'
construct = {
data : Object.create(data),
changeData : function(n){
this.data.innerData = n;
},
showData:function(){
console.log(this.data.innerData)
}
}
--------------
newInst = Object.create(construct);
newInst.changeData(5);
newInst.showData();
newInst2 = Object.create(construct);
newInst2showData();
when I run it using the constructor/prototype functions it works and the console outputs 5,2
when I run it using the object literal the console outputs 5,5 I guess when I create the first instance it changes the actual data object and not the data property of the instance of the construct object.
If someone could explain in depth why this happens that would be much help as I've not been working with OOJS for that long
UPDATE:
so I had a little go at merging what I found useful from the answers and I've come up with this....
data = {
innerData : 1
}
function construct(d){
return {
data : Object.create(d),
changeData : function(n){
this.data.innerData = n;
},
showData : function(){
console.log(this.data.innerData)
}
}
}
build = function(){
return(new construct(data));
}
newInst = build();
newInst.changeData(5);
newInst.showData();
newInst2 = build();
newInst2.showData();
Given what we know about the inheritance, what does the following actually do
this.data.innerData = n;
where this is the result of Object.create(construct)?
this does not have own property data, so look up in the inherited properties.
Okay we found a data === Object.getPrototypeOf(this).data, call it d
Next set innerData of d to n
So what the actual result has ended up is the innerData property has been set on the reference from the prototype, and not a new Object.
Why has this happened? Because if you have o = {} and try to do o.foo.bar, you get a TypeError: Cannot read property 'bar' of undefined, and therefore for it to not throw an error, it has to be accessing a defined Object.
var proto = {foo: {bar: 'fizz'}},
obj = Object.create(proto);
obj.foo.bar = 'buzz';
proto.foo.bar; // "buzz" (i.e. NOT "fizz" anymore)
// and
obj.foo === Object.getPrototypeOf(obj).foo; // true
In your second example, there is only ever one data object. That object lives inside construct and is available to all the subsequent objects on the prototype chain.
In your first example, you make a new data object every time a new instance is created. So each object gets its own copy of data.
Try this, for the first example:
console.log(newInst.data === newInst2.data); // should be false
and for the second example:
console.log(newInst.data === newInst2.data); // should be true
The second piece of code works just fine:
construct.changeData(2);
construct.showData(); // 2
The only difference is that in the above example construct is not a constructor, so there will be only a single instance of construct.data as opposed to the first approach; calling Object.create() on it will create a new object but will keep the same .data reference as the first.
This looks like an attempt to create a factory function instead of a constructor function. Since Object.create is now widespread, and shimmable for these purposes where not available, this has become perhaps the best default, although there are plenty of constructor functions still around.
Some of the other answers explain what went wrong with your attempt. Here's how you might do it so that it works as expected:
var factory = (function() {
var clone = function(obj) {return JSON.parse(JSON.stringify(obj));};
var data = {
innerData : 1
};
var proto = {
changeData : function(n){
this.data.innerData = n;
},
showData:function(){
console.log(this.data.innerData)
}
};
return function() {
var obj = Object.create(proto);
obj.data = clone(data);
return obj;
}
}());
But it looks as though your innerData might have been an experiment to try to get these things working. It it's not necessary, this would be cleaner:
var factory = (function() {
var proto = {
data: 1,
changeData : function(n){
this.data = n;
},
showData:function(){
console.log(this.data)
}
};
return function() {
return Object.create(proto);
}
}());
Firstly and most importantly I'm trying to detect the end call of a method chain. I would also like to devise a way to detect how many methods "in" or "down" an object chain I am within my method calls in a method chain.
For instance, in the plugin I'm writing:
var result = $("#someDiv").myPlugin.foo().bar()._foo()._bar();
Say the method is currently executing in .bar() I would like to know that I'm 2 methods down the chain.
The reason I need to abstract this information in some manner is so when I reach the last method in the chain I can return a result instead of the plugin object thus breaking the chain at that point for the sake of gaining access to our data in the 'result' variable.
Here's an example pulled from your project:
var strLenA = parseInt( P.strlen('some string').data );
var strLenB = parseInt( P.strlen('another string').data );
var totalStrLen = strLenA + strLenB;
console.log(strLenA, strLenB, totalStrLen);
From this I can see why our answers aren't really adequate - and why you want to get rid of .data. Happily, your .data always returns a string, anyway. So, you can use the mystical .toString override to have your methods still return a copy of the parent method - but also allow for them to be treated like strings.
Here's an example: [with a fiddle]
var stringMagic = function() {
var chain = "",
self = this;
self.toString = function () { return chain; }; // Where the real magic happens.
self.add = function(str) {
chain += str + " ";
return self;
};
};
var magi = new stringMagic();
alert(magi.add("hello").add("world")); // Alerts, "hello world"
magi.add("and").add("thanks").add("for").add("all").add("the").add("fish");
alert(magi); // Alerts, "hello world and thanks for all the fish"
In your case, probably all you'd have to do is change .data in P to .toString and wrap it in a function.
In the future when you add support for other data types such as numbers and booleans, you can use valueOf in the same way you use toString. In fact, you should also continue to include toString when the return value is not a string for when they're treating that number as a string - like in console.log or $.fn.text. Here's the example above, but with numbers: http://jsfiddle.net/emqVe/1/
For the sake of completeness. Yet another alternative is to pass a an object that will get updated as the chain progress. That would let you access the result value whenever suits you (instead of having to add it at the end of the chain).
Instead of a syntax like this:
var result = chainableFunction.doThis().doThat().result;
You would then have:
chainableFunction.update(variableToUpdate).doThis().doThat();
var result = variableToUpdate.result;
The logic is very much the same as the solution proposed by others. Which one to use probably depends on your use cases. A possible issue with having to end the chain with .result is that nothing prevents you from using it this way:
var chainedUp = chainableFunction.doThis().doThat();
doSomething(chainedUp.result);
...
chainedUp.doOneMoreThing()
...
doSomething(chainedUp.result); // oups, .result changed!
With the variableToUpdate option, the result value is not affected by future function calls. Again, that could be desirable in some contexts, not in others.
Full example below
#!/usr/bin/env node
var ChainUp = {};
(function(Class) {
// Pure functions have no access to state and no side effects
var Pure = {};
Pure.isFunction = function(fn) {
return fn && {}.toString.call(fn) === '[object Function]';
};
Class.instance = function() {
var instance = {};
var result;
var updateRef;
function callBack(fn) {
// returning a clone, not a reference.
if(updateRef) { updateRef.r = (result || []).slice(0); }
if(Pure.isFunction(fn)) { fn(result); }
}
instance.update = function(obj) {
updateRef = obj;
return instance;
};
instance.one = function(cb) {
result = (result || []); result.push("one");
callBack(cb);
return instance;
};
instance.two = function(cb) {
result = (result || []); result.push("two");
callBack(cb);
return instance;
};
instance.three = function(cb) {
result = (result || []); result.push("three");
callBack(cb);
return instance;
};
instance.result = function() {
return result;
};
return instance;
};
}(ChainUp));
var result1 = {};
var chain = ChainUp.instance().update(result1);
var one = chain.one(console.log); // [ 'one' ]
console.log(one.result()); // [ 'one' ]
console.log(result1.r); // [ 'one' ]
var oneTwo = chain.two();
console.log(oneTwo.result()); // [ 'one', 'two' ]
console.log(result1.r); // [ 'one', 'two' ]
var result2 = {};
var oneTwoThree = chain.update(result2).three();
console.log(oneTwoThree.result()); // [ 'one', 'two', 'three' ]
console.log(result2.r); // [ 'one', 'two', 'three' ]
console.log(result1.r); // [ 'one', 'two' ]
Note. The Class and instance keywords are probably unfamiliar. That's a convention that I use when using closures instead of prototypical inheritance to construct instances from a prototype. You could replace instance with self (and self = this instead of instance = {})..
It isn't possible to determine if a call is the last instance in a chain when determining the return value, and here is why:
var result = $("#someDiv").myPlugin.foo().bar()._foo()._bar();
foo returns myPlugin on which bar is called which returns myPlugin on which _foo is called which returns myPlugin on which _bar is called.
So effectively, when _foo returns its value (myPlugin), it is before that value is utilized. Unless _foo is psychic, it can't know what will happen next.
As pointed out in your comments, your best bet is to have some "end" method, like results().
Another suggestion would be to pass a handler in to myPlugin that gets called to set the value using setTimeout(..., 0). Have a value in myPlugin that foo, bar, _foo, and _bar all set. Let's call it returnValue. Modify myPlugin to accept a method as it's only parameter. Let's call that handler. This method's first argument will contain the value. Inside of myPlugin, before your return, do:
window.setTimeout(function () {
handler(returnValue);
}, 0);
Since setTimeout's function parameter will not be called until execution is finished, it will contain the last value set for returnValue - effectively the value set by the last call in the chain. I'd consider this the closest option to what you are trying to achieve, since the developer doesn't have to worry about which of his methods are called last.
There are no (legal or easy or nice) way to find out inside a method what happens with the result outside, after it returns with it. You should use a "chain end mark" method.
Think again, are you looking for the last method applied on an object, or do you want to detect something more explicite thing? Maybe you lose a possibility to apply methods on a decision (with fake silly method names):
obj.turnLeft().pushUp().makeBig().makeSilent;
if (colorSupport) {
obj.paintRed();
} else {
obj.paintStripes();
}
obj.makeShine().lastMethodCallSoObjectIsNowInFinalState();
There is no native way, however, you can add a parameter to the method meant to be chained as you want. And determine if the current call is the latest by setting it to true, as in:
var AlertText = {
text: "",
chainEntry: function(textToAdd, isTheLastOne) {
this.text += textToAdd;
// Perform only if this is told to be the last call in the chain:
if (isTheLastOne) {
alert(this.text);
this.text = "";
}
//
return this;
}
};
AlertText
.chainEntry("Hi, ")
.chainEntry("how ")
.chainEntry("are ")
.chainEntry("you?", true); // alert("Hi, how are you?");
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));
STORE = {
item : function() {
}
};
STORE.item.prototype.add = function() { alert('test 123'); };
STORE.item.add();
I have been trying to figure out what's wrong with this quite a while. Why doesn't this work? However, it works when I use the follow:
STORE.item.prototype.add();
The prototype object is meant to be used on constructor functions, basically functions that will be called using the new operator to create new object instances.
Functions in JavaScript are first-class objects, which means you can add members to them and treat them just like ordinary objects:
var STORE = {
item : function() {
}
};
STORE.item.add = function() { alert('test 123'); };
STORE.item.add();
A typical use of the prototype object as I said before, is when you instantiate an object by calling a constructor function with the new operator, for example:
function SomeObject() {} // a constructor function
SomeObject.prototype.someMethod = function () {};
var obj = new SomeObject();
All the instances of SomeObject will inherit the members from the SomeObject.prototype, because those members will be accessed through the prototype chain.
Every function in JavaScript has a prototype object because there is no way to know which functions are intended to be used as constructors.
After many years, when JavaScript (ES2015 arrives) we have finally Object.setPrototypeOf() method
const STORE = {
item: function() {}
};
Object.setPrototypeOf(STORE.item, {
add: function() {
alert('test 123');
}
})
STORE.item.add();
You can use JSON revivers to turn your JSON into class objects at parse time. The EcmaScript 5 draft has adopted the JSON2 reviver scheme described at http://JSON.org/js.html
var myObject = JSON.parse(myJSONtext, reviver);
The optional reviver parameter is a
function that will be called for every
key and value at every level of the
final result. Each value will be
replaced by the result of the reviver
function. This can be used to reform
generic objects into instances of
pseudoclasses, or to transform date
strings into Date objects.
myData = JSON.parse(text, function (key, value) {
var type;
if (value && typeof value === 'object') {
type = value.type;
if (typeof type === 'string' && typeof window[type] === 'function') {
return new (window[type])(value);
}
}
return value;
});
As of this writing this is possible by using the __proto__ property. Just in case anyone here is checking at present and probably in the future.
const dog = {
name: 'canine',
bark: function() {
console.log('woof woof!')
}
}
const pug = {}
pug.__proto__ = dog;
pug.bark();
However, the recommended way of adding prototype in this case is using the Object.create. So the above code will be translated to:
const pug = Object.create(dog)
pug.bark();
Or you can also use Object.setPrototypeOf as mentioned in one of the answers.
Hope that helps.
STORE = {
item : function() {
}
};
this command would create a STORE object. you could check by typeof STORE;. It should return 'object'. And if you type STORE.item; it returns 'function ..'.
Since it is an ordinary object, thus if you want to change item function, you could just access its properties/method with this command.
STORE.item = function() { alert('test 123'); };
Try STORE.item; it's still should return 'function ..'.
Try STORE.item(); then alert will be shown.
I'm reading "Pro JavaScript Techniques" by John Resig, and I'm confused with an example. This is the code:
// Create a new user object that accepts an object of properties
function User( properties ) {
// Iterate through the properties of the object, and make sure
// that it's properly scoped (as discussed previously)
for ( var i in properties ) { (function(){
// Create a new getter for the property
this[ "get" + i ] = function() {
return properties[i];
};
// Create a new setter for the property
this[ "set" + i ] = function(val) {
properties[i] = val;
};
})(); }
}
// Create a new user object instance and pass in an object of
// properties to seed it with
var user = new User({
name: "Bob",
age: 44
});
// Just note that the name property does not exist, as it's private
// within the properties object
alert( user.name == null );
// However, we're able to access its value using the new getname()
// method, that was dynamically generated
alert( user.getname() == "Bob" );
// Finally, we can see that it's possible to set and get the age using
// the newly generated functions
user.setage( 22 );
alert( user.getage() == 22 );
Now running that in the Firebug console (on Firefox 3) throws that user.getname() is not a function. I tried doing this:
var other = User
other()
window.getname() // --> This works!
And it worked!
Why?
Doing:
var me = this;
seems to work a bit better, but when executing "getname()" it returns '44' (the second property)...
Also I find it strange that it worked on the window object without modification...
And a third question, what's the difference between PEZ solution and the original? (He doesn't use an anonymous function.)
I think it's best not to use the new keyword at all when working in JavaScript.
This is because if you then instantiate the object without using the new keyword (ex: var user = User()) by mistake, *very bad things will happen...*reason being that in the function (if instantiated without the new keyword), the this will refer to the global object, ie the window...
So therefore, I suggest a better way on how to use class-like objects.
Consider the following example :
var user = function (props) {
var pObject = {};
for (p in props) {
(function (pc) {
pObject['set' + pc] = function (v) {
props[pc] = v;
return pObject;
}
pObject['get' + pc] = function () {
return props[pc];
}
})(p);
}
return pObject;
}
In the above example, I am creating a new object inside of the function, and then attaching getters and setters to this newly created object.
Finally, I am returning this newly created object. Note that the the this keyword is not used anywhere
Then, to 'instantiate' a user, I would do the following:
var john = user({name : 'Andreas', age : 21});
john.getname(); //returns 'Andreas'
john.setage(19).getage(); //returns 19
The best way to avoid falling into pitfalls is by not creating them in the first place...In the above example, I am avoiding the new keyword pitfall (as i said, not using the new keyword when it's supposed to be used will cause bad things to happen) by not using new at all.
Adapting Jason's answer, it works:
We need to make a closure for the values. Here's one way:
function bindAccessors(o, property, value) {
var _value = value;
o["get" + property] = function() {
return _value;
};
o["set" + property] = function(v) {
_value = v;
};
}
Then the User constructor looks like this:
function User( properties ) {
for (var i in properties ) {
bindAccessors(this, i, properties[i]);
}
}
You probably want something like this, which is more readable (closures are easy to learn once you get some practice):
function User( properties ) {
// Helper function to create closures based on passed-in arguments:
var bindGetterSetter = function(obj, p, properties)
{
obj["get" + p] = function() { return properties[p]; }
obj["set" + p] = function(val) { properties[p]=val; return this; }
};
for (var p in properties)
bindGetterSetter(this, p, properties);
}
I also added "return this;", so you can do:
u = new User({a: 1, b:77, c:48});
u.seta(3).setb(20).setc(400)
I started this post with the sole purpose of learning why that things happened, and I finally did. So in case there's someone else interested in the "whys", here they are:
Why does 'this' changes inside the anonymous function?
A new function, even if it is an anonymous, declared inside an object or another function, always changes the scope, in this case returning to the global scope (window).
Solution: all stated in the post, I think the clearer is executing the anonymous function with .call(this).
Why does getname() always return the age?
While the anonymous function gets executed right away, the getters/setters get executed for the first time when they are called. In that moment, the value of i will always be the last, because it has already iterated for all the properties... and it will always return properties[i] which is the last value, in this case the age.
Solution: save the i value in a variable like this
for ( i in properties ) { (function(){
var j = i
// From now on, use properties[j]
As written in the OP, this in the loop is not referring to the User object as it should be. If you capture that variable outside the loop, you can make it work:
function User( properties ) {
// Iterate through the properties of the object, and make sure
// that it's properly scoped (as discussed previously)
var me = this;
for ( i in properties ) { (function(){
// Create a new getter for the property
me[ "get" + i ] = function() {
return properties[i];
};
// Create a new setter for the property
me[ "set" + i ] = function(val) {
properties[i] = val;
};
// etc
I just modified the code a bit like this.. This one should work.. This is same as setting me=this; But a closure is required to set the value of each property properly, else the last value will be assigned to all properties.
// Create a new user object that accepts an object of properties
var User = function( properties ) {
// Iterate through the properties of the object, and make sure
// that it's properly scoped (as discussed previously)
var THIS = this;
for ( var i in properties ) { (function(i){
// Create a new getter for the property
THIS[ "get" + i ] = function() {
return properties[i];
};
// Create a new setter for the property
THIS[ "set" + i ] = function(val) {
properties[i] = val;
};
})(i); }
}
// Create a new user object instance and pass in an object of
// properties to seed it with
var user = new User({
name: "Bob",
age: 44
});
// Just note that the name property does not exist, as it's private
// within the properties object
alert( user.name == null );
// However, we're able to access its value using the new getname()
// method, that was dynamically generated
alert( user.getname() == "Bob" );
// Finally, we can see that it's possible to set and get the age using
// the newly generated functions
user.setage( 22 );
alert( user.getage() == 22 );
Maybe the variable i is "closured" with the last value in the iteration ("age")? Then all getters and setters will access properties["age"].
I found something that seems to be the answer; it’s all about context. Using the anonymous function inside the for loop, changes the context, making 'this' refer to the window object. Strange isn't it?
So:
function User(properties) {
for (var i in properties) {
// Here this == User Object
(function(){
// Inside this anonymous function, this == window object
this["get" + i] = function() {
return properties[i];
};
this["set" + i] = function(val) {
properties[i] = val;
};
})();
}
}
I don't know why that function changes the context of execution, and I'm not sure it should do that. Anyway, you can test it running the code there and trying window.getname(). It magically works! :S
The solution, as stated before, is changing the context. It can be done like J Cooper said, passing the variable 'me' and making the function a closure or you can do this:
(function(){
// Inside this anonymous function this == User
// because we called it with 'call'
this[ "get" + i ] = function() {
return properties[i];
};
this["set" + i] = function(val) {
properties[i] = val;
};
}).call(this);
Anyway, I'm still getting 44 when running 'getname'... What could it be?