As a front end white, I don’t know if you have the same problem as me. After reading the analysis of an interview question, I thought I could at that time, but I couldn’t look again two days later. Blindly pursuing all kinds of new technology, I feel like I can do everything, but I can’t do it once I get started… After reflecting on the pain, I finally realized the problem and began to focus on the training of basic skills. Nearly half a year to read through (in fact is swallowed) “JavaScript Advanced Programming”, “You don’t know JavaScript on, in, under” and other books, this series of articles is my reading process of knowledge points in some summary. Like the students remember to help me point 😁.

Understand wang series (1) thoroughly understand JavaScript function execution mechanism understand Wang series (2) thoroughly understand JavaScript scope understand Wang series (3) thoroughly understand JavaScript objects understand Wang series (4) thoroughly understand JavaScript classes To understand this, we need to understand JavaScript data types. To understand this, we need to understand JavaScript data types Complete JavaScript type conversion

1. Misunderstanding of this

1.1 Pointing to itself

It’s easy to think of this as referring to the function itself, and this inference makes sense from an English grammar point of view.

So why do you need to reference the function itself from within? The common reasons are recursion (calling the function from within) or you can write an event handler that unbinds itself after the first call.

1.2 points to its scope

In some cases it is true, but in other cases it is false. To be clear, this does not in any case refer to the lexical scope of the function. A JavaScript scope is indeed like an object in that the visible identifiers are its properties. But the scope “object” is not accessible through JavaScript code; it exists inside the JavaScript engine.

function foo() { 
    var a = 2; 
    this.bar(); 
} 
function bar() { 
    console.log( a ); 
} 
foo(); // ReferenceError: a is not defined
Copy the code

1.3 What is this

This is bound at run time, not at write time, and its context depends on the conditions at the time of the function call. The binding to this has nothing to do with where the function is declared, and only depends on how the function is called. When a function is called, an activity record (sometimes called the execution context) is created. This record contains information about where the function was called (the call stack), how the function was called, and the arguments passed in. This is a property of the record that will be used during the execution of the function

2. This binding rule

The this of each function is bound at the time it is called, depending entirely on where the function is called (that is, the method the function is called to).

2.1 Default Binding

Independent function calls. Think of this rule as the default rule when no other rules can be applied.

function foo() { 
    console.log( this.a ); 
} 
var a = 2; 
foo(); / / 2
Copy the code

Called directly with a function reference without any embellishments, so only the default binding can be used

In strict mode, this is bound to undefined, otherwise to the global object window

2.2 Implicit binding

Whether the call location has a context object, or is owned or packaged by an object

function foo() { 
    console.log( this.a ); 
} 
var obj = { 
    a: 2.foo: foo 
}; 
obj.foo(); / / 2
Copy the code

The call location refers to the function using the OBj context, so you can say that the obj object “owns” or “contains” the function when it is called

When a function reference has a context object, the implicit binding rule binds this in the function call to that context object. Because this is bound to obj when calling foo(), this.a and obj.a are the same

Only the previous or last level in the chain of object property references counts in the call location

function foo() { 
    console.log( this.a ); 
} 
var obj2 = { 
    a: 42.foo: foo 
}; 
var obj1 = { 
    a: 2.obj2: obj2 
}; 
obj1.obj2.foo(); / / 42
Copy the code

Let’s look at an example of default and implicit bindings:

function fn() {
	console.log(this);
}
let obj = {
	name: 'OBJ'.fn: fn
};
fn();
obj.fn();
console.log(obj.hasOwnProperty('name')); //=> this:obj TRUE
console.log(obj.__proto__.hasOwnProperty('name')); //=> this:obj.__proto__(object.prototype) FALSE
console.log(Object.prototype.hasOwnProperty.call(obj, 'name')); // TRUE <=> obj.hasOwnProperty('name')
Copy the code

Implicit loss

function foo() { 
    console.log( this.a ); 
} 
var obj = { 
    a: 2.foo: foo 
}; 
var bar = obj.foo; // Function alias!
var a = "oops, global"; // A is a property of the global object
bar(); // "oops, global"
Copy the code

Although bar is a reference to obj.foo, it is actually a reference to the function foo itself, so bar() at this point is an undecorated function call, so the default binding is applied.

Function callbacks also produce implicit loss:

function foo() { 
    console.log( this.a ); 
} 
function doFoo(fn) { 
    // fn refers to foo
    fn(); // <-- call location!
}
var obj = { 
    a: 2.foo: foo 
}; 
var a = "oops, global"; // A is a property of the global object
doFoo( obj.foo ); // "oops, global"
Copy the code

Parameter passing is actually an implicit assignment, so when we pass in the function we get an implicit assignment, so the result is the same as in the previous example

2.3 Displaying Binding

Most functions provided by JavaScript and any functions you create yourself can use call(..) And the apply (..) methods

function foo() { 
    console.log( this.a ); 
} 
var obj = { 
    a:2 
}; 
foo.call( obj ); / / 2
Copy the code

Through the foo. Call (..) , we can force foo to bind its this to obj when calling foo.

If you pass in a primitive value (String, Boolean, or numeric) as the binding object for this, the primitive value is converted to its object form (i.e., new String(..)). , new Boolean (..) Or the new Number (..) ). This is often referred to as “boxing”.

  1. Hard binding

A hard bound bar can no longer modify its this

function foo() { 
    console.log( this.a ); 
}
var obj = { 
    a:2 
}; 
var bar = function() { 
    foo.call( obj ); 
}; 
bar(); / / 2
setTimeout( bar, 100 ); / / 2
bar.call( window ); / / 2
Copy the code

Since hard binding is a very common pattern, ES5 provides a built-in method called function.prototype.bind, which is used as follows

function foo(something) { 
    console.log( this.a, something ); 
    return this.a + something; 
}

var obj = { 
    a:2 
}; 
var bar = foo.bind( obj ); 
var b = bar( 3 ); / / 2, 3
console.log( b ); / / 5
Copy the code

bind(..) Returns a hard-coded new function that sets the argument you specify to the context of this and calls the original function.

  1. API call context

Many functions in third-party libraries, as well as many new built-in functions in the JavaScript language and host environments, provide an optional argument, often called a “context,” that functions in conjunction with bind(..) Again, make sure your callback uses the specified this

function foo(el) { 
    console.log( el, this.id ); 
} 
var obj = { 
    id: "awesome" 
}; 
/ / calls foo (..) Bind this to obj
[1.2.3].forEach( foo, obj ); // 1 awesome 2 awesome 3 awesome
Copy the code

2.4 the new binding

Using new to call a function, or when a constructor call occurs, automatically does the following.

  1. Create (or construct) an entirely new object.
  2. The new object will be connected by [[Prototype]].
  3. This new object is bound to the this function call.
  4. If the function returns no other object, then the function call in the new expression automatically returns the new object.

3. The priority

  1. Is the function called in new (new binding)? If so, this binds to the newly created object.
var bar = new foo()
Copy the code
  1. Are functions invoked by call, apply (explicit binding), or hard binding? If so, this is bound to the specified object.
var bar = foo.call(obj2)
Copy the code
  1. Is the function called in a context object (implicit binding)? If so, this binds to that context object.
var bar = obj1.foo()
Copy the code
  1. If neither, use the default binding. If in strict mode, it is bound to undefined, otherwise it is bound to a global object.
var bar = foo()
Copy the code

4. Bind exceptions

4.1 to ignore this

If the function does not care about this but needs to pass in a placeholder value, null can be used

foo.call( null )
Copy the code

However, always using NULL to ignore this binding can have some side effects. If a function does use this (such as a function in a third-party library), the default binding rule binds this to the global object (which in the browser is window).

To solve the above problems, we can use the following methods:

Pass in a special object that binds this to without any side effects to your program

