I have an object which has several properties that are set when the object is created.
This object recently changed to object literal notation, but I've hit a bit of a problem that searching on the net doesn't reveal.
Simply stated I need to do this:
Star = function(_id, _x, _y, _n, _o, _im, _c, _b, _links) {
var self = {
id: _id,
// other properties
links: [],
for (var i=0,j=0;i<8;i++) { //<- doesn't like this line
var k = parseInt(_links[i]);
if (k > 0) {
this.links[j++] = k;
}
},
// other methods
};
return self;
};
How do I initialise a property in the constructor in object literal notation?
You can create the array on the fly using an anonymous function:
Star = function(_id, _x, _y, _n, _o, _im, _c, _b, _links) {
var self = {
id: _id,
// other properties
links: (function() {
var a = [];
for (var i=0;i<8;i++) {
var k = parseInt(_links[i]);
if (k > 0) {
a.push(k);
}
}
return a;
})(),
// other methods
};
return self;
};
You can do it after the literal is defined:
Star = function(_id, _x, _y, _n, _o, _im, _c, _b, _links) {
var self = {
id: _id,
// other properties
links: [],
// other methods
};
for (var i=0,j=0;i<8;i++) {
var k = parseInt(_links[i]);
if (k > 0) {
self.links[j++] = k;
}
}
return self;
};
For this specific example, you could use an anonymous function, like this:
Star = function(_id, _x, _y, _n, _o, _im, _c, _b, _links) {
var self = {
id: _id,
// other properties
links: (function(){
var links = [];
for (var i=0;i<8;i++) {
var k = parseInt(_links[i]);
if (k > 0) {
links.push(k);
}
}
return links;
})(),
// other methods
};
return self;
};
You can't. Object notation doesn't allow this. However, you can add the properties AFTER initializing your object.
By the way, self is a defined object already, which is an alias for the window object. While you ARE defining a temporary object named self [and thus, you aren't getting rid of window.self] this isn't something you shold be doing. Act as if self was a keyword and your current usage of it is illegal.
Related
I am trying to simulate a simple Holder "class" in JavaScript with a "private" property that holds something and "public" getter and setter "methods" to access the value.
The approach exhibited by HolderA below is mentioned e.g. here. The other approach I more or less arrived at by mutation but I guess it must be recognizable as an idiom as well. I like it because it contains no this or prototype stuff and seems very elementary and functional. Is there a difference between the two?
The test code (I run it under nodejs) seems to suggest that the two approaches are identical except that in the first case the objects I get have typeof object whereas in the second function.
var test = function(o) {
var initialValueCorrect = (!(typeof o.getX()==='undefined'))&&(o.getX()===0);
var VALUE_TO_SET = 10;
o.setX(VALUE_TO_SET);
var getSetWorks = o.getX()===VALUE_TO_SET;
var xIsPrivate = (typeof o.x === 'undefined');
var xHasCorrectValue;
if (!xIsPrivate)
xHasCorrectValue = o.x === VALUE_TO_SET;
return {initialValueCorrect: initialValueCorrect,
getSetWorks : getSetWorks,
xIsPrivate: xIsPrivate,
xHasCorrectValue: xHasCorrectValue};
};
var HolderA = (function() {
function foo(x) {
this.getX = function() {
return x;
};
this.setX = function(_x) {
x = _x;
};
};
return foo;
})();
var createHolderB = (function() {
var x;
function foo(_x) {
x = _x;
return foo;
}
foo.getX = function() {
return x;
};
foo.setX = function(_x) {
x = _x;
};
return foo;
})();
var objects = [{object: new HolderA(0), name: "approach with constructor-invocation and 'this'"},
{object: createHolderB(0), name: "approach with normal function invocation and closed variable"}];
for (var i = 0; i<objects.length ; i++) {
var testResult = test(objects[i].object);
console.log('['+objects[i].name+']: the object is a: '+(typeof objects[i].object)
+'\n\n\t\t\t'+JSON.stringify(testResult)+'\n\n\n\n\n');
}
update
As Bergi has pointed out function createHolderB in my code above is plain wrong and only creates a singleton object. So, is not really a "constructor" function. To that end I've now created createHolderC which can be used to really create multiple objects with a hidden private property like this:
var objectC1 = createHolderC()(0);
Now, is there any material difference between HolderA and the createHolderC function or is the difference purely stylistic?
var createHolderC = function () {
return (function() {
var x;
function foo(_x) {
x = _x;
return foo;
};
foo.getX = function() {
return x;
};
foo.setX = function(_x) {
x = _x;
};
return foo;
})();
};
createHolderB does not create new holders like HolderA does. It's essentially a singleton pattern. You might also want to call it a module. Notice that createHolderB() === createHolderB.
createHolderC is still different from HolderA in that it returns function objects, not instances. You may see the differences better when you strip out the unnecessary IEFEs:
function HolderA(x) {
this.getX = function() {
return x;
};
this.setX = function(_x) {
x = _x;
};
// implicit `return this;` when called via `new`
}
function createHolderC() {
var x;
function foo(_x) {
x = _x;
return foo;
};
foo.getX = function() {
return x;
};
foo.setX = function(_x) {
x = _x;
};
return foo;
}
A typical factory would rather look like this:
function createHolderD(x) {
var foo = {};
foo.getX = function() {
return x;
};
foo.setX = function(_x) {
x = _x;
};
return foo;
}
(or even with return {getX: …, setX: …};), the only difference to HolderA is the prototypical inheritance then.
Basically both exhibits the private access behavior to the x variable. But the difference between those two are
Constructor function
var HolderA = (function() {
function foo(x) {
this.getX = function() {
return x;
};
this.setX = function(_x) {
x = _x;
};
};
return foo;
})();
This is a self executing function, where it returns Constructor foo.
That is the reason you are using new while creating this type of holder {object: new HolderA(0)
A function
var createHolderB = (function() {
var x;
function foo(_x) {
x = _x;
return foo;
}
foo.getX = function() {
return x;
};
foo.setX = function(_x) {
x = _x;
};
return foo;
})();
Even this is also a self executing function, but while execution it creates x variable and returns function foo, and foo access x by closure.
You are creating this type of holder just by normal function call object: createHolderB(0).
Both cases are really bad in practice.
If you're making closures (anonymous functions) that hold your "private variable" scope, it means you're creating those functions for every instance of the class. That costs memory and performance. I used to create classes like this, but then once I was making large number of instances and found out that it hits performance hard.
Just as in Python, make private variables private by some convention, like underscore in _name. Then you can break it for debug reasons (because variables in closure scope are not accessible).
This is how I would do it:
function MyClass(arg) {
this._privateVar = arg;
}
MyClass.prototype.getPrivateVar = function() {
return this._privateVar;
}
You can also make getters and setters:
Object.defineProperty(MyClass.prototype, "privateVar", {
get: function() {
return this._privateVar;
}
}
But don't try to push javascript into things it's not designed to, or you'll pay with performance, code readability and debugging complexity. Applies to other languages too.
I'm writing up a small game using CraftyJS. Here's what I want to write:
Crafty.scene('MainMap', function() {
this.player = Crafty.e('Player');
this.player.move(5, 5);
this.game_objects = [this.player];
isOccupied: function(x, y) {
for (var i = 0; i < this.game_objects.length; i++) {
// ...
}
}
if (!this.isOccupied(5, 5)) { ... }
// ...
}
Unfortunately, this doesn't work as expected; it's not an anonymous object, but it's a function. I have to use a different syntax, and pass in my objects, like so:
function isOccupied(x, y, game_objects) { ... }
// Same place as previous call to isOccupied
if (!isOccupied(x, y, this.gameObjects) { ... }
It's clear to me why I have to declare it as function isOccupied and not isOccupied: function (because it's inside a function, not an object) but it's not clear to me what the scope of this is. It doesn't get passed into the function.
Is it possible to somehow keep the objects within some non-global scope, and not need to pass them into isOccupied?
You could assign the parent scope to another variable, so it will be available in your closure. Like so...
Crafty.scene('MainMap', function() {
var self = this;
this.player = Crafty.e('Player');
this.player.move(5, 5);
this.game_objects = [this.player];
function isOccupied (x, y) {
for (var i = 0; i < self.game_objects.length; i++) {
// ...
}
}
}
You have a syntax error in your Crafty scene.
The colon in this part should not be there. In JavaScript, colons are only used in objects.
// Wrong
isOccupied: function(x, y) {
for (var i = 0; i < this.game_objects.length; i++) {
// ...
}
}
// Right
function isOccupied(x, y) {
// ...
}
In your function, this refers to the global object (window).
EDIT:
To fix this, use Function.prototype.bind, like so:
function isOccupied(x, y) {
// ...
}.bind(this);
After doing more research, it appears that this is a well-known issue with the this keyword. It will be fixed in ECMAscript 5.
To summarize: the this keyword gets confused when you have more than one level of nesting:
obj = {
speak: function() {
alert(this); // obj
inner = function() {
alert("inner: " + this); // window
};
}
};
The work-around is to use a scope chain, by assigning a variable to this:
obj = {
speak: function() {
alert(this); // obj
var that = this;
inner = function() {
alert("inner: " + that); // obj instead of window
};
}
};
Unfortunately for me, CraftJS has me inside a function instead of an object. To declare a sub-function, I still must specify it as function isOccupied:
Crafty.scene('MainMap', function() {
self = this; // new
this.player = Crafty.e('Player');
this.player.move(5, 5);
this.game_objects = [this.player];
function isOccupied(x, y) { // game_objects no longer passed in
for (var i = 0; i < self.game_objects.length; i++) { // uses self
// ...
}
}
if (!this.isOccupied(5, 5)) { ... }
// ...
}
I have some Javascript code spread over multiples files, yet I want to facilitate minification with an IFFE. Some methods must remain public.
Wikipedia suggests the implementation of accessors:
var counter = (function(){
var i = 0;
return {
get: function(){
return i;
},
set: function( val ){
i = val;
},
increment: function() {
return ++i;
}
};
}());
Unfortunately, declaring these from return is not easy when such functions are declared in multiple files.
Is the following a proper solution? If not, what is?
var counter = (function(){
var i = 0;
// Import js file 1...
this.get = function(){ return i; };
// Import js file 2...
this.set = function( val ){ i = val; };
// Import js file 3...
this.increment = function() { return ++i; };
return this;
}());
P.S.: I want to be able to perform the following calls:
counter.get();
counter.set(33);
counter.increment();
It seems like the pattern that you are interested in is something like this:
//file 1
var xy = (function (that) {
that.prop1 = 1;
return that;
}(xy || {}));
//file2
var xy = (function (that) {
that.prop2 = 2;
return that;
}(xy || {}));
This will result in a single global object xy that has two properties:
xy = {
prop1: 1,
prop2: 2
};
I have a class definition method for create class in javascript:
var Class = function() {
var clazz = null,
pros = {};
for (var i = 0; i < arguments.length; i++) {
var arg = arguments[i];
if (typeof arg == "function") arg = arg.prototype;
else {
arg.init && (clazz = arg.init, delete arg.init)
}
for (var p in arg) pros[p] = arg[p];
}
clazz.prototype = pros;
return clazz;
};
var Person = Class({
init: function(name) {
this.name = name;
},
say:function(){
console.info(this.name);
}
});
var Man = Class(Person, {
init: function(name) {
Person.apply(this, arguments);
this.gender = 'man'
}
});
var m = new Man('kk');
console.info(m instanceof Man);
console.info(m instanceof Person);
However, it does not support the instanceof operator.
Any idea to fix it?
You should keep track of the prototype chain to make instanceof work. What you're currently doing is merely copying properties into one object, and use that as prototype of the returned function. As a result, the information that Parent is a parent of Man is lost.
You'd need to set the prototype instead of copying. Since you need to modify an existing object to set the underlying prototype of, the deprecated __proto__ property is required. Object.create cannot be used here, because that returns a new object.
Edit: It is possible without __proto__, but you'd need the function F trick: http://jsfiddle.net/p9pvQ/.
var clazz = null,
pros = Object.prototype; // root of chain
for (var i = 0; i < arguments.length; i++) {
var arg = arguments[i];
if (typeof arg === "function") {
arg = arg.prototype;
} else {
if(arg.init) {
clazz = arg.init;
delete arg.init;
}
var o = arg;
arg = (function() { function F() {}; F.prototype = pros; return new F; })();
for(var key in o) arg[key] = o[key];
}
pros = arg;
}
m will then have the prototype chain as follows:
Class.init // m
gender: "man"
name: "kk"
__proto__: F // Man
__proto__: F // Person
say: function (){
__proto__: Object // Object.prototype
__defineGetter__: function __defineGetter__() { [native code] }
__defineSetter__: function __defineSetter__() { [native code] }
... (Object.prototype functions)
Say i have this function that dynamically creates my namespace for me when I just pass it a string, (I'm pretty sure basically what YUI JS library does):
MyObj.namespace('fn.method.name');
would result in
MyObj.fn.method.name = {}
being created - all three levels being equivalent to an empty object.
Now, what I want to do, though, is make the last level, in this case name, set to a function, but without having to redeclare the newly created object.
So instead of doing this:
function fnName() { /* some code here */ }
MyObj.namespace('fn.method.name');
MyObj.fn.method.name = new fnName();
i want to call something like:
MyObj.add('fn.method.name', fnName);
And internally, the add method would programmatically instantiate the passed in function:
MyObj.fn.method.name = new fnName()
In the way I have it implemented, I can create the namespace object and set it to an empty object, however, when I try to instantiate a passed in function and associate that namespace with the passed in function, it never gets added to the namespace. Instead, an empty object is always returned. Any ideas?
edit: Here is the namespace method. this is attached to the base object as a JSON object, so please ignore the formatting:
namespace: function (ns) {
var _ns = ns.split('.'),
i = 0, nsLen = _ns.length,
root = this;
if (_ns[0] === gNS) {
_ns.shift();
nsLen = _ns.length;
}
for (i = 0; i < nsLen; i++) {
// create a property if it doesn't exist
var newNs = _ns[i];
if (typeof root[newNs] === "undefined") {
root[newNs] = {};
}
root = root[newNs];
}
return root;
}
edit2 - removed the passed in fn argument
Were you looking for something like this:
var root = {};
function create(ns, fn) {
var nsArray = ns.split(/\./);
var currentNode = root;
while(nsArray.length > 1) {
var newNS = nsArray.shift();
if(typeof currentNode[newNS] === "undefined") {
currentNode[newNS] = {};
}
currentNode = currentNode[newNS];
}
if(fn) {
currentNode[nsArray.shift()] = fn;
}
else {
currentNode[nsArray.shift()] = {};
}
}
Then:
create("a.b.c");
console.log(root.a);
console.log(root.a.b);
console.log(root.a.b.c);
Gives:
Object { b={...}}
Object { c={...}}
Object {}
And:
create("d.e.f", function() { console.log("o hai"); });
console.log(root.d);
console.log(root.d.e);
console.log(root.d.e.f);
Gives:
Object { e={...}}
Object {}
function()
Calling the function you defined:
root.d.e.f();
Gives:
o hai
Well you haven't given the namespace function but your add function could look something like this:
MyObj.add = function (namespace, value) {
var names = namespace.split('.'), current = this, name;
while (names.length > 1) {
name = names.shift();
current[name] = {};
current = current[name];
}
current[names[0]] = value;
};
This code assigns the value given to the last part of the namespace. You could modify it to current[names[0] = new value(); if you want the object constructed by the passed in function (and you are assuming the constructor function takes no arguments).
function ns() {
var root = window;
for (var i = 0; i < arguments.length; i++) {
var arr = arguments[i].split(/\./);
for (var j = 0; j < arr.length; j++) {
var item = arr[j];
if (typeof item !== 'string') {
root = item;
}
else {
if (!root[item]) {
root[item] = {};
}
root = root[item];
}
}
root = window;
}
}
then you can create using
ns('fn.method.name');
or
ns('fn.method.name','fn.method.secondName');
and call using
fn.method.name
this function creates your namespace on 'window' so alternatively you can use
window.fn.method.name