Before you get into this, you need to know something about the this binding. The this binding can be subdivided into default, implicit, explicit (hard), and new bindings. Today we’re going to focus on the implementation details of bind in hard binding and how you can modify BIND to make it more flexible (called soft binding here)
The binding rule for this
Each function’s this is bound at call time, depending entirely on where the function is called (that is, how the function is called).
The default binding
When a function is run, the function is called without any embellishment, and the default rule when no other rules can be applied is the default binding. Under strict mode, the default binding is bound to undefined.
function foo() {
console.log( this.a );
}
var a = 2;
foo(); / / 2
function foo() {
"use strict";
console.log( this.a );
}
var a = 2;
foo(); // TypeError: this is undefined
Copy the code
There is a subtle but very important detail here, although the binding rules for this depend entirely on where it is called, the default binding can only be bound to a global object if foo() is not running in strict mode; Strictly independent of where foo() is called.
function foo() {
console.log( this.a );
}
var a = 2;
(function(){
"use strict";
foo(); / / 2}) ();Copy the code
Implicit binding
The implicit binding rule refers to whether the calling location has a context object, or whether it is owned or contained by an object.
function foo() {
console.log( this.a );
}
var obj = { a: 2.foo: foo };
obj.foo(); / / 2
Copy the code
The call location references the function using the OBJ context, so you can say that the obJ object “holds” or “contains” the function when it is called. No matter what you call this pattern, when foo() is called, its foothold does point to an obj object. When a function references a context object, the implicit binding rule binds this in the function call to that context object. Because this is bound to obj when foo() is called, this.a and obj.a are the same.
According to the binding
You can use the call(..) of the function And the apply (..) Methods pass in a binding object, which they bind to this, which they then specify when calling the function. Because you can specify the binding object for this directly, we call it an explicit binding.
function foo() {
console.log( this.a );
}
var obj = { a:2 };
foo.call( obj ); / / 2
Copy the code
If you pass a primitive value (String, Boolean, or number) as a binding object to this, the primitive value will be converted to its object form (i.e., new String(..)). , new Boolean (..) Or the new Number (..) ). This is often referred to as “boxing”.
The new binding
When a function is called with new, or when a constructor call occurs, the following operations are performed automatically.
- Create (or construct) a brand new object.
- The new object will be connected by the implementation [[prototype]].
- This new object is bound to the function call’s this.
- If the function returns no other object, the function call in the new expression automatically returns the new object.
function foo(a) {
this.a = a;
}
var bar = new foo(2);
console.log( bar.a ); / / 2
Copy the code
Call foo(..) with new , we construct a new object and bind it to foo(..). Call to this. New is the last way to influence the behavior of the this binding when a function is called, and we call it the new binding.
Bind code implementation
if (!Function.prototype.bind) {
Function.prototype.bind = function(oThis) {
if (typeof this! = ="function") {
// The internal IsCallable function closest to ECMAScript 5
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();
returnfBound; }}Copy the code
Here’s an interesting point:
this instanceof fNOP && oThis ? this : oThis // … And: fNOP. Prototype = this.prototype; fBound.prototype = new fNOP();
In simple terms, this code determines if the hard-bound function is called by new, and if so, it replaces the hard-bound this with the newly created this. In code:
var obj = {val: 123}
function foo(value) {
if(value) this.val = value;
console.log(this.val)
}
var bar = foo.bind(obj);
bar() / / 123
var baz = new bar("p2");
baz.val; // p2
// Prototype link
baz.__proto__ === bar.prototype === fBound.prototype === new fNOP() === fNOP.prototype
Copy the code
Soft binding
As we’ve seen before, hard binding is a way to force this to the specified object (except when using new), preventing function calls from applying the default binding rules. The problem is that hard binding greatly reduces the flexibility of the function, making it impossible to modify this using either implicit or explicit binding. If you can give the default binding a value other than a global object and undefined, you can achieve the same effect as hard binding, while retaining the ability of implicit or explicit binding to modify this. The desired effect can be achieved through a method called soft binding:
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
If this is bound to a global object or undefined, the specified default object obj is bound to this. Otherwise, this is not modified.
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 ); // obj3 <----
setTimeout( obj2.foo, 10 ); // name: obj <---- Soft binding is applied
Copy the code
As you can see, the soft-bound version of foo() can manually bind this to obj2 or obj3, but if the default binding is applied, this will be bound to obj.
This is the end, I hope to help you some.
Reference: javaScript you Didn’t Know