I came across this code for stripping Marketo forms of their included stylesheets. Let's assume that the code author is a super senior engineer. Array.from() could have been used instead of defining arrayFrom (functionally at any rate), so why use the latter?
For my part I'm trying to understand the arrayFrom definition (first line of the codeblock):
bind() sets this to the provided value, here [].slice (why?)
call() allows us to call getSelection with the this value bound by bind.
getSelection() returns a Selection object (or string in Firefox) of the selected text. This I'm unsure about.
In its use, arrayFrom gets passed an array (or NodeList) of stylesheets and returns an array of the same stylesheets (a shallow copy thereof?) no differently than if Array.from were used, so the functional bit of bind and call must be to alter the this value in a desirable way. Not sure how that acts on [].slice though.
Anyone? I'm clearly missing something.
const arrayFrom = getSelection.call.bind([].slice);
// remove element styles from <form> and children
const styledEls = arrayFrom(formEl.querySelectorAll("[style]")).concat(
formEl
);
styledEls.forEach(function (el) {
el.removeAttribute("style");
});
// create an array of all stylesheets in document
const styleSheets = arrayFrom(document.styleSheets);
// loop through stylesheets and check for ownerNode properties on each
styleSheets.forEach(function (ss) {
if (
//array of <link/> elements tied to stylesheets
[mktoForms2BaseStyle, mktoForms2ThemeStyle].indexOf(ss.ownerNode) !=
-1 ||
formEl.contains(ss.ownerNode)
) {
ss.disabled = true;
}
});
Nowadays we would just use Array.from. But your questions are about the construct that is used:
const arrayFrom = getSelection.call.bind([].slice);
First of all, this has nothing to do with getSelection, as the expression is not binding that, but the call function. This call function is on the Function prototype, so the above leads to the same result as:
const arrayFrom = Function.prototype.call.bind(Array.prototype.slice);
call is a function that allows one to call another function with the possibility to provide a this-argument to it. Here we define that the function to be called should be slice. The first argument we will provide to arrayFrom will be like the first argument we would provide to call, i.e. the object on which slice should be called. This gives it a similar behaviour as Array.from.
It may help to replace bind by this function that does a similar thing:
function arrayFrom(arrayLike) {
return Function.prototype.call.call(Array.prototype.slice, arrayLike);
}
It is confusing, but we invoke call with call so that we can provide a this argument to it (defining the function we want to call), making the second argument the this-argument that call (the first one) deals with.
Does javascript not check function parameters when invoking.
This function "test" below fires even though it is being called with no parameter.
<input type="button" value="test" onclick="test()">
test = function(param){
alert("test");
}
fiddle :
http://jsfiddle.net/Yazpj/1912/
Should an error not being thrown or does the javascript engine/parser not even check function parameters when finding what to call. Does this have any implications for overriding functions ?
No, JavaScript does not check parameters.
Extra parameters will be ignored. Parameters declared but not passed will have a value of undefined. All passed parameters (declared or otherwise) will appear in the arguments pseudo-array.
There are no implications for overriding functions because JS does not support overriding functions.
Libraries such as jQuery that have methods with multiple signatures use a single function that figures out the type of the passed parameters and then performs the required action.
You have to check on your own:
var test = function (param) {
if (typeof param === 'undefined') {
// do something...
}
};
Javascript is a really flexible language and this is just one example of it. Unless you are not accessing the param it won t rise any error e.g. param.sender
As for your override question it is sort of true. Every Javascript function has a arguments variable which is the array of passed parameters. if you give name the parameter defining the function JS just give it to you according to order.
But overriding is another story the overriding is achieved by checking the arguments element sometimes just length of the array sometimes type of the individual item.
For example; when you call $("#name").val() as there is no item it just return the value if arguments has values this time jQuery user proper assignment e.g. element.value = arguments[0]
I have a jQuery deferred, which I an resolving like so:
deferredAction.resolve(returnArray);
and this is calling a callback like:
function someCallback(myArray) {
...
}
This works fine, the callback function receives the array. However I need to set the context of the callback function, so I used deferred.resolveWith like so:
deferredAction.resolveWith(someContext, returnArray);
The context is now being set correctly. However, it now seems as if the returnArray is being split up. My callback only receives the first item of the array.
Why is this happening, and how can I work around it?
The documentation states that you should pass the arguments in a single array. In your case:
deferredAction.resolveWith(someContext, [returnArray]);
I fixed this by putting square brackets around the return parameter:
deferredAction.resolveWith(someContext, [returnArray]);
According to jQuery load() method api:
.load( url [, data] [, complete(responseText, textStatus, XMLHttpRequest)] )
1st parameter is url
2nd parameter is map or string that is sent to the server
3rd parameter is callback function.
With the working example below
$('#result').load('ajax/test.html', function() {
alert('Load was performed.');
});
it supplies arguments of 'url' and 'callback function', [data] argument is skipped.
Shouldn't the example code treat the callback function as [data] argument (2nd parameter) ? Because of the order that parameters defined in the API . By following the API, 1st is url, 2nd is data, 3rd is callback.
I don't get why the code would work. Very confused.
It is very clearly written in the jQuery source code.
https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.js
Search for load: function( url, params, callback )
It checks for the params (second parameter) and if it exist, It will call the isFunction method which internally check the type of the argument and return true if it is a function. The rest you know....
This is how isFunction looks like
No, It inspects the datatype of the parameters. If it finds a function as the second parameter then it uses it as a callback.
The position and order of the parameters has been thought out with the typical use-cases in mind and in stead of having to give a null value to skip a parameter .load('url', null, null, function() {}); you just imagine that the parameters "shift" places when skipped.
This applies to a lot of functions not just .load.
The square brackets ([]) around a parameter in the documentation indicate that it is optional. So your example is perfectly valid according to said documentation.
Checkout jquery's source file ajax.js in Github: https://github.com/jquery/jquery/blob/master/src/ajax.js#L178
Here it checks whether second argument is function. If yes, it takes it as the callback and params as undefined.
The brackets in the specification means that the parameters are optional, so you can use any of these forms:
.load(url, data, complete)
.load(url, data)
.load(url, complete)
.load(url)
The method will figure out if the second parameter is a callback function or a data object/string depending on the data type.
I'm looking back at the Backbone todo list and have a question about the collection.
Here is the code:
window.TodoList = Bacbone.Collection.extend({
model: Todo,
localStorage: new Store("todos"),
done: function() {
return this.filter(function(todo){return todo.get("done")})
},
remaining: function() {
return this.without.apply(this, this.done());
}
})
I understand everything that is going on here, except for the 'remaining' function.
The return statement: return this.without.apply(this, this.done()); is using a proxy to an underscore method - _.without
According to Underscore docs, here is what that is for:
without_.without(array, [*values]) Returns a copy of the array with
all instances of the values removed. === is used for the equality
test.
_.without([1, 2, 1, 0, 3, 1, 4], 0, 1);
=> [2, 3, 4]
So, I get that it is saying to return everything in the collection without a 'done' attribute with the value of 'true'.
What I don't understand is the 'apply' function that is being chained to it. That doesn't appear in the Backbone docs or the Underscore docs. At least not anywhere I can find it.
Can anyone explain in detail what is going on with those elements in the Return statement?
this is referring to the collection.
apply is a method of javascript functions that allows you to set context of a method and send an array of values to the caller.
apply expects context as the first parameter then an array or array-like (such as arguments) which will be passed in as parameters the function.
You can do the same thing with .call except the 2nd+ params are comma separated.
apply and call are native to javascript.
So...
return this.without.apply(this, this.done());
the method this.done() returns an array, but uses the context of the collection and passes in a series of values to be ignored via the without method. Which in turn returns all todos that aren't done within the collection.
Example:
_.without([1,2,3,4],1,2); === _.without.call([], [1,2,3,4], 1, 2);
My understanding is that, in this case, the use of apply is redundant, remaining could be shortened as follows:
remaining: function() {
return this.without(this.done());
}
As I understand it, the only reason to use apply is if you want (or need) to change the contextual item that without will operate on. In this case, we have no need to do that.
If I'm wrong, I'd really (really!) like to have an explanation of why apply is necessary here.
apply invokes a function and binds this in the context of that function to the first argument passed (in this case, the Collection instance TodoList). The second argument is an array of arguments to be passed to without.
By the way, apply isn't a Backbone thing -- it's native to JavaScript.
The reason for this
this.without.apply(this, this.done())
is that "_.without" does not accept as argument the array of items to be excluded as a single array ([]) argument
See here _.without to accept array as second argument
apply, which is part of the JS Function.prototype, here is a workaround to inject the excluding items in a single array argument
the use of apply in this case is redundant because backbone collections is doing the job correctly cf. http://backbonejs.org/docs/backbone.html#section-122
we can use underscore like this: _.without.apply(this, this.done()) or backbone binding like this: this.without(this.done) by using backbone bind.
Please take a look at underscore doc :
like this :
without_.without(array, [*values])
Returns a copy of the array with all instances of the values removed.
_.without([1, 2, 1, 0, 3, 1, 4], 0, 1);
=> [2, 3, 4]
I changed the function to this and got the exact same result on the TODO list application:
remaining: function () {
return this.filter(function (todo) {
return !todo.get('done');
});
}
I still didn't understand how apply became a method of without, I knew apply was a Javascript function, but then I read the documentation for apply and understood that in this case without was being used in an object-oriented way (see http://underscorejs.org/#chain).
In fact, apply could be passed null instead of this as the context and it wouldn't change the result because it's not being used at all:
return this.without.apply(null, this.done());
Notice the first this is the actual collection of models and without, via the second argument in apply, which is the array of done (completed) tasks, is producing the array of pending todo tasks.
By the way, the latest version of the TODO application renames the function done to completed.
Here this.without() delegates to _.without() function. _.without() needs an array and elements as parameters not as an array. By using apply() , apply calls _.without() in the correct manner.
var obj = {
f1: function(a,b,c){}
};
Now obj.f1(1,2,3) == obj.f1.apply(obj, [1,2,3]).
With this infomation, this.without(this.complete()) passes an array to the without method. But without method needs individual elements to be passed as arguments. That can be done using Function.apply().
The without function needs a list of elements to remove from this.
this.completed() returns an array, therefore it is not what without function is expecting.
apply is a native JavaScript function, which calls a function setting the this context as the first argument and an array as the second argument. The argument is passed to the original function as its list arguments.
In this case apply passes the arguments to without in this.completed(), meeting the expectation of without.
In conclusion, in this case, apply is necessary.
Sorry, I'm a total newb # this stuff, but couldn't fn.remaining (also) be declared as:
return this.filter(function(todo){return !todo.get("done")})
Stating this as a request for clarification, rather than an alternative declaration :)
(edit: couldn't bold the '!' before 'todo.get...')