I'm not used to working with this and trying to make some simple functions pass it back and forth. I'm not quite sure what javascript is expecting, but I don't think I'm doing it right.
$(".search-result").each(function() {
var x = $(this).find('.past-positions ol').children()
console.log(x)
//this prints as expected
pastJobs(this)
// this does not
})
function pastJobs() {
var x = $(this).find('.past-positions ol').children()
console.log(x)
// this prints as undefined
}
I assume its possible to pass this to functions, but I don't think I'm doing it in the right way.
What am I doing wrong?
Try pastJobs.call(this) instead.
Actually, here pastJobs(this) you're passing the lexical context this as param rather than binding that context to the function.
You can use the function bind to achieve what you want:
pastJobs.bind(this)()
pastJobs(this) you are passing this as an argument
and you're function doesn't accept arguments function pastJobs(). so doing $(this) in pastJobs is really out of context.
you could call the function .call(this)/.apply(this), or bind() and then call it. (bind only binds this object but unlike apply or call doens't invoke the function.
keep in mind that call and apply takes arguments after this object in a different manner. The call() method takes arguments separately.
The apply() method takes arguments as an array.
you need something like
$(".search-result").each(function() {
var x = $(this).find('.past-positions ol').children()
console.log(x)
//this prints as expected
pastJobs.call(this);
// this does not
})
Related
I'm a total newbie in js, so please be gentle with me :)
I want to understand where can we use the dot operator on some variable (in this case - an array), and when we cannot.
consider the following code:
//example 1
function f1(x) {
return x*x;
}
console.log(map(f1, [1,2,3,4,5]));
console.log([1,2,3,4,5].map(f1));
//example 2
function f2(arr) {
return arr;
}
console.log(f2([1,2,3,4,5]));
console.log([1,2,3,4,5].f2());
I know the examples are rather different, but still - in example 1 both prints work (and print the same) - even when using the array.function(..) syntax, while in example 2 the second print raises an error.
basically, what is the difference between the two, and why does it work only in example 1?
and generally - can I apply this method over different variable types (numbers, booleans, etc.)?
In the first example your are using the Array.prototype.map() function.
This function can be called in 2 different ways (See the docs)
[1,2,3].map(function(x){ ... }): //In your case the callback function is *f1()*
OR
arr.map(callback, [1,2,3]);
The second example is not working because the class Array has not function called f2()
[1,2,3,4,5] is an instance of Array "class", and that "class" has map method, that's why the following is a valid code:
[1,2,3,4,5].map(f1)
map method can accept any function as a parameter, and you're passing in your f1 function. It will be executed inside map function and it's executed as a standalone function.
Array "class" doesn't have f2 method, that's why this is invalid code:
[1,2,3,4,5].f2()
Here, f2 is executed immediately and is a part of [1,2,3,4,5] object
In the first case, map is defined as both a global function and public function of the Array. You can therefore call it via map(arr) or arr.map(..)
In the second case, as you only defined f2 as a global function which means the array can't access it.
Can someone please explain why we can simply pass a method name to a higher order function and everything works just fine. I know in something like Java I have to call the method words on each element individually. I was told that in Javascript if method signature matches we can simply pass in the name of the function with () and it will work. It is great but I want to know whats going on in the background. Why are we able to do this in javascript ?
function words(str) {
return str.split(" ");
}
var sentences = function(newArr){
return newArr.map(words);
}
In many languages you can pass a reference to a function as an argument to a function. That then allows the host function to use that argument and call that function when appropriate. That's all that is going on in Javascript. When you pass the name of a function without the () after it, you're just passing a reference to the function. That enables the host function to use that function as an argument and call it some time later.
In your specific example, .map() expects you to pass in a function that it will call once for each item in an array. So, you pass the name of a function that will then get called multiple times, once for each item in the array. That function you pass has a bit of a contract that it has to meet. It will be passed three arguments (value, index, array) and it must return a value that will be used to construct a new array.
In Javascript, since there is no argument type checking by the language, it is the developer's responsibility to make sure the arguments of the function you are passing match what the caller of that function will actually pass to it and you have to consult documentation of the calling code itself to know what arguments will be passed to it. You can name the arguments anything you want (that is entirely internal to your function implementation), but the order and the quantity of the arguments is determined by the caller and you must declare your function to match what the caller will provide.
Once thing that confused many in Javascript.
If you pass just a function name, you are passing a reference to the function (something that the host function can call at some later time).
array.map(myFn) // passes a function reference
Or, use an inline function (same outcome):
array.map(function(value, index, arr) {
// code goes here
})
If you put parens at the end of the function name, then the function is executed immediately and the return value of that function execution is what is passed:
array.push(myFn()); // pushes the result of calling myFn()
You are calling the words function repeatedly. You're calling it for each iteration of the map function.
The map function takes a callback which it runs for every iteration. That callback is usually in the form of
function (elementOfNewArr, indexOfNewArr, newArr) { }
Because functions are objects, you can store them on a variable and use that new variable name to call that function, instead of its original one. That's mostly the use of functions as objects. You can toss them around.
let foo = function () { return 'jasper!'; }
let boo = foo;
let ron = boo; // ron() will now return 'jasper!'
So, what you've done is plop in your callback function, though it was defined elsewhere. Since callback functions, like all functions are objects, you can declare that callback function, "saving" it to whatever variable you want and use it in anywhere that you can use it normally.
This is super useful if you have to use the same function in more than one place.
What I believe you are misunderstanding is that functions themselves can be treated the same as other variables in javascript. Consider this example:
var newArr = [1,2,3,4];
newArr.map(function(item){
return item * item;
});
In the above example, a function is passed as an argument to the map() function. Notice that it is described anonymously (no function name given). You can accomplish the exact same thing like this:
var newArr = [1,2,3,4];
function squared(item){
return item * item;
}
newArr.map(squared);
These two examples achieve the same thing, except in the second example, rather than writing the function in place, we define it earlier in the code. If it helps, you can even create the function in the same way as you would any other regular variable:
var squared = function(item){
return item * item;
};
You can pass this function around the same way. If you want to know the difference between defining functions in these ways try var functionName = function() {} vs function functionName() {}
Take this very simple framework I am experimenting with (so that I can learn JavaScript's function prototype more in depth.)
(function(){
var app = {
ui: {
app: document.querySelector('.app'),
settings: document.querySelector('.settings'),
},
actions: 'click settings openSidebar'
,
functions: {
openSidebar: function(e){
console.log(this); // <- expected value of this is 'app.ui'
}
},
run: function(){
var a1 = this.actions.split("\n");
var a2 = this.actions.split(" ");
var self = this;
this.ui[a2[1]].addEventListener(a2[0], function(e){
app.functions.openSidebar.call(self.ui,e);
});
}
};
app.run();
})();
This works great. Output from console is:
Object {ui: Object, actions: "click settings openSidebar", functions: Object, run: function}
However, when I try to do it like this:
var self = this;
this.ui[a2[1]].addEventListener(a2[0], function(e){
app.functions.openSidebar(e);
}.bind(this));
The context of this in openSidebar() is openSidebar (aka bind has no effect). Console output:
Object {openSidebar: function}
However, when I try to use the apply function, like so:
app.functions.openSidebar.apply(self.ui,e);
It works fine (this in openSidebar is app.ui) EXCEPT that the argument (e) does not get passed, so e == undefined.
Here goes:
1. Why does not bind work (at all) in the first example?
2. Why does apply work without passing arguments (the e (event))?
And for added brownie points:
3. Why does call work as expected?
Why does not bind work (at all) in the first example?
It "works", it just doesn't do what you expect.
Inside your anon function this is indeed the value set by bind. However, when you then call a function that is also a property of an object (functions.openSidebar) then for that invocation this is automatically bound to that object inside the function (i.e. this === functions). The value of this from the parent context is never "inherited" down the call chain.
Why does apply work without passing arguments (the e (event))?
Because apply tries to pull out the arguments for the call from its own second argument e by treating it as an array. This means that its length property is checked first; your event object doesn't have a length so apply gets the undefined value produced and effectively behaves as if you had passed in an empty array. See the annotated ES5 reference for the details.
Why does call work as expected?
This is a strange question. Why would it not work? You are using it exactly like it's meant to be used.
You'll need to do this:
this.ui[a2[1]].addEventListener(a2[0], app.functions.openSidebar.bind(this));
bind returns you a new function with a manually set context of whatever you pass in. Because you're not using bind on the right function, you're calling your function with app.functions to the left of the invoked function, which henceforth is known as this inside the invoked function!
Apply takes arguments as an array, not named parameters...
I can't explain the third without saying that that is how call works!
I study JavaScript Proxy Pattern, but I still do not get, where I can benefit from it. I would therefore like to provide you with two examples and kindly ask you to point at the difference between them.
Please, take a look at the code below:
What is the difference between the two addEventListener calls? One of them calls handleDrop in regular way. The other uses Proxy Pattern.
What will I gain using Proxy pattern approach?
I tested both functions, and they both call handleDrop successfully.
DndUpload.prototype.buildDropZone = function ()
{
var self = this,
this.dropZone.addEventListener('drop', function (e) { self.handleDrop.call(self, e) }, false);
this.dropZone.addEventListener('drop', self.handleDrop, false);
DndUpload.prototype.handleDrop = function (e)
{
alert("test");
...
};
}
You can provide me with good reference which contains very clear explanation of Proxy Pattern in JavaScript.
So what you're describing in your example isn't so much a demonstration of the Proxy pattern as much as a demonstration of confusion regarding the "calling object" and how it works in JavaScript.
In JavaScript, functions are "first-class." This essentially means that functions are data just like any other data. So let's consider the following situation:
var fn = (function () { return this.x; }),
a = {
x : 1,
fn : fn,
},
x = 2,
nothing = (function (z) { return z; });
So, we have an object a, which has two properties: fn and x. We also have variables x, fn (which is a function returning this.x), and nothing (which returns whatever it gets passed).
If we evaluate a.x, we get 1. If we evaluate x, we get 2. Pretty simple, eh? Now, if we evaluate nothing(a.x), then we get 1. That's also very simple. But it's important to realize that the value 1 associated with the property a.x is not in any way connected to the object a. It exists independently and can be passed around simply as a value.
In JavaScript, functions work the same way. Functions that are properties (often called "methods") can be passed as simple references. However, in doing so, they can become disconnected from their object. This becomes important when you use the this keyword inside a function.
The this keyword references the "calling object." That's the object that is associated with a function when that function is evaluated. There are three basic ways to set the calling object for a function:
If the function is called using the dot operator (e.g. a.fn()), the relevant object (in the example, a) is set as the calling object.
If the function is called using the function's call or apply properties, then you can explicitly set the calling object (we'll see why this is useful in a second).
If no calling object is set through method 1 or method 2, the global object is used (in a browser, this is typically called window).
So, back to our code. If we call a.fn(), it will evaluate as 1. That's expected because the this keyword in the function will be set to a due to the use of the dot operator. However, if we call simply fn(), it will return 2 because it is referencing the x property of the global object (meaning our global x is used).
Now, here's where things get tricky. What if you called: nothing(a.fn)()? You might be surprised that the result is 2. This is because passing a.fn into nothing() passes a reference to fn, but does not retain the calling object!
This is the same concept as what's going on in your coding example. If your function handleDrop were to use the this keyword, you would find it has a different value depending on which handler form you use. This is because in your second example, you're passing a reference to handleDrop, but as with our nothing(a.fn)() example, by the time it gets called, the calling object reference is lost.
So let's add something else to the puzzle:
var b = {
x : 3
};
You'll note that while b has an x property (and therefore satisfies the requirements for fn's use of this), it doesn't have a property referencing fn. So if we wanted to call the fn function with its this set to b, it might seem we need to add a new property to b. But instead we can use the aforementioned apply method on fn to explicitly set b as the calling object:
fn.apply(b); //is 3
This can be used to "permanently" bind a calling object to a function by creating a new function "wrapper." It's not really permanently binding, it's just creating a new function that calls the old function with the desired calling object. Such a tool is often written like so:
Function.prototype.bind = function (obj) {
var self = this;
return function() {
return self.apply(obj, arguments);
};
};
So after executing that code, we could do the following:
nothing(a.fn.bind(a))(); //is 1.
It's nothing tricky. In fact, the bind() property is built into ES5 and works pretty much like the simple code above. And our bind code is actually a really complicated way to do something that we can do more simply. Since a has fn as a property, we can use the dot operator to call it directly. We can skip all the confusing use of call and apply. We just need to make sure when the function gets called, it gets called using the dot operator. We can see how to do it above, but in practice, it's far simpler and more intuitive:
nothing(function () { return a.fn(); })(); //is 1
Once you have an understanding of how data references can be stored in closure scope, how functions are first-class objects, and how the calling object works, this all becomes very simple to understand and reasonably intuitive.
As for "proxies," those also exploit the same concepts to hook into functions. So, let's say that you wanted to count the number of times a.fn gets called. You can do that by inserting a proxy, like so (making use of some concepts from our bind code from above):
var numCalls = (function () {
var calls = 0, target = a.fn;
a.fn = (function () {
calls++;
return target.apply(a, arguments);
});
return (function () {
return calls;
});
}());
So now, whenever you call numCalls(), it will return the number of times a.fn() was called without actually modifying the functionality of a.fn! which is pretty cool. However, you must keep in mind that you did change the function referenced by a.fn, so looking way back to the beginning of our code, you'll notice that a.fn is no longer the same as fn and can't be used interchangeably anymore. But the reasons should now be pretty obvious!
I know that was basically a week of JavaScript education in a couple pages of text, but that's about as simple as it gets. Once you understand the concepts, the functionality, usefulness, and power of many JavaScript patterns become very simple to understand.
Hope that made things clearer!
UPDATE: Thanks to #pimvdb for pointing out my unnecessary use of [].slice.call(arguments, 0). I have removed it because it's, well, unnecessary.
Basically, passing self.handleDrop directly is functionally equivalent to passing the following function:
function() {
return self.handleDrop.apply(this, arguments);
}
because everything is passed through to the original function:
The this value
The arguments
The return value
With this in mind, compare your functions as follows:
function(e) { self.handleDrop.call(self, e) }
function() { return self.handleDrop.apply(this, arguments); }
The difference with your proxy way is:
It doesn't pass the return value through.
It doesn't pass all arguments through (only the first, e)
It doesn't pass the this value through, but uses a predefined one: self.
Now, the first two items don't make a difference here, because addEventListener doesn't care about the return value, and it also only passes one argument anyway.
But the third item is important: it sets a different this value in the function. By default, this is the element you bind the event to (it it set by the browser). Using the proxy way, you can set another this value.
Now, in your snippet it is not fully clear why you're setting a prototype function each time buildDropZone is called. Usually you define prototype functions only once. But when your handler handleDrop is called using the proxy way, this refers to the DndUpload instance, which is consistent with prototype functions in general.
Consider the code below:
function printThis() {
console.log(this);
}
var someObject = {
performTest : function() {
var self = this;
someOtherObject.higherOrderFunction(printThis);
someOtherObject.higherOrderFunction(function(){printThis.call(self)});
}
}
var someOtherObject = {
higherOrderFunction : function(f) {
f();
}
}
What will someOtherObject.higherOrderFunction(printThis) return?
How about someOtherObject.higherOrderFunction(function(){printThis.call(self)})
The answer to the first question depends on who and how you call someObject.performTest(). If I just call someObject.performTest() from a global context, it will probably print Window.
The second one will always print the someObject instance no matter what.
The closures or 'proxy pattern' as you call it comes in handy when you want to control exactly the execution context of a function.
Note: this in javascript does not behave like it does in other languages(in Java for example).
I've built a GUI which passes in a long JS Object as settings for an animation plugin. One of the settings allows for the user to call a function once an animation is complete. Unfortunately, it has to be passed to the JS Object as a string.
... [ 'functioncall()' ] ......
Inside my animation callback, I'm retrieving this string and trying to run it as a function.
First attempt works perfectly, but uses eval...
eval( OS.path_jscall[index].path_jscall[0][i] )
I've setup a more preferred approach like so:
var HookFunction=new Function( OS.path_jscall[index].path_jscall[0][i] );
HookFunction();
Both examples call the functioncall() function perfectly. I'm not sure how to pass (this) to the functioncall() though...
functioncall(obj){ console.log(obj); };
Everything keeps referring to the window. Any ideas? Thanks!
Assuming that HookFunction is the name, you can do either a call() or apply()
HookFunction.call(this,arg1,arg2,...,argN);
//or
HookFunction.apply(this,[arg1,arg2,...,argN]);
the basic difference of the 2 is that call() receives your "this" and an enumerated list of arguments after it, while apply() receives your "this" and an array of arguments
Use .call when calling your function. .call assigns the first parameter to the this variable.
var myFunction = function(arg1, arg2) {
alert(this);
}
myFunction.call(this, "arg1", "arg2");
Using your second example, you could do this:
HookFunction.call(this);