Square brackets after object creation - javascript

I have a problem with the following code:
// At the beginning
var prog = {}
// some attributes of prog and other methods
// ...
prog.stateChange = function(state)
{
var functionOfState =
{
onState1: function()
{
// some code
},
onState2: function()
{
// some code
}
}['on'+state]()
}
Which purpose have these square brackets after the creation of the object functionOfState? Is this an array of possible methods?
Sorry, I'm a total newbie in JS and I haven't found any information about this.
I really appreciate any help.

This code does almost the same as:
var functionOfState =
{
onState1: function()
{
// some code
},
onState2: function()
{
// some code
}
}
functionOfState['on'+state]();
It simply creates an object which stores different functions. Then, it calls one of them according to the current value of state.
Maybe, this one will be even easier:
var functionOfState = {};
functionOfState['onState1'] = function() {
// someCode
};
functionOfState['onState2'] = function() {
// someCode
};
functionOfState['on'+state](); // when state is 'State2', 'onState2' will be called
The difference that in your code it doesn't store this object with functions, but calls it directly.

This is (not the most clear) way to extract field from an object.
in JS, the subscript operator ([]) can extract a property out of an object just like the dot operator (.), so the following expressions are equal:
var obj = { field : value };
obj.field == obj["field"]; //returns true
on your example an object with the fields onState1, onState2 is created. then, using the subscript operator the correct property is extract. it is equivilant on writing
prog.stateChange = function(state)
{
var temp =
{
onState1: function()
{
// some code
},
onState2: function()
{
// some code
}
};
var functionOfState = state == onState1 ? temp.onState1 : temp.onState2;
functionOfState();
}
This is not a legit way to extract a property/method out of an object. basically if someone changes the name of the method, the code breaks. it is much better to simply use a switch case.

Related

how to add a new key value to the same key in an object in JavaScript based on a condition?

I have done it using ...qqParam['queryParams'] but is this the right approach?
const qqParam = {};
if (view) {
qqParam['queryParams'] = { view: view };
}
if (productNumber) {
qqParam['queryParams'] = { ...qqParam['queryParams'], mode: 'productNumber' };
}
I think your approach is correct, just a couple things that can simplify your code while keeping it readable:
if you know you'll always need queryParam attribute, you can call it like this: qqParam.queryParam without the [], if the key of the attribute is dynamic then you're doing it ok by passing it as a variable qqParam[variable].
you're in both ifs modifying the same value so you might consider doing that on one statement:
qqParam.queryParams = {
...(view && {view}),
...(productNumber && {mode:'productMode' })
};
Your sample code works but you can simplify it.
You don't need to use the square brackets when assigning a property to the object unless it contains a symbol, a special character or a computed property name.
const qqParam = {};
if(view) {
qqParam.queryParams = { view };
}
if(productNumber) {
qqParam.queryParams = { ...qqParam.queryParams, mode: 'productNumber' };
}
Your approach is fine. one improve that you can do is not to use : {'view' : view}.
you can just use {view} as the default key will be the value name when you not specifing one.
Also, i guess that 'productNumber' should be the variable productNumber and not the string 'productNumber'.
const qqParam = {};
if (view) {
qqParam['queryParams'] = { view };
}
if (productNumber) {
qqParam['queryParams'] = { ...qqParam['queryParams'], mode: productNumber };
}

Store state of a JavaScript Object

