简体   繁体   中英

Are callbacks in JavaScript just blank functions?

I have been trying to wrap my head around callbacks and out of curiosity I was going through source code for vanilla js in todomvc and found this function:

Store.prototype.findAll = function (callback) {
    callback = callback || function () {};
    callback.call(this, JSON.parse(localStorage[this._dbName]).todos);

};

What does the following statement mean?

callback = callback || function () {};

Does that mean callbacks are simply blank functions?

Also, what's the difference between callback.call(this) and callback(anything) ?

I tried modifying this line

callback.call(this, JSON.parse(localStorage[this._dbName]).todos);

to

callback(JSON.parse(localStorage[this._dbName]).todos);

But the result was same. Why would one do callback.call instead of simply callback ?

No, callbacks are not "just blank". Here the function receives a callback as argument, and simply makes sure that it is a function. If no callback was passed it will be set to an empty function, but at least it's a function that can be called. Setting arguments to a default value (here: an empty function) simplifies the following code, which would otherwise need a bunch of if..else conditions to do something different if the callback wasn't a function.

For the other part of the question see How does the "this" keyword work? .

Are callbacks in JavaScript just blank functions?

Callbacks are not just blank functions. They are functions that can be called (often with some arguments) sometime later. Sometimes, they are optional and a host function will let you pass the callback or not pass the callback. If the callback is not passed, the host function can detect that it was not passed as an argument and adapt its behavior accordingly.

What does the following statement mean?

callback = callback || function () {};

Does that mean callbacks are simply blank functions?

This is a common design pattern to ensure that an argument to a function has a meaningful value in case it was not passed by the caller so the function can proceed internally and execute normally assuming that argument was passed by the caller, even if the caller didn't pass the argument.

The code callback = callback || function () {}; callback = callback || function () {}; is equivalent to this:

if (callback) {
    callback = callback;
} else {
    callback = function() {};
}

In Javascript, the || operator assigns the result to be the first of the two operands that is truthy (or false if neither is truthy), so if callback has a value, then it becomes the result of the || operand and you get callback = callback . Otherwise if callback is not truthy, it assigns callback to have a default function value that allows the rest of the function to operate assuming that callback has a legitimate value. This keeps all the rest of the function code from having to check if callback has a legitimate value before using it.

Also, what's the difference between callback.call(this) and callback(anything) ?

someFunction.call() allows you to the value of this when the function runs. So callback.call(this) makes the callback have the same value of this when it runs that the current function does. If you just did callback(...) without the .call() , then this will take on the default value which be either the window object or, if running in strict mode, it will be undefined .

If the particular callback doesn't happen to refer to the value of this in its code, then there will be no difference in outcome by using .call() or not using it, but in this case, it is an extra feature offered to the callback that it can access the current instance of the Store object in use by accessing the this value inside the callback.


Setting the value of this like this allows you to use a method of the object as the direct callback since that is the same calling convention that an object method has.

So, if you had a Store object like this:

var s = new Store();

And, there was a method of that object called setName() that uses this to refer to its own object instance, you could do:

s.findAll(s.setName);

This would only work because callback.call(this) was setting the instance value into the value of this in the callback.

The pattern:

function foo(bar) {
  bar = bar || someDefault;
  ...
}

is a common (but obsolete) way to set a function parameter to a default value if it was left undefined by the caller.

In this case, the function takes advantage of all functions being truthy and JS' unusual OR operator to set callback to an empty (nop) function if it was not set. You can see that behavior in the following example:

 var barDefault = 'No value provided!'; function foo(bar) { bar = bar || barDefault; console.log(bar); } foo(); // No value provided, since no arguments passed foo(3); // Argument passed, value provided foo(0); // Tricky case: falsy argument passed, so function assumes no value was provided 

As you can see from the example, this pattern can cause problems when an argument was passed that is falsy, as the OR operator will fall back to the default. To solve that, using:

bar = typeof bar !== 'undefined' ? bar : barDefault;

will explicitly check for undefined arguments.

With ES6 and default parameter values, this can be expressed more idiomatically as:

function foo(bar = someDefault) {
  ...
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM