I have a function, which I have prepared for a constructor call...
function Queue() {
if (!(this instanceof Queue)) return new Queue();
this.capacity = {};
this._count = 0;
}
And these method are being set on the the prototype property of Queue...Everything kosher right?
Queue.prototype.enqueue = function(name, options) {
this.capacity[name] = options || {};
this.count();
if (this._count > 5) {
return 'Max capacity has been reached—which is five, please dequeue....'
}
};
Queue.prototype.count = function() {
var total = Object.keys(this.capacity);
total.forEach(function(elem) {
this._count++
});
if (this._count == 1) {
console.log(this.capacity[Object.keys(this.capacity)])
console.log( 'There is one item in the queue');
} else {
console.log(this.capacity[Object.keys(this.capacity)])
console.log( 'There are ' + this._count + ' items in the queue');
}
};
My question how do i get this._count to increment when the enqueue/count method fires? I keep getting:
There are 0 items in the queue
I know I could add it on the .prototype property and place that in the count function and have it reference a local var...
Queue.prototype.count = function() {
var total = Object.keys(this.capacity), count = 0;
total.forEach(function(elem) {
this.count++
});
Queue.prototype.call = call // <-- weird no?
if (this.count == 1) {
console.log(this.capacity[Object.keys(this.capacity)])
console.log( 'There is one item in the queue');
} else {
console.log(this.capacity[Object.keys(this.capacity)])
console.log( 'There are ' + this.count + ' items in the queue');
}
};
But that seems not to be elegant...
Thanks in advance!
You need to bind this within forEach
Queue.prototype.count = function() {
var total = Object.keys(this.capacity);
total.forEach(function(elem) {
this._count++
}.bind(this)); //bind the context
if (this._count == 1) {
console.log(this.capacity[Object.keys(this.capacity)])
console.log( 'There is one item in the queue');
} else {
console.log(this.capacity[Object.keys(this.capacity)])
console.log( 'There are ' + this._count + ' items in the queue');
}
};
Try following modification (bind the function):
total.forEach(function(elem) {
this._count++
}.bind(this));
The problems is that this refers to a different object than in the parent function, because in JS, closures do not preserve this but instead the caller decides the this value. Alternatively, you can use the second thisArg argument of foreach.
The existing answers provide good solutions to the problem itself, I just thought I'd elaborate a bit more on the why.
this is a reference assigned by the execution context. More plainly it's a reference that's determined by the call site of the function. Since you can pass functions around in JavaScript like any other value this can lead to problems being caused by that reference being a moving target.
The issue with your code is that you're referring to this inside of a forEach. forEach takes a function as an argument and calls it, since what this is pointing to is determined by where the function is called and not where it's defined the value is something different when it gets called. It ends up falling back to whatever global context or undefined if you're in strict mode.
There are a number of different ways to handle the problem.
You could store a reference to the outer this on a variable and use it inside the other function.
var self = this;
total.forEach(function(elem) {
self._count++;
});
You could use .bind. It's a function method which returns a function that uses the passed in object as the reference for this no matter where you call it.
total.forEach(function(elem) {
this._count++;
}.bind(this));
Or you could use an arrow function. Arrow functions don't create their own context so they'll just maintain the value of this from the surrounding one.
total.forEach(() => {
this._count++;
});
This is a common problem and these are all valid solutions. They go from least to most elegant in my opinion.
Related
How can I pass a parameter to a function within CasperJS's evaluate?
//Should be logged in at this point
casper.then(function() {
var counter = 0;
var cap = 500;
this.evaluate(function(counter) {
var children = $('.companies-using-service').children();
while (counter < children.length) {
child = children[counter];
console.log($(child).find('a').attr('data-hint'));
counter++;
}
}, counter);
});
};
var scrapeClients = function(counter) {
var children = $('.companies-using-service').children();
while (counter < children.length) {
child = children[counter];
console.log($(child).find('a').attr('data-hint'));
counter++;
}
}
Above, I am able to pass parameters in using an unamed function. However, I wish to pass in the function scrapeClients to the evaluate function. In that case, I tried the following this.evaluate(scrapeClients(counter), counter). However, this does not work and the error says that it could not find $ variable.
Functions are first-class citizen in JavaScript. You can treat them in the same way as variables. You can pass them around. This means that you don't want
this.evaluate(scrapeClients(counter), counter)
but rather
this.evaluate(scrapeClients, counter)
In the first case, you're actually calling the function directly. Since the function uses some page properties that are only available inside of casper.evaluate, this will throw an error and stop the script.
I can create a recursive function in a variable like so:
/* Count down to 0 recursively.
*/
var functionHolder = function (counter) {
output(counter);
if (counter > 0) {
functionHolder(counter-1);
}
}
With this, functionHolder(3); would output 3 2 1 0. Let's say I did the following:
var copyFunction = functionHolder;
copyFunction(3); would output 3 2 1 0 as above. If I then changed functionHolder as follows:
functionHolder = function(whatever) {
output("Stop counting!");
Then functionHolder(3); would give Stop counting!, as expected.
copyFunction(3); now gives 3 Stop counting! as it refers to functionHolder, not the function (which it itself points to). This could be desirable in some circumstances, but is there a way to write the function so that it calls itself rather than the variable that holds it?
That is, is it possible to change only the line functionHolder(counter-1); so that going through all these steps still gives 3 2 1 0 when we call copyFunction(3);? I tried this(counter-1); but that gives me the error this is not a function.
Using Named Function Expressions:
You can give a function expression a name that is actually private and is only visible from inside of the function ifself:
var factorial = function myself (n) {
if (n <= 1) {
return 1;
}
return n * myself(n-1);
}
typeof myself === 'undefined'
Here myself is visible only inside of the function itself.
You can use this private name to call the function recursively.
See 13. Function Definition of the ECMAScript 5 spec:
The Identifier in a FunctionExpression can be referenced from inside the FunctionExpression's FunctionBody to allow the function to call itself recursively. However, unlike in a FunctionDeclaration, the Identifier in a FunctionExpression cannot be referenced from and does not affect the scope enclosing the FunctionExpression.
Please note that Internet Explorer up to version 8 doesn't behave correctly as the name is actually visible in the enclosing variable environment, and it references a duplicate of the actual function (see patrick dw's comment below).
Using arguments.callee:
Alternatively you could use arguments.callee to refer to the current function:
var factorial = function (n) {
if (n <= 1) {
return 1;
}
return n * arguments.callee(n-1);
}
The 5th edition of ECMAScript forbids use of arguments.callee() in strict mode, however:
(From MDN): In normal code arguments.callee refers to the enclosing function. This use case is weak: simply name the enclosing function! Moreover, arguments.callee substantially hinders optimizations like inlining functions, because it must be made possible to provide a reference to the un-inlined function if arguments.callee is accessed. arguments.callee for strict mode functions is a non-deletable property which throws when set or retrieved.
You can access the function itself using arguments.callee [MDN]:
if (counter>0) {
arguments.callee(counter-1);
}
This will break in strict mode, however.
You can use the Y-combinator: (Wikipedia)
// ES5 syntax
var Y = function Y(a) {
return (function (a) {
return a(a);
})(function (b) {
return a(function (a) {
return b(b)(a);
});
});
};
// ES6 syntax
const Y = a=>(a=>a(a))(b=>a(a=>b(b)(a)));
// If the function accepts more than one parameter:
const Y = a=>(a=>a(a))(b=>a((...a)=>b(b)(...a)));
And you can use it as this:
// ES5
var fn = Y(function(fn) {
return function(counter) {
console.log(counter);
if (counter > 0) {
fn(counter - 1);
}
}
});
// ES6
const fn = Y(fn => counter => {
console.log(counter);
if (counter > 0) {
fn(counter - 1);
}
});
I know this is an old question, but I thought I'd present one more solution that could be used if you'd like to avoid using named function expressions. (Not saying you should or should not avoid them, just presenting another solution)
var fn = (function() {
var innerFn = function(counter) {
console.log(counter);
if(counter > 0) {
innerFn(counter-1);
}
};
return innerFn;
})();
console.log("running fn");
fn(3);
var copyFn = fn;
console.log("running copyFn");
copyFn(3);
fn = function() { console.log("done"); };
console.log("fn after reassignment");
fn(3);
console.log("copyFn after reassignment of fn");
copyFn(3);
Here's one very simple example:
var counter = 0;
function getSlug(tokens) {
var slug = '';
if (!!tokens.length) {
slug = tokens.shift();
slug = slug.toLowerCase();
slug += getSlug(tokens);
counter += 1;
console.log('THE SLUG ELEMENT IS: %s, counter is: %s', slug, counter);
}
return slug;
}
var mySlug = getSlug(['This', 'Is', 'My', 'Slug']);
console.log('THE SLUG IS: %s', mySlug);
Notice that the counter counts "backwards" in regards to what slug's value is. This is because of the position at which we are logging these values, as the function recurs before logging -- so, we essentially keep nesting deeper and deeper into the call-stack before logging takes place.
Once the recursion meets the final call-stack item, it trampolines "out" of the function calls, whereas, the first increment of counter occurs inside of the last nested call.
I know this is not a "fix" on the Questioner's code, but given the title I thought I'd generically exemplify Recursion for a better understanding of recursion, outright.
Using filter and map, recursion example removing null properties from an object
const obj = {
name: {
first: "Jeson",
middle: null,
last: "Holder"
},
age: 45
}
function removeNullOrEmpty(obj){
return Object.fromEntries(
Object.entries(obj)
.filter(([_, v])=> v!== null && v.length !== 0)
.map(([k, v])=>[k, v === Object(v)?removeNullOrEmpty(v):v])
)
}
console.log(removeNullOrEmpty(obj))
In my AngularJS application I am doing the following
$http.get('/plugin/' + key + '/js').success(function (data) {
if (data.length > 0) {
console.log(data);
// Here I would also need the value of 'key'
}
});
Now I need to access the key value within the success callback, i.e. I need to know which value it had when the get() request has been made.
Any "best practice" how to do so?
PS: I can do the following, but is there a better way?
var key = config.url.split('/')[2];
Solution 1:
$scope.key = key;
$http.get('/plugin/' + key + '/js').success(function (data) {
if (data.length > 0) {
console.log(data, $scope.key);
}
});
Solution 2 (Updated per Jim Hong's observation in his answer):
$http.get('/plugin/' + key + '/js').success((function(key) {
return function(data) {
console.log(key, data);
}
})(key));
Reference to #geniuscarrier
The working solution on my side is
$http.get('/plugin/' + key + '/js').success((function(key) {
return function(data) {
console.log(key, data);
}
})(key));
Since using #geniuscarrier, I'l get
data undefined error
.
Technically speaking, this is not an AngularJS problem but a feature of javascript
first of all, functions that you defined inside a scope will have access to local variable and parameter of its parent scope
function parent(arg){
var local
function child(){
// have access to arg and local
}
}
Scope actually works well with the parent-child analogy: if you are the parent and you own a cookie, of cause you are welling to share it with your children...but if you are a kid...your cookie is your cookie, your parent is not allowed to touch it :). In other words, inner scope can access outer scope but it does not work both ways
So you should definitely be able to do:
$http.get('/plugin/' + key + '/js').success(function (data) {
if (data.length > 0) {
console.log(data, key); //as long as you can pass it to $http.get as an argument
//you can access it here
}
});
Secondly, because of the event-driven nature of javascript, inner function store references to the outer function’s variables. you probably have heard of this
functions in javascript are objects
local variables and parameters are thus private members of the function:
function ObjectA(){ // define a constructor
var x = 10 // private variable
changeX : function(){
x = 20 // access and MODIFY a variable of parent scope
}
}
if you can understand how private variable works in javascript, then you basically understand what closure is. Thus, for call back function, it is very possible that by the time it is triggered, the value of the parent scope variable is already changed. To fix this, you can use an Immediately Invoked Function Expression (IIFE)
$http.get('/plugin/' + key + '/js').success((function(currentKeyValue) {
return function(data) {
console.log(currentKeyValue, data);
// again, currentKeyValue is a REFERENCE to outer function's
// parameter. However, since String is passed by value in javascript
// currentKeyValue of outer scope is a DIFFERENT string that has the
// same value as KEY when it is invoked
}
})(key)); // immediately invoke the function passing the key as a parameter
Instead of polluting scope or complicating with iif, another cleaner way is to create a callback function and call it with parameters;
var myCallBack = function (key) {
return function (data) {
if (data.length > 0) {
console.log(data, key);
}
}
}
$http.get('/plugin/' + key + '/js').success(myCallBack(key));
Phew, I was looking for this answer for so long, but it's good it is here. Just to update it, since legacy promise methods success and error have been deprecated and we should use the standard then method instead.
Solution 2 in #geniuscarrier and #jim-horng answers may be rewritten like this:
$http.get('/plugin/' + key + '/js').then(
(function(key) {
return function(data) {
console.log(key, data);
}
})(key),
function(data) {
//error handle
});
Here's an example of a situation where a simple JS loop does not behave as expected, because of the loop variable not being in a separate scope.
The solution often presented is to construct an unpleasant-looking bit of loop code that looks like this:
for (var i in obj) {
(function() {
... obj[i] ...
// this new shadowed i here is now no longer getting changed by for loop
})(i);
}
My question is, could this be improved upon? Could I use this:
Object.prototype.each = function (f) {
for (var i in this) {
f(i,this[i]);
}
};
// leading to this somewhat more straightforward invocation
obj.each(
function(i,v) {
... v ...
// alternatively, v is identical to
... obj[i] ...
}
);
when I ascertain that I need a "scoped loop"? It is somewhat cleaner looking and should have similar performance to the regular for-loop (since it uses it the same way).
Update: It seems that doing things with Object.prototype is a huge no-no because it breaks pretty much everything.
Here is a less intrusive implementation:
function each (obj,f) {
for (var i in obj) {
f(i,obj[i]);
}
}
The invocation changes very slightly to
each(obj,
function(i,v) {
... v ...
}
);
So I guess I've answered my own question, if jQuery does it this way, can't really go wrong. Any issues I've overlooked though would warrant an answer.
Your answer pretty much covers it, but I think a change in your original loop is worth noting as it makes it reasonable to use a normal for loop when the each() function isn't handy, for whatever reason.
Update: Changed to use an example that's similar to the example referenced by the question to compare the different approaches. The example had to be adjusted because the each() function requires a populated array to iterate over.
Assuming the following setup:
var vals = ['a', 'b', 'c', 'd'],
max = vals.length,
closures = [],
i;
Using the example from the question, the original loop ends up creating 2n functions (where n is the number of iterations) because two functions are created during each iteration:
for (i = 0; i < max; i++) {
closures[i] = (function(idx, val) { // 1st - factoryFn - captures the values as arguments
return function() { // 2nd - alertFn - uses the arguments instead
alert(idx + ' -> ' + val); // of the variables
};
})(i, vals[i]);
}
This can be reduced to creating only n + 1 functions by creating the factory function once, before the loop is started, and then reusing it:
var factoryFn = function(idx, val) {
return function() {
alert(idx + ' -> ' + val);
};
};
for (i = 0; i < max; i++) {
closures[i] = factoryFn(i, vals[i]);
}
This is nearly equivalent to how the each() function might be used in this situation, which would also result in a total of n + 1 functions created. The factory function is created once and passed immediately as an argument to each().
each(vals, function(idx, val) {
closures[idx] = function() {
alert(idx + ' -> ' + val);
};
});
FWIW, I think a benefit to using each() is the code is a bit shorter and creating the factory function right as it's passed into the each() function clearly illustrates this is its only use. A benefit of the for loop version, IMO, is the code that does the loop is right there so it's nature and behavior is completely transparent while the each() function might be defined in a different file, written by someone else, etc.
Global Scope
When something is global means that it is accessible from anywhere in your code. Take this for example:
var monkey = "Gorilla";
function greetVisitor () {
return alert("Hello dear blog reader!");
}
If that code was being run in a web browser, the function scope would be window, thus making it
available to everything running in that web browser window.
Local Scope
As opposed to the global scope, the local scope is when something is just defined and accessible in a
certain part of the code, like a function. For instance;
function talkDirty () {
var saying = "Oh, you little VB lover, you";
return alert(saying);
}
alert(saying); // Throws an error
If you take a look at the code above, the variable saying is only available within the talkDirty
function. Outside of it it isn’t defined at all. Note of caution: if you were to declare saying without
the var keyword preceding it, it would automatically become a global variable.
What this also means is that if you have nested functions, the inner function will have access to the
containing functions variables and functions:
function saveName (firstName) {
function capitalizeName () {
return firstName.toUpperCase();
}
var capitalized = capitalizeName();
return capitalized;
}
alert(saveName("Robert")); // Returns "ROBERT"
As you just saw, the inner function capitalizeName didn’t need any parameter sent in, but had complete
access to the parameter firstName in the outer saveName function. For clarity, let’s take another
example:
function siblings () {
var siblings = ["John", "Liza", "Peter"];
function siblingCount () {
var siblingsLength = siblings.length;
return siblingsLength;
}
function joinSiblingNames () {
return "I have " + siblingCount() + " siblings:\n\n" + siblings.join("\n");
}
return joinSiblingNames();
}
alert(siblings()); // Outputs "I have 3 siblings: John Liza Peter"
As you just saw, both inner functions have access to the siblings array in the containing function, and
each inner function have access to the other inner functions on the same level (in this case,
joinSiblingNames can access siblingCount). However, the variable siblingsLength in the siblingCount is
only available within that function, i.e. that scope.
//I have the following function:
function handle_message(msg)
{
//do work
console.log('some work: '+msg.val);
//call next message
msg.next();
}
//And array of message objects:
var msgs = [ {val : 'first msg'}, { val : 'second msg'}, { val : 'third msg'}];
//I link messages by setting next parameter in a way that it calls handle_message for the next msg in the list. Last one displays alert message.
msgs[2].next = function() {alert('done!')};
msgs[1].next = function() {handle_message(msgs[2]);};
msgs[0].next = function() {handle_message(msgs[1]);};
//Start the message handle "chain". It works!
handle_message(msgs[0]);
//======== Now I do exactly the same thing but I link messages using the for loop:
for (var i=msgs.length-1; i>=0; i--)
{
if (i==msgs.length-1)
{
msgs[i].next = function() {alert('done!');};
}
else
{
msgs[i].next = function() {handle_message(msgs[i+1]);};
}
}
//Start the message handling chain. It fails! It goes into infinite recursion (second message calls itself)
handle_message(msgs[0]);
Can sombody explain why it happens? Or maybe an alternative to this pattern? My case is this: I receive an array with messages and I have to handle them in order, one ofter another SYNCHRONOUSLY. The problem is some of the messages require firing a series of animations (jqwuery animate() which is async) and the following messages cannot be handled until the last animation is finished. Since there is no sleep() in javascript I was trying to use such pattern where the message calls the next one after it is finished (in case of animations I simply pass the 'next' function pointer to animate's "complete" callback). Anyway, I wanted to build this 'chain' dynamically but discovered this strange (?) behaviour.
You need a closure to make it work:
function handle_message( msg ) {
console.log( 'some work: ' + msg.val );
msg.next();
}
var msgs = [{val :'first msg'},{val:'second msg'},{val:'third msg'}];
for ( var i = msgs.length - 1; i >= 0; i-- ) {
(function(i) {
if ( i == msgs.length - 1 ) {
msgs[i].next = function() { alert( 'done!' ); };
} else {
msgs[i].next = function() { handle_message( msgs[i + 1] ); };
}
})(i);
}
handle_message( msgs[0] );
Live demo: http://jsfiddle.net/simevidas/3CDdn/
Explanation:
The problem is with this function expression:
function() { handle_message( msgs[i + 1] ); }
This function has a live reference to the i variable. When this function is called, the for loop has long ended and the value of i is -1. If you want to capture the current value of i (the value during the iteration), you need to an additional wrapper function. This function captures the current value of i permanently (as an argument).
I think the problem is that i doesn't have the value you think it has:
// i is defined here:
for (var i=msgs.length-1; i>=0; i--)
{
if (i==msgs.length-1)
{
msgs[i].next = function() {alert('done!');};
}
else
{
msgs[i].next = function() {
// when this line gets executed, the outer loop is long finished
// thus i equals -1
handle_message(msgs[i+1]);
};
}
}
See point #5 Closures in loops at http://blog.tuenti.com/dev/top-13-javascript-mistakes/
Think about the values you are capturing in the closure.
msgs[i].next = function() {handle_message(msgs[i+1]);};
This captures the value of i, but it changes the next iteration so you get an infinite loop.
By the end of the loop i is -1 so i+1 is going just going to be the same message over and over again.