Im trying to store the stats of 'this' in my javscript object so that later on in my application I can return 'this' to a previous state. I thought I could accomplish using a closure but so far I haven't successful. My idea was to do something like this
function SavedFeature() {
var self = this;
this.savedItem;
this.storeState = function() {
this.savedItem = storeClosure();
}
function storeClosure() {
var closure = self;
return function() {
return closure;
};
};
//other things the user can change...
}
so later on in my application if I needed to return to the point when I called storeState I could just do
//return the object I put in my closure
var backToNormal = savedFeature.savedItem();
that doesn't work though because any changes to my savedFeature object after I call storeState() are being reflected in the item im retrieving from called savedItem(). I'm guessing this is happening because closure is being set to a reference of self instead of copied to a new instance.
Is there anyway to store the state of my entire object in a closure like this or do I need to store this some other way.
The issue you are running into is that in js objects are passed by reference. This means that all changes performed on your object will apply to your obj.savedItem property.
Fix: Store a deep clone into obj.savedItem
this.storeState = function() {
this.savedItem = _.cloneDeep(this); // or _.clone(this, true);
}
cloneDeep is a lodash method, most js libs supply one of their own, e.g. jQuery's $.extend, etc.
You could easily roll your own deep clone function, look up the options on this thread.
A complete example with jQuery:
function SavedFeature() {
this.savedItem;
this.clone = function() {
return $.extend(true, {}, this);
},
this.storeState = function() {
this.savedItem = this.clone();
}
}
Doing it this way allows you adapt to different environments by changing your clone method as it is facading the used library method.
There are dozens of ways how to implement it. I will do just simple one. saving property.
Take into account if you want to save entire object you need to do deep copy of the object.
this is your feature:
function SavedFeature() {
this.savedItem = {'isNew': true};
this.stateMachine = new StateMachine();
}
this is some kind of state machine:
function StateMachine () {
var state = { 'isNew' : null};
function set(newState) {
state.isNew = newState.isNew;
}
function get() {
return state.isNew;
}
return {
get : get,
set : set
};
}
which, know how to store isNew property
and a working sample:
var savedFeature = new SavedFeature();
console.log(savedFeature.savedItem); // true by default
savedFeature.stateMachine.set(savedFeature.savedItem); // saving state.
savedFeature.savedItem.isNew = false; // modifying state
console.log(savedFeature.savedItem); // return false, because of statement above
var restoredState = savedFeature.stateMachine.get(); // restoring state
console.log(restoredState); // true
savedFeature.savedItem.isNew = restoredState.isNew;
console.log(savedFeature.savedItem); // true
you can adjust that code, and reach functionality whatever you need. hope that helps

How can I make Ember.js handlebars #each iterate over objects?

I'm trying to make the {{#each}} helper to iterate over an object, like in vanilla handlebars. Unfortunately if I use #each on an object, Ember.js version gives me this error:
Assertion failed: The value that #each loops over must be an Array. You passed [object Object]
I wrote this helper in attempt to remedy this:
Ember.Handlebars.helper('every', function (context, options) {
var oArray = [];
for (var k in context) {
oArray.push({
key : k,
value : context[k]
})
}
return Ember.Handlebars.helpers.each(oArray, options);
});
Now, when I attempt to use {{#every}}, I get the following error:
Assertion failed: registerBoundHelper-generated helpers do not support use with Handlebars blocks.
This seems like a basic feature, and I know I'm probably missing something obvious. Can anyone help?
Edit:
Here's a fiddle: http://jsfiddle.net/CbV8X/
Use {{each-in}} helper. You can use it like like {{each}} helper.
Example:
{{#each-in modelWhichIsObject as |key value|}}
`{{key}}`:`{{value}}`
{{/each-in}}
JS Bin demo.
After fiddling with it for a few hours, I came up with this hacky way:
Ember.Handlebars.registerHelper('every', function(context, options) {
var oArray = [], actualData = this.get(context);
for (var k in actualData) {
oArray.push({
key: k,
value: actualData[k]
})
}
this.set(context, oArray);
return Ember.Handlebars.helpers.each.apply(this,
Array.prototype.slice.call(arguments));
});
I don't know what repercussions this.set has, but this seems to work!
Here's a fiddle: http://jsfiddle.net/CbV8X/1/
I've been after similar functionality, and since we're sharing our hacky ways, here's my fiddle for the impatient: http://jsfiddle.net/L6axcob8/1/
This fiddle is based on the one provided by #lxe, with updates by #Kingpin2k, and then myself.
Ember: 1.9.1, Handlebars: 2.0.0, jQuery 2.1.3
Here we are adding a helper called every which can iterate over objects and arrays.
For example this model:
model: function() {
return {
properties: {
foo: 'bar',
zoo: 'zar'
}
};
}
can be iterated with the following handlebars template:
<ul class="properties">
{{#every p in properties}}
<li>{{p.key}} : {{p.value}}</li>
{{/every}}
</ul>
every helper works by creating an array from the objects keys, and then coordinating changes to Ember by way of an ArrayController. Yeah, hacky. This does however, let us add/remove properties to/from an object provided that object supports observation of the [] property.
In my use case I have an Ember.Object derived class which notifies [] when properties are added/removed. I'd recommend looking at Ember.Set for this functionality, although I see that Set been recently deprecated. As this is slightly out of this questions scope I'll leave it as an exercise for the reader. Here's a tip: setUnknownProperty
To be notified of property changes we wrap non-object values in what I've called a DataValueObserver which sets up (currently one way) bindings. These bindings provide a bridge between the values held by our internal ArrayController and the object we are observing.
When dealing with objects; we wrap those in ObjectProxy's so that we can introduce a 'key' member without the need to modify the object itself. Why yes, this does imply that you could use #every recursively. Another exercise for the reader ;-)
I'd recommend having your model be based around Ember.Object to be consistent with the rest of Ember, allowing you to manipulate your model via its get & set handlers. Alternatively, as demonstrated in the fiddle, you can use Em.Get/Em.set to access models, as long as you are consistent in doing so. If you touch your model directly (no get/set), then every won't be notified of your change.
Em.set(model.properties, 'foo', 'asdfsdf');
For completeness here's my every helper:
var DataValueObserver = Ember.Object.extend({
init: function() {
this._super();
// one way binding (for now)
Em.addObserver(this.parent, this.key, this, 'valueChanged');
},
value: function() {
return Em.get(this.parent, this.key);
}.property(),
valueChanged: function() {
this.notifyPropertyChange('value');
}
});
Handlebars.registerHelper("every", function() {
var args = [].slice.call(arguments);
var options = args.pop();
var context = (options.contexts && options.contexts[0]) || this;
Ember.assert("Must be in the form #every foo in bar ", 3 == args.length && args[1] === "in");
options.hash.keyword = args[0];
var property = args[2];
// if we're dealing with an array we can just forward onto the collection helper directly
var p = this.get(property);
if (Ember.Array.detect(p)) {
options.hash.dataSource = p;
return Ember.Handlebars.helpers.collection.call(this, Ember.Handlebars.EachView, options);
}
// create an array that we will manage with content
var array = Em.ArrayController.create();
options.hash.dataSource = array;
Ember.Handlebars.helpers.collection.call(this, Ember.Handlebars.EachView, options);
//
var update_array = function(result) {
if (!result) {
array.clear();
return;
}
// check for proxy object
var result = (result.isProxy && result.content) ? result.content : result;
var items = result;
var keys = Ember.keys(items).sort();
// iterate through sorted array, inserting & removing any mismatches
var i = 0;
for ( ; i < keys.length; ++i) {
var key = keys[i];
var value = items[key];
while (true) {
var old_obj = array.objectAt(i);
if (old_obj) {
Ember.assert("Assume that all objects in our array have a key", undefined !== old_obj.key);
var c = key.localeCompare(old_obj.key);
if (0 === c) break; // already exists
if (c < 0) {
array.removeAt(i); // remove as no longer exists
continue;
}
}
// insert
if (typeof value === 'object') {
// wrap object so we can give it a key
value = Ember.ObjectProxy.create({
content: value,
isProxy: true,
key: key
});
array.insertAt(i, value);
} else {
// wrap raw value so we can give it a key and observe when it changes
value = DataValueObserver.create({
parent: result,
key: key,
});
array.insertAt(i, value);
}
break;
}
}
// remove any trailing items
while (array.objectAt(i)) array.removeAt(i);
};
var should_display = function() {
return true;
};
// use bind helper to call update_array if the contents of property changes
var child_properties = ["[]"];
var preserve_context = true;
return Ember.Handlebars.bind.call(context, property, options, preserve_context, should_display, update_array, child_properties);
});
Inspired by:
How can I make Ember.js handlebars #each iterate over objects?
http://mozmonkey.com/2014/03/ember-getting-the-index-in-each-loops/
https://github.com/emberjs/ember.js/issues/4365
https://gist.github.com/strathmeyer/1371586
Here's that fiddle again if you missed it:
http://jsfiddle.net/L6axcob8/1/

how to get a property name which represent a function in JS

This is some JS code
var methodArr = ['firstFunc','secondFunc','thirdFunc'];
for(var i in methodArr)
{
window[methodName] = function()
{
console.log(methodName);
}
}
My problem is that how to get the name of a function in JS.
In JS, use this.callee.name.toString() can get the function name. But in this situation, it is a null value. How can i get the 'funName' string?
Sorry, I didn't make it clear.
I want to create functions in a for loop, all these functions has almost the same implementation which need its name. But others can call these functions use different name.I want to know what methodName function is called.
it seems a scope problem.
Try this:
var methodArr = ['firstFunc','secondFunc','thirdFunc'];
for(var i in methodArr) {
var methodName = methodArr[i]; // <---- this line missed in your code?
window[methodName] = (function(methodName) {
return function() {
console.log(methodName);
}
})(methodName);
}
window['secondFunc'](); // output: secondFunc

Javascript object literal: why can't I do this?

I have the following (simplified) object literal. The icons method uses closure to hide the icons variable, which I'd like to have as an associative array for later lookups.
var MapListings = {
icons: function () {
var allIcons = [] ;
return {
add: function (iconType, iconImage) {
var icon = new GIcon(MapListings.baseIcon);
icon.image = iconImage;
allIcons[iconType] = icon; // fails, but this is what I want
// allIcons.push(icon); // works, but this is not what I want
},
get: function () {
return allIcons;
}
};
} ()
}
I add items to the to the icons object like so:
MapListings.icons.add("c7", "/images/maps/blue.png");
MapListings.icons.add("c8", "/images/maps/red.png");
The following doesn't work:
allIcons[iconType] = icon;
But this does:
allIcons.push(icon);
Outside of the closure the associative array style works fine, so perhaps there is a conflict with jQuery? The error I get in firebug a is undefined looks to come from the library. I'd like to maintain the associative array style.
Any ideas?
Update
It looks like this conflict is coming from google maps. Odd, not sure of a way around this.
Dumbass Update
The part of my object literal that returned a base GIcon() object wasn't returning an object at all. So, the object didn't have the right properties.
baseIcon: function () {
var base = new GIcon();
base.shadow = '/images/maps/shadow.png';
base.iconSize = new GSize(12, 20);
base.shadowSize = new GSize(22, 20);
base.iconAnchor = new GPoint(6, 20);
base.infoWindowAnchor = new GPoint(5, 1);
return base;
}
And MapListings.baseIcon is NOT the same as MapListings.baseIcon()! D'oh
if you want a lookup table, just do var allIcons = {}
EDIT: Though technically it should work either way, as an array IS an object. Are you sure there isn't more to this?
EDIT #2: Can't you just make allIcons as a property of MapListings?
EDIT #3: I think it's working, but maybe you're not accessing it right? That or it fails creating the object with Google somehow, or the error you posted is happening elsewhere, and not here
function GIcon(){};
var MapListings = {
icons: function () {
var allIcons = [] ;
return {
add: function (iconType, iconImage) {
var icon = new GIcon(MapListings.baseIcon);
icon.image = iconImage;
allIcons[iconType] = icon; // fails, but this is what I want
// allIcons.push(icon); // works, but this is not what I want
window.x = allIcons
},
get: function () {
return allIcons;
}
};
} ()
};
MapListings.icons.add("c7", "/images/maps/blue.png");
MapListings.icons.add("c8", "/images/maps/red.png");
alert( MapListings.icons.get()['c8']['image'] )
You shouldn't loop using .length but instead directly access c7 or c8.
x = MapListings.icons.get();
for ( var prop in x ) {
if ( x.hasOwnProperty(prop ) ) {
alert( x[prop]['image'] )
}
}
So one thing you could do to fix this is change the way you reference the array. Since external to your add method you do this:
MapListings.icons["c7"]
You can also just use this to add to your array inside your add function:
add: function (iconType, iconImage) {
MapListings.icons[iconType] = iconImage;
},
allIcons[iconType] = icon; fails because allIcons is an Array, not an object. Try initializing allIcons to {} instead. That would allow you to place items in the collection by key.

Categories

Resources