function foo(a,b) { 
    console.log( "a:" + a + ", b:" + b ); 
} 
// Our DMZ empty object
var ø = Object.create( null ); 
// Expand the array to argumentsFoo. Apply (ø, [2.3]);// a:2, b:3 
/ / use the bind (..) Currying
varThe bar = foo. Bind (ø,2 ); 
bar( 3 ); // a:2, b:3
Copy the code

Using the variable name ø not only makes the function more “safe”, but also improves the readability of the code, since ø means “I want this to be empty”.

4.2 Indirect Reference

function foo() { 
    console.log( this.a ); 
}
var a = 2; 
var o = { a: 3.foo: foo }; 
var p = { a: 4 }; 
o.foo(); / / 3
(p.foo = o.foo)(); / / 2
Copy the code

The return value of the assignment expression p.foo = o.foo is a reference to the target function, so the call location is foo() instead of p.foo() or o.foo().

4.3 soft binding

If you can specify a value other than the global object and undefined for the default binding, you can achieve the same effect as the hard binding, while retaining the ability for implicit or explicit binding to modify this

if (!Function.prototype.softBind) { 
    Function.prototype.softBind = function(obj) { 
         var fn = this; 
         // Capture all curried parameters
        var curried = [].slice.call( arguments.1 ); 
        var bound = function() { 
            return fn.apply( 
                (!this || this= = = (window || global))? obj :this,
                curried.concat.apply( curried, arguments)); }; bound.prototype =Object.create( fn.prototype ); 
        return bound; 
    }; 
}
Copy the code

Let’s see if softBind implements soft binding:

function foo() { 
    console.log("name: " + this.name); 
} 
var obj = { name: "obj" }, 
    obj2 = { name: "obj2" }, 
    obj3 = { name: "obj3" }; 
var fooOBJ = foo.softBind( obj ); 
fooOBJ(); // name: obj 
obj2.foo = foo.softBind(obj); 
obj2.foo(); // name: obj2 <---- look!
fooOBJ.call( obj3 ); // name: obj3 <----!
setTimeout( obj2.foo, 10 ); 
// name: obj <---- The soft binding is applied
Copy the code

4.4 Arrow Function

Instead of using the four standard rules for this, the arrow function determines this based on the outer (functional or global) scope. The arrow function binding cannot be modified

Arrow functions have no Prototype and cannot be new. The arrow function does not have this, its this depends on the this of the outer function, if there is no outer function, it is global scope.

4.5 Bind a method to an event behavior of an element

A method is bound to an event behavior of an element, the event is fired, the method executes, and THIS in the method is usually the current element itself


btn.onclick = function anonymous() {
	console.log(this); / / = > element
};
// =>DOM2
btn.addEventListener('click'.function anonymous() {
	console.log(this);  / / = > element
}, false);
btn.attachEvent('onclick'.function anonymous(){
	// <= DOM2 event binding in Internet Explorer 8
	console.log(this); //=>window
});

function fn() {	
	console.log(this);
}
btn.onclick = fn.bind(window); //=>fn.bind(window) returns an anonymous function (AM) to bind the AM to the event; Clicking triggers AM execution. THIS in AM is the element, but FN is executed in AM. THIS in FN is the pre-specified WINDOW
Copy the code

5. Apply, calll, bind

if (!Function.prototype.bind) { 
    Function.prototype.bind = function(oThis) { 
        if (typeof this! = ="function") { 
             // The closest to ECMAScript 5
             // Internal IsCallable function
            throw new TypeError( 
             "Function.prototype.bind - what is trying to be bound is not callable"
            ); 
        } 
        var aArgs = Array.prototype.slice.call( arguments.1 ), 
            fToBind = this, 
            fNOP = function(){}, 
            fBound = function(){ 
                return fToBind.apply( 
                ( 
                    this instanceof fNOP && 
                    oThis ? this : oThis 
                ), 
                 aArgs.concat( 
                    Array.prototype.slice.call( arguments)); }; fNOP.prototype =this.prototype; 
        fBound.prototype = new fNOP(); 
        return fBound; 
    }; 
}
Copy the code

This bind (..) It’s a polyfill code.