String.call.call type? [duplicate] - javascript

This question already has answers here:
"object is not a function" when saving function.call to a variable
(3 answers)
a is a function, then what `a.call.call` really do?
(2 answers)
Closed 5 years ago.
This is true (run it in the console in Google Chrome):
typeof String.call.call === 'function'
But calling it as
String.call.call() throws an error saying it's not a function:
Uncaught TypeError: String.call.call is not a function
at :1:13
Why trying to call a function (as per its typeof definition) throws an error saying it's not a function? Is it actually a function or not?
Note this happens to other classes such as Number, Function or others that inherit the .call method.

Let's try to break this down into more manageable pieces.
String is a constructor (or a static function / constructor hybrid, whatever you want to call it), and as such, it inherits from Function.prototype, because it is a function.
call is a function that executes its context (the this) with the context given to it in the first parameter:
console.log(String.call(true));
console.log(Array.call(null));
console.log(Function.call('so?'));
console.log(Object.call(/notice me/));
Let's look at a more official definition for Function.call():
The call() method calls a function with a given this value and arguments provided individually.
function.call(thisArg, arg1, arg2, ...)
Now, the interesting part here is noting that these three are identical:
'use strict';
var callRef = String.call; // Function.call, Array.call, Boolean.call, all of them are ===
console.log(String(/make me into a string/));
console.log(String.call(undefined, /make me into a string/));
console.log(callRef.call(String, undefined, /make me into a string/));
The callRef.call(String) means, call callRef() with the context of String, which is to say, execute String() with the context and parameters provided in the following arguments.
As we recall, the context doesn't really matter, but now we know that the first parameter of callRef.call() does matter, because it determines what function to execute, because it's telling callRef() what its context is, which gets executed as a function with the context provided in the next parameter.
Now reflecting on the initial question, what happens when we attempt to execute String.call.call()? Well, if a parameter is not specified, it is undefined, and we know that typeof undefined !== 'function'.
So here's my final answer:
String.call.call() is indeed a function... but all it's doing is attempting to execute undefined as a function, which obviously isn't.
String.call.call();
I hope this has been an interesting and informative explanation.

Related

Why console.log doesn't work as an argument? [duplicate]

This question already has answers here:
What is the difference between a function call and function reference?
(6 answers)
Closed 12 months ago.
In javascript there is a callback, and functions are first-class objects right? so i can pass a function as an argument however if i pass console.log() it doesn't work why is that isn't it a function as well?
setTimeout(function(){console.log("hello")},5000);
this code is valid however
setTimeout(console.log("hello"),5000);
produces an error, why is that?
When you call console.log with some argument, the argument is printed to console and the function returns undefined.
So when you do:
setTimeout(console.log("hello"),5000);
"hello" will be printed, but what you're essentially doing is:
setTimeout(undefined, 5000);
In the other example (that works) you create a new function, but you do not call it. So you pass this new function to setTimeout, and that is why it works.
The reason that the following code
setTimeout(console.log("hello"),5000);
fails is because console.log() is invoked directly inside the setTimeout parameter and it always returns undefined (more info: MDN Documentation on Console.log). This means you are essentially running setTimeout(undefined,5000);
You need to provide a function declaration that is not invoked. If you wrap the code inside of a function declaration and do not invoke it, setTimeout will invoke it for you after the specified time lapses. This is why your first statement works, you provide a function declaration:
setTimeout(function(){console.log("hello")},5000);
If you had invoked the function you provided in the first parameter it would also return undefined and "hello" would be output immediately.
So if you provide a function declaration it will work:
setTimeout(()=>{console.log("hello")},5000);
Another way to use console.log directly without a function declaration is to bind it to a variable (more info: MDN Documentation on Function.prototype.bind). An example using .bind prototype:
setTimeout(console.log.bind(null,"hello"),5000);
The code above binds "hello" to invocation of console.log. The first parameter is null in this example but it is the this context.
setTimeout also allows you to pass variables that you want the function to be invoked with. See MDN Documentation on setTimeout
For example to pass a variable:
setTimeout(console.log,5000,'hello');
In the above example you are telling setTimeout to invoke console.log in 5 seconds with the variable (in this case a sting) of 'hello'.
Invoking console.log('hello') will return undefined so you are not really passing it to setTimeout, It will print "hello" though but not inside a callback.
In most cases it wont throw an error (as you can see in the example below).
What you can do however is pass console.log (the function) and a 3rd argument the string "hello" in our case.
Running example with all 3 cases:
setTimeout(console.log("hello"),500);
setTimeout(function(){console.log("hello2")},500);
setTimeout(console.log,500,"hello3");
It produces an error since it evaluates console.log(...), which evaluates to undefined, and thus is not a function.
setTimeout accepts the function as a parameter, not the function call. console.log() is basically invoking the function/method but setTimeout requires the reference or more specifically called the callback.
In your example:-
setTimeout(function(){console.log("hello")},5000);
you can call it like
var callback = function(){console.log("hello")};
setTimeout(callback,5000);
The callback will be called by setTimeout later in future. I hope it clears everything.

JavaScript Function Context Incorrect

I noticed a weird thing in javascript. Consider the below:
var fn = ''.toUpperCase.call
console.log(typeof fn); // "function"
fn(); // Uncaught TypeError: `fn` is not a function
The above was executed on my Chrome's Developer Console. Version is 43.0.2357.81 m.
The typeof operator clearly shows that fn is a function, but the error suggests otherwise.
I've noticed that Function.apply shows at least some meaningful error message.
So, when is a function, not a function?
Context in Javascript is always established by the way you call a function.
var fn = ''.toUpperCase.call
This assigns the prototype implementation of the call function to fn. If you now call fn(), there's no context to the call. call would try to invoke the function object it was associated with. However, that context is established at call time. Since you're not giving it any context at call time, some internal component of call is throwing an error.
You'd have to do this:
fn.call(''.toUpperCase)
That's right, you call the call function, establishing a context, namely the toUpperCase string function. In this specific case this would lead to another error inside toUpperCase, since it is not bound to a specific context. You'd need to establish that context explicitly as well:
var fn = ''.toUpperCase.call
fn.call(''.toUpperCase.bind(''))
Also see How does the "this" keyword work?
deceze's answer is correct, I just want to explain it from a different point of view.
Your fn is a reference to Function.prototype.call which needs to be called with a function as its this reference, in this case, the context for call is String.prototype.toUpperCase, which was inherited through ''.toUpperCase
On top of that, String.prototype.toUpperCase also has to be called with a specific context, the string to upper case.
Here's another way to code what you wanted that may help you understand what is going on.
var str = 'aaa';
var upper = ''.toUpperCase;
var fn = upper.call;
// Now we have to specify the context for both upper and fn
console.log( fn.call(function() { return upper.call(str)}) ); // AAA
In your example, fn() is attempting to call call but it's not specifying the context, which typically defaults to the window object but in this case it just makes it undefined, which triggers a weird error in Chrome (as you discovered), but Firefox is clearer about the problem,
TypeError: Function.prototype.call called on incompatible undefined
So, when is a function, not a function?
The function is a function, but you are not executing it correctly, as explained in comments and the answer by #deceze
I noticed a weird thing in javascript.
You seem confused by the message that you are getting in the environment in which you tested, the message is not strictly correct.
Uncaught TypeError: fn is not a function
The current stable version of chromium (40.0.2214.91 on my linux install, chrome fails to run on my hardware without making settings changes), chrome being the environment on which you tested, gives a seemingly more correct error message that makes more sense.
Uncaught TypeError: undefined is not a function
So, were you asking/wanting to ask a serious question or were you just poking a little fun at a mistake in a version of chrome?

Javascript overloading functions

So I'm reading through a book and it uses this method to overload functions-
function addMethod(object,name,fn){
var old = object[name];
object[name] = function(){
if (fn.length == arguments.length){
return fn.apply(this,arguments);
} else if(typeof old == 'function'){
return old.apply(this,arguments);
}
}
}
I have a few questions on this.
Why is fn.length still in scope when the function that's passed in is called? Shouldn't executing the addMethod have caused fn to go out of scope?
Why isn't the arguments referring to the arguments property of the anonymous function, rather than the arguments property of the fn function? (which it should I thought?)
The parameter "fn" is in scope because that's how JavaScript works. It's part of the closure around that anonymous function.
The anonymous function created is the replacement for the original function bound to the object with the property name "name". When called, it checks the arguments actually passed on that particular call by looking at the arguments object. If it sees that the number of arguments passed is the same as the number of formal parameters in the "fn" function, then it calls that function. Otherwise, it calls the previous ("old") function, if it exists and is a function.A key thing to understand is that the .length property of a function instance gives you the number of formal parameters in the declaration. For example, the .length for that "addMethod" function would be 3.
The way that closures work in JavaScript took me a while to really "get" because I was a C programmer for a long time. In languages like that, space for the function call local variables (etc) is allocated on the stack, and when the function exits it's all popped off the stack. JavaScript just doesn't work that way.

