简体   繁体   中英

window.onblur and console.log partial application with bind

I was reading through some javascript posts, and I came across this answer .

Basically, in the answer, the poster said that you could set

window.onblur = myBlurFunction

only if myBlurFunction is a function that doesn't need any arguments passed to it.

I was about to comment that it was possible to use bind to perform partial application for functions requiring arguments, but when I tried

var myBlurFunction = console.log.bind(console,'blur'); 
window.onblur = myBlurFunction;

blurring the window didn't print the string "blur", but instead printed what seems to be a blur object

blur blur { target: Window → window-onblur-not-working, …

Does anyone know why this approach doesn't work?


What I'm really looking for with my question is why is the event handler function given the event as an argument?

window.onblur = function(event){console.log(event)}

I've never seen any documentation that mentions or explains the event parameter.

Also, how is the bound parameter overridden? Typically once a value is bound to a function parameter, any additional arguments will be assigned to the subsequent parameters:

var f = function(arg1,arg2){console.log(arg1,arg2)}; 
g = f.bind(null,1);
g();            // 1 undefined
g(2);           // 1 2
g.call(null,2); // 1 2

Quoting the bind() page on MDN :

The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.

Let's split those two concepts and then explain them together in your example.


Changing the this context

This is an adaptation of the example in the MDN page (I promise it's simple!):

// This assignment is equivalent to 'window.x' or 'var x' in the global scope
this.x = "global!";

var obj = {
    x: "not global!",
    getX: function() {
        return this.x;
    }
};


// Running inside the object's scope, returns obj.x
obj.getX();
//=> "not global!"



// Assign the scoped function (obj.getX) to a global variable
var retrieveX = obj.getX;

// Running in the global scope, returns window.x
retrieveX();
//=> "global!"



// Binds the 'retrieveX' function to run inside the object's scope
var boundedRetrieveX = retrieveX.bind(obj);

// Running inside the specified 'obj' scope, returns obj.x
boundedRetrieveX();
//=> "not global!"

From that, we gather that passing obj as an argument changes what context this refers to.

On your example, you're doing something like this:

console.log.bind(console);  // The second argument doesn't matter for now

So you're telling console.log that any instances of this are a reference to the console context. Which I suppose is fine, shouldn't do much damage.


Prepending arguments

Again, adapting from the MDN page example:

function list() {
    // Simply convers the arguments list into an Array, then returns it
    return Array.prototype.slice.call(arguments);
}

// Example usage
list(1, 2, 3);
//=> [1, 2, 3]

// Using 'bind' to prepend to (append to the start of) the arguments list
// Note that, because 'this' context doesn't matter, the first argument is null
var betterList = list.bind(null, 98);

// Passing no arguments, it returns an array with only 98
// This is similar to '[98].concat([])'
betterList();
//=> [98]

// Passing arguments, it appends 98 to the start of the array
// Again, this is similar to '[98].concat([1,2,3])'
betterList(1, 2, 3);
// [98, 1, 2, 3]

// The parameters can go on indefinitely. They will all be added to the start of the arguments list in order
var bestList = list.bind(null, 98, 99, 100);

bestList(1, 2, 3);
//=> [98, 99, 100, 1, 2, 3]

The function list turns the Array-like object arguments , which contains all of the arguments passed to the function, into an actual Array.

With bind() , we append values to the start of that argument list, so that to the function, it seems as if they were already passed in that way in the first place.

Your code looks something like this:

console.log.bind(console, "blur");

Ignoring the first argument, you're prepending the arguments sent to console.log (which in this case is the event response) with "blur" . Which also isn't harmful, just not very useful.


Final thoughts

So, here's a screenshot of me playing around with the arguments. The first argument, that indicates the context for this , is set to null , just like in the example above, because it doesn't actually matter here. And I've passed a long list of arguments afterwards to be prepended to the onblur event response.

截图

As you can see, even though I added a bunch of stuff to the response, the Event object (not a blur object ! haha) is still there.

So that is why it "doesn't" work. It works in its own way. That just might not be what you were expecting.

You can still go for approaches presented in the question you linked, such as

window.onblur = () => console.log("blur");

Solutions that are less complicated and actually do what you expect them to

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