Javascript pattern: ( function(){} (function(){}) ) - javascript

I apologize in advance if this question is very simple, I'm a beginner in JavaScript.
I found a wealth of information about a resembling pattern (module pattern) but unless I am mistaken, this is either something different or an extension. Here is a typical code excerpt from the (wonderful) domjs project by Mariusz Nowak:
renameReserved = (function (rename) {
return function (scope) {
Object.keys(scope).forEach(rename, scope);
};
}(function (key) {
if (contains.call(reserved, key)) {
this['_' + key] = this[key];
delete this[key];
}
}));
I am finding it difficult to understand exactly what's happening here, even though each part taken independently is quite simple. Detailed help would be greatly appreciated, or a link to where I could learn more about this.

There are two functions involved here. First one
function (rename) {
return function (scope) {
Object.keys(scope).forEach(rename, scope);
};
}
And the other function object is passed as an argument to this function
function (key) {
if (contains.call(reserved, key)) {
this['_' + key] = this[key];
delete this[key];
}
}
Since we execute the first function, with an argument, (rename is the parameter which holds the function object passed) it returns another function which holds the function which we passed as argument because of the closure property.

I'm going to rewrite the code in a way that won't change what happens, but may make it a little clearer:
function makeNameReplacer( rename ) {
return function( scope ) {
Object.keys(scope).forEach(rename, scope);
}
}
function reservedWordRenamer( key ) {
if (contains.call(reserved, key)) {
this['_' + key] = this[key];
delete this[key];
}
}
renameReserved = makeNameReplacer( reservedWordRenamer );
So the first function is something that creates a function. The created function applies a name-substitution strategy to all the property names in a given object ("scope").
The second function is a strategy for replacing property names. Specifically, it checks to see if the property name passed in ("key") is in the set of reserved words. If it is, it replaces it with the name prefixed by an underscore, and removes the old property.
Thus the overall effect is that "renameReserved" becomes a function, one that takes an object as a parameter and which will scrub out property names that are reserved words.
You could come up with another strategy, and make another function. For example, if you wanted objects whose property names were all upper-case, you could do this:
function upperCaseRenamer( key ) {
var uckey = key.toUpperCase();
if (key !== uckey) {
this[uckey] = this[key];
delete this[key];
}
}
renameLowerCase = makeNameReplacer( upperCaseRenamer );

Related

Create a function that takes an object and a key as input - return value for key within the object

Just learning JS, came across this question on coursera. I can't even begin to answer this question; I'm not certain what it's asking. Sorry for the ignorance. Just looking for the basic format. I can write a function that takes input, but not certain how to do this. I've spent a while researching objects and it's not quite sinking in yet. Thanks!
You need to use multiple parameters. You need to separate the parameters with commas like:
function func(param1, param2) {
console.log("Parameter 1: " + param1);
console.log("Parameter 2: " + param2);
}
func(1, 2);
// Console:
// Parameter 1: 1
// Parameter 2: 2
In actual code this would look like the following:
function getValue(object, key) {
if (object.hasOwnProperty(key)) {
// Object.prototype.hasOwnProperty(key) returns if the specified object has the specified key in it
return object[key];
} else {
// The else statement is not really needed as the function already returned
// if the object has the specified key
return null;
// If you need this function for something specific, then you should return a default value
}
}
clean and clear:
function f(obj,keyname){
return obj[keyname];
}
Usage: f(myObj,my_key_field_name)
or simply obj[keyname] if you do not like to write a function

Javascript, syntax after function

I have a function that changes an object for me and I'm wondering how a bit of it works and would be grateful if someone could explain it or point me in the right direction. Here is the function:
$scope.filteredObject = Object.keys($scope.filterObject).reduce(function(p, collection) {
var values = $scope.filterObject[collection];
p[collection] = Object.keys(values).filter(function(key) {
return values[key] === true;
}).map(function(key) {
return key;
});
return p;
}, {});
So this works great, but I'm wondering what the }, {}); at the end of the function does exactly. Im not exactly sure the name of that and googleing "}, {} after a function in javascript" seems to confuse the hell out of google (lol). Thanks!
} - end of the anoymous function expression function(p, collection) { … }
, - delimiter between multiple arguments
{} - (empty) object literal, the second argument
) - end of function invocation, closing parentheses for the arguments list to .reduce(…)
It is an empty object and has nothing to do with the function.
Have a look at the Array.prototype.reduce()
The reduce function has a second optional parameter.
arr.reduce(callback[, initialValue])
So in your case it's like this:
callback = function(p, collection) { /*...*/ };
initialValue = {}; // could also be new Object()
Hope this helps :)

Possible strict violation - using prototype method for JavaScript function

I am getting the following issue with my code. I have a function that will take product data and then return it. We have many options on what we can sort the data on (for example price, name, data, stock levels, etc). Now I pass my data (which is in a JSON format) and depending on the option that is set as an argument I perform the data transform, however I get the following error when I run my code: Possible strict violation.
This is my function to sort the data:
// this is how i call the function, the data argument is the JSON data and formatting options are set in an object literal - this determines the data by name
dataSort(data, {'name':'product name'});
function dataSort(d, obj) {
// we do lots of stuff then later in the function
for (var key in obj) {
if(obj.hasOwnProperty(key)){
if(key === 'name'){
d = this.getProductByName(d, obj[key]); // This line produces the error
}
}
}
return d;
}
dataSort.prototype.getProductByName = function(data, val){
// do stuff the return data
return data;
};
Now I was alway taught to use the prototype when creating internal methods on functions, why am I getting my error? What am I doing wrong? Should I use a private method or an internal method using this.getProductByName = function(){} Any help will be appreciated.
The correct way to define private helper functions is to use a closure.
var datasort = (function () {
var getProductByName = function (data, val){
// do stuff
return data;
};
return function datasort(d, obj) {
var key;
// do lots of stuff ...
return getProductByName(d, obj.name);
};
}());
getProductByName is now only visible to the inner datasort function (which gets assigned to a variable of the same name).
The effect of the closure is that getProductByName is created only once, instead of with every call of datasort (that would be the case if you moved var getProductByName ... into function datasort).

strange Javascript notation - calling a function

I am trying to use some code from this tutorial and it contains some strange javascript notation that I am not familiar with chart.attr = function(name, value) {... . More than it being unfamiliar to me, it is throwing errors. I am trying to figure out how it can be changes to work in pure javascript.
function LineChart(config) {
function chart() {
// Draw the line.
chartContainer.append("path")
.datum(p.data)
.attr("class", "line")
.attr("d", line);
}
// **** This is the notation I do not understand, and gives me errors ****
chart.attr = function(name, value) {
if (arguments.length == 1)
{
return p[name];
}
else if (arguments.length == 2)
{
p[name] = value;
}
return chart;
}
chart.update = function() {
}
return chart;
}
Your code is trying to use a variable p which is undefined. It should be defined in the LineChart function as:
function LineChart(config) {
var p =
{
parent : null,
labels : [ "X", "Y" ],
...
};
...
}
As for the notation that you don't understand, this is an anonymous function expression which is being assigned to the chart.attr property. Even though it can be called by chart.attr(), this is still an anonymous function because it doesn't have a name.
The purpose of this particular function is to be a getter and setter for properties of the p object. It looks at the arguments to determine the way the function should behave: if there is only one argument, then it needs to return the property value, if there are two arguments then it should set the property value.
Example usage would look like:
var c = new LineChart();
var parent = c.attr('parent'); // get the value of the parent property
c.attr('parent', $('#something')); // set the value of the parent property
Let's dissect that line of code:
//Define chart.attr as a function that by default takes 2 parameters;
chart.attr = function(name, value) {
//If the function only gets 1 argument (so the first one)
if (arguments.length == 1)
{
//return the element with key "name" from the array p
//effectively a getter
return p[name];
}
// else, check if there are 2 arguments, but no more
else if (arguments.length == 2)
{
Assign the value of "value" to the element with key "name" from p
effectively a setter;
p[name] = value;
}
//at the end, return the chart
return chart;
}
So what this piece of code does is that if you pass only 1 argument to chart.attr(), it retrieves the value associated with that key from the array p. If you pass 2 arguments, it uses the second argument as the value of the key-valuepair from the array p with the first argument as the key.
now, without knowing the error you get, it's hard to debug this. However, the only way in which this would give an error is if p is undefined. if p doesn't contain that key, it returns null if it's a getter, and creates if it's a setter.
There is another way for this code to fail. And since the op didn't provide the error I will just speculate.
This can fail if you call, for example, chart.attr('somekey','somevalue') before chart.attr = function(name,value) { } is executed. This happens because of function hoisting...you are assigning a value to a property in this line of code. You're not defining a function...you're assigning one.
If you call chart.attr('somekey','somevalue') in the above conditions, you'll get a chart.attr is not a function error.

Having a closure issue. Can't seem to resolve it. Please advise

I have this code:
_trackit: function(){
for(var key in this.items.sublinks){
switch(key){
case 'shoes':
for(var innerkey in this.items.sublinks[key]){
(function(){
$(innerkey).observe('click', (function(e){
Event.stop(e);
someClass.click_link( this.items.sublinks[key][innerkey],false)
}));
)(this);
}
break;
}
}
}
The hash I am passing in has a size of 2. But as you would guess both of the links (since the hash maps to links), are passing the last hash value to come through (someClass.click_link <- in here this value, this.item.sublinks[key][innerkey]).
I've tried using an innerfuction etc... but something is messing up. If I go to "inner function deep", then this.items returns undefined.
Any help?
Since you're passing this in as an argument, you just need to create a parameter for it — call it, say, _this — and then you can refer to _this instead of this inside the function:
(function(_this, innerkey){
$(innerkey).observe('click', (function(e){
Event.stop(e);
someClass.click_link( _this.items.sublinks[key][innerkey],false)
}));
)(this, innerkey);
(There are other ways as well, but the above seems to be the way you were going for when you passed this in as an argument? And it's a perfectly respectable way to do it.)
Edited to add: Per Rob W's comment, I've edited the above to add innerkey as a parameter as well, since otherwise the inner function(e){...} expression will refer to the same innerkey variable as the outer function — a variable which, as a loop variable, is likely to have changed by the time the inner function actually runs. Passing it as a parameter gives the inner expression a new innerkey variable that's equal to what innerkey was when the inner function was created.
As others mentioned, you need to have an argument to receive the "this" you are passing. You will also need to pass copies of the "key" and "innerkey" variables, in order to avoid the closures inside for loops bug.
var make_event_listener = function(that, key, innerKey){
return function(e){
Event.stop(e);
someClass.click_link( that.items.sublinks[key][innerkey], false)
};
};
//...
for(var innerkey in this.items.sublinks[key]){
$(innerkey).observe('click', make_event_listener(this, key, innerKey) );
}
//...
OF course, you can use an anonymous version of make_event_listener instead but I find this way more readable.
The second call to "this" references the actual element being clicked. Change it to:
_trackit: function () {
var self = this;
for (var key in this.items.sublinks) {
switch (key) {
case 'shoes':
for (var innerkey in this.items.sublinks[key]) {
(function () {
$(innerkey).observe('click', (function (e) {
Event.stop(e);
someClass.click_link(self.items.sublinks[key][innerkey], false)
}));)(this);
}
break;
}
}
}
}

Categories

Resources