"You do not know JavaScript (on)," notes --this comprehensive analysis

It must first understand the call location: call position is the position (rather than a statement of position) function in your code is called.

The most important thing is to analyze the call stack (that is, in order to reach all functions called by the current execution position). We are concerned in a position to call before calling the function currently being executed.

function baz() {
    // 当前调用栈是: baz
    // 因此, 当前调用位置是全局作用域
    
    console.log( "baz" );
    bar(); // <-- bar 的调用位置
}

function bar() {
    // 当前调用栈是 baz -> bar
    // 因此, 当前调用位置在 baz 中
    
    console.log( "bar" );
    foo(); // <-- foo 的调用位置
}

function foo() {
    // 当前调用栈是 baz -> bar -> foo
    // 因此, 当前调用位置在 bar 中
    
    console.log( "foo" );
} 
baz(); // <-- baz 的调用位置
Binding rules
  1. Default binding.

    The most common function call: separate function call. This rule can be seen as a default rule can not be applied when the other rules.

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

If you are using strict mode (strict mode), then the global object will not use the default bindings, so this will be bound to undefined:

function foo() {
    "use strict";
    console.log( this.a );
}
var a = 2;
foo(); // TypeError: this is undefined
  1. Implicit Bind

    Another rule to consider is whether there is a position to call the context object, or whether an object is owned or contains, but this statement may cause some misleading.

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

Note that the first foo () statement mode, and then how it is used as a reference attribute to the obj. But either directly defined or define the obj add a reference attribute, this function is not strictly speaking part of the object obj.
However, the location will use to call obj context to refer to function, so you can say that when the function is called object obj "owns" or "contain" it.

When the function references have context object, the implicit this binding rule will function call is bound to the context object.

Implicit loss : One of the most common problem is that this binding is implicit binding function of binding object will be lost, that it will apply the default bindings, so this is bound to put the global object or undefined, depending on whether it is strict mode.

function foo() {
    console.log( this.a );
}
var obj = {
    a: 2,
    foo: foo
};
var bar = obj.foo; // 函数别名!
var a = "oops, global"; // a 是全局对象的属性
bar(); // "oops, global

Although the bar is obj.foo a reference, but in fact, it refers to is itself a function foo, so this time the bar () is a fact without any modification of function calls, so the application of the default bindings.

A more subtle, more common and more unexpected situation occurs when an incoming callback:

function foo() {
    console.log( this.a );
}
function doFoo(fn) {
    // fn 其实引用的是 foo
    fn(); // <-- 调用位置!
}
var obj = {
    a: 2,
    foo: foo
};
var a = "oops, global"; // a 是全局对象的属性
doFoo( obj.foo ); // "oops, global"

Parameter passing is actually an implicit assignment, therefore will be bound implicit assignment, the callback function is lost when we pass this function is very common.

  1. Explicit binding

    Like call, apply, bind these three methods can be specified directly bound object of this, we call it explicitly binding.

* If you pass a primitive value (string type, Boolean type or digital type) as a binding object to this, and the original value will be converted to the form of its objects (that is, new String (..), new Boolean (..) or a new Number (..)). This is often referred to as "packing."

  1. new bindings

    JavaScript in new mechanisms and class-oriented language is actually quite different.

In JavaScript, are just a few constructor function is called when the new operator. They do not belong to a class, it will not instantiate a class. In fact, they can not even be said to be a special function type, they are just ordinary function to be called the new operator only.

Use new to call a function, or event of constructor calls, automatically performs the following operations:

  1. Creating (or construct) a new object.
  2. This new object will be executed [[prototype]] connection.
  3. This new object will be bound to this function call.
  4. If the function does not return other objects, then the function call new expression will automatically return to the new object.
function foo(a) {
    this.a = a;
}
var bar = new foo(2);
console.log( bar.a ); // 2
Judge this

Now we can be judged according to the priority application function calls in a position which rule. It can be determined in the following order:

  1. Whether the function call (new binding) in new in? If so this is bound to the newly created object.
    var bar = new foo ()
  2. Whether by function call, apply (explicit binding) or a hard-bound call? If so, this is bound to
    the specified object.
    var bar = foo.call (obj2)
  3. Whether the function call (implicit binding) in the context of an object? If so, this is binding on that
    below target.
    var bar = obj1.foo ()
  4. If not, then use the default bindings. If strict mode, it is bound to undefined, otherwise bound to the
    global object.
    var bar = foo ()
Binding Exception

In some scenarios this unexpected binding behavior, when you think should apply to other binding rules, the application may in fact be the default binding rules.

  • If you put null or undefined incoming call, apply or bind as the binding target of this, these values ​​are ignored when you call, the practical application of the default binding rules:
function foo() {
    console.log( this.a );
} 
var a = 2;
foo.call( null ); // 2

So under what circumstances would you pass null it?

A very common practice to use apply (..) to "expand" an array and passed as an argument to a function. Similarly, the bind (..) can be curried (pre-set parameters) parameters, this method may be useful:

function foo(a,b) {
    console.log( "a:" + a + ", b:" + b );
} 
// 把数组“展开” 成参数
foo.apply( null, [2, 3] ); // a:2, b:3
// 使用 bind(..) 进行柯里化
var bar = foo.bind( null, 2 );
bar( 3 ); // a:2, b:3

Both methods need to pass a parameter as a binding target of this. If the function does not care about this, you still need to pass a placeholder value, then null might be a good choice, as shown as code.

However, always use null to ignore this binding might produce some side effects. If a function do use this (such as a third-party library function), the default binding rules that will bind to this global object (the object in the browser is a window), which will lead to unpredictable consequences (such as modifying the global object).

If we always pass a DMZ object while ignoring this binding, then do not worry about anything, because any object in the air, will not have any impact on the global object for this use will be restricted.

Create an empty object easiest way in JavaScript are Object.create (null), Object.create (null) {} and the like, but does not create Object.prototype this commission, so it is more than {} "more empty ":

function foo(a,b) {
console.log( "a:" + a + ", b:" + b );
} 
// 我们的 DMZ 空对象
var ø = Object.create( null );
// 把数组展开成参数
foo.apply( ø, [2, 3] ); // a:2, b:3
// 使用 bind(..) 进行柯里化
var bar = foo.bind( ø, 2 );
bar( 3 ); // a:2, b:3
  • Indirect application

Another thing to note is that you may (intentionally or unintentionally) to create a function of "indirect references" in this case, calling this function will apply the default binding rules.

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

Assignment expression p.foo = o.foo return value is a reference to the objective function, so the position is to call foo () instead p.foo () or o.foo (). According to we have said before, this will apply the default bindings.

Note: For default binding, the decision is not binding object calls this position is in strict mode, but the body of the function is in strict mode. If the function body is in strict mode, this will be bound to undefined, otherwise this will be bound to the global object.

Soft binding

Ultimate feeling about this art, the cause of hard binding function of flexibility will be greatly reduced, after using a hard binding can not use implicit or explicit binding bind to modify this.
If a binding specifies a default value and undefined other than the global object, and it can be hard to achieve the same effect binding, while retaining the ability to bind an implicit or explicit binding of this modification. This is the soft binding.

To achieve the following:

if (!Function.prototype.softBind) {
    Function.prototype.softBind = function(obj) {
        var fn = this; // fn就是调用的函数
        
        // 捕获所有 curried 参数
        var curried = [].slice.call( arguments, 1 ); 
        
        var bound = function() {
            return fn.apply(
                (!this || this === (window || global)) ?
                    obj : this
                curried.concat.apply( curried, arguments ) //这里的argements是bound的arguments,也就是说在softBind的时候可以传参一次,后面可以再传一次,参数会在这里合并起来
            );
        };
        bound.prototype = Object.create( fn.prototype ); // 原型链继承过来
        return bound;
    };
}

First check this time of call, if this is bound to the global object or undefined, then the specified default object obj is bound to this, otherwise they would not modify this.

Scenario:

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 <---- 看! ! !

fooOBJ.call( obj3 ); // name: obj3 <---- 看!

setTimeout( obj2.foo, 10 );
// name: obj <---- 应用了软绑定

It can be seen soft binding version of foo () can be manually bound to this obj2 or obj3, but if you apply the default bindings will bind this to obj.

Guess you like

Origin www.cnblogs.com/simpul/p/11027207.html