Why use (function(){}).call(this);? [duplicate]

This question already has answers here:
Reason behind this self invoking anonymous function variant
(5 answers)
Closed 8 years ago.
I've been studying some JS libraries lately that were written by people who really knew what they were doing, and I keep seeing this pattern, and I can't find information about it. I read the docs on the .call() method, but it didn't really make sense to me. I'm hoping to get one of those good old classic in-depth SO explanations with examples.
(function(undefined){
/*(insert entire library here)*/
}).call(this);
What is this about? Why is this a good way to write a library?
Note, sometimes the undefined is omitted, though I have no idea what difference it makes to put it there or not. I don't even know where the arguments are coming from, or who the caller is.
Let's disassemble this piece of code.
First off there is an anonymous function with immediate invocation. It's similar to this:
(function () {/**/}).call();
(new Date()).getTime(); // timestamp since 1970 jan 1 in milliseconds
We don't assign new Date() to a variable, instead we use it immediately.
Now why use .call instead of just ()?
.call is a method all Functions have. The first argument is what this will be bound to, subsequent arguments will be passed as arguments to the function. So:
(function () {
console.log(this.foo); // bar
}).call({ "foo": "bar" });
This works in conjunction with undefined (see below).
.call is the same as .apply with one minor difference. .apply only takes 2 arguments, where the 2nd is an array of arguments. This would be similar:
(function () {}).call(this, "foo", "bar");
(function () {}).apply(this, [ "foo", "bar" ]);
A common use of apply is in conjunction with the magic variable arguments.
(function () {
console.log(Array.prototype.slice.call(arguments, 1)); // [ "bar" ]
})([ "foo", "bar" ]);
Array.prototype.slice.call(arguments, 1) may look scary but really it's just arguments.slice(1), but arguments isn't an Array so it doesn't have a slice function. We borrow Arrays slice function and use .call to set the this to arguments. Array.prototype.slice(arguments, 1??) is incorrect.
Now why is there this in .call(this)? this always points to the context you're in. If you're in an instance of a class it will point to the instance and if you're in the global scope it will point to that. In a browser environment it is also window.
Why undefined? Since we did a .call(this) with no second argument, all arguments to our anonymous function are undefined. I'm not really sure why you need to make an explicit variable named undefined there. Maybe this is support for some browsers or some lint tool that likes to see undefined defined.
Thanks to #TedHopp. undefined is fickle.
var undefined = "foo";
console.log(undefined); // undefined
(function (undefined) {
console.log(undefined); // "foo"
})("foo");
You can just as easily have:
(function () {
/* code here */
}());
This is completely valid and works just the same. There might be some performance or linting benefits to use the form you posted.

Abbreviating console.log in JavaScript [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
alias to chrome console.log
This is really silly, but I can't abbreviate console.log (I'm using Chrome). Here's my naive attempt:
var log = console.log;
log("Bingo"); // Uncaught TypeError: Illegal invocation
Should I be using apply()? But then I'd have to pass along the arguments, right?
This fails because references to Javascript methods do not include a reference to the object itself. A simple and correct way to assign the method console.log to a variable, and have the call apply to console, is to use the bind method on the log method, passing console as the argument:
var log = console.log.bind(console);
There is a hidden this argument to every method, and because of arguably bad language design, it's not closured when you get a reference to a method. The bind method's purpose is to preassign arguments to functions, and return a function that accepts the rest of the arguments the function was expecting. The first argument to bind should always be the this argument, but you can actually assign any number of arguments using it.
Using bind has the notable advantage that you don't lose the method's ability to accept more arguments. For instance, console.log can actually accept an arbitrary number of arguments, and they will all be concatenated on a single log line.
Here is an example of using bind to preassign more arguments to console.log:
var debugLog = console.log.bind(console, "DEBUG:");
Invoking debugLog will prefix the log message with DEBUG:.
The simple way will be to wrap it in a function:
var log = function (l) {
console.log(l);
}
But make note that console.log can take an unlimited number of arguments so the appropriate way will be do this:
var l = function () {
console.log.apply(console, arguments);
}

Categories

Resources