It must be a function to use call, apply, bind, i.e. It must be preceded by a function, such as this in call, apply, and bind
Call, apply is a fn property that binds this(the function to which this refers, call, or apply) to context(this to which this refers) and context.fn()
Bind uses closures to pass this(the function to which this refers, the function before bind) to the returned function at execution time
- This. Appl changes the reference of this to the context.
- Es manual implementation: also and
The call, the apply
The sameAn FN property bound to the context(to point to this)
Then the context. Fn ()
call:fn.call(arg1, arg2, …) ;
The first parameter must be the object data type
Function. Prototype call => Function call(){[native code]}
Fn.call () : Executes the call method found through the prototype chain. When the call method executes, something is done internally
- We first change this in the function to the first argument passed by the call method
- Get the second and subsequent arguments to the call method
Executes the function to be operated on and passes the second argument passed in later (including the second) to the function
In non-strict mode, this refers to window if no argument is passed, or if null/undefined is passed first
In strict mode, the first argument passed is this, including null and undefined; Not passing this is undefined
Return value: The return value of the function called with the this value and arguments provided by the caller. If the method returns no value, undefined is returned.
/ / call the principlefn.call(arg1, arg2, ...) ;Function.prototype.call=function(){
let param1=arguments[0];
let paramOther=[] ; Argumentargument.getarguments (); // Getarguments ()
//=> This :fn in call is currently operating on a function (an instance of the function class)
//1、把fn(也就是call中的this)中的this关键字修改为param1 => 把this(call中的this)中的this关键字修改为param1
// Pass fn(this) to paramOther (the second and subsequent arguments)
//this(paramOther)
}
Copy the code
window.name='aaa';
let fn=function (){
console.log(this.name)
}
let obj={
name:'OBJ'.fn:fn
}
let oo={
name:'oo'
}
fn(); //this:window 'aaa'
obj.fn(); // this:obj 'OBJ'
oo.fn() / / an error
fn.call(oo) //this:oo 'oo'
fn.call(obj) //this:obj 'OBJ'
Copy the code
Implement Call manually to learn how it works
- Step 1: Set the function as a property of the object passed in
- Step 2: Execute the function
- Step 3: Delete this function
Function.prototype.myCall = function(context) {
// We need to get the function that calls call first. Previous function)
context.fn = this;
context.fn(); // Call a function on context. The function's this value is context.
delete context.fn;
}
Copy the code
There are two ways to accept arbitrary parameters:
-
Process arguments (native JS) to form a string like this eval(“fn(arguments[1],… ,arguments[n])”) and then execute with eval
-
Deconstruction operators (ES6)
call(target, ... args)Copy the code
Function.prototype.myCall = function (context, ... args) {
context = context ? Object(context) : window;
// Use this to get the method that called the current myCall and bind it to the context
context.fn = this
// Get the arguments passed in (from the arguments object)
constval = context.fn(... args);// Delete the methods added to the context
delete context.fn
return val
}
Copy the code
The final code
Function.prototype.myCall = function(context){
if(context === null || context === undefined){
context = window;
} else {
context = Object(context);
}
let arg = [];
let val ;
// I takes only the second and subsequent arguments starting from 1
for(let i = 1 ; i<arguments.length ; i++){
arg.push( 'arguments[' + i + '] '); }/ / execution after the args [" the arguments [1] ", "the arguments [2]", "the arguments [3]",...
context._fn_ = this;
val = eval( 'context._fn_(' + arg + ') ' )
delete context._fn_;
return val
}
Copy the code
The principle of the call. The call
A call is the function that precedes the call; Two or more calls cause the function in parentheses following call to execute, and if the parentheses following call are not functions, an error is reported
Call (); call(); call(); call(); call(); call(); call();
let sum=function(a,b){
console.log(this)}let sum2=function(a,b){
console.log(this)
console.log(555)}let opt={n:20}
sum.call(opt,20.30);
//sum this: opt a=20 b=30
//call this = sum; //call this = sum
sum.call.call(opt)
//1, sum. Call = Function. Prototype (' call ', 'call', 'apply', 'bind')
// select * from call (opt); // select * from call (opt); Sum. Call. Call is not A function
sum.call.call(sum2) //555 this:window
Prototype. Call => call.call(sum2)
// In the second call context is sum2, context._fn_ is call and finally sum2.call()
Copy the code
function fn1(){console.log(1)}
function fn2(){console.log(2)}
fn1.call(fn2); / / 1
// Find callAA and execute, this in callAA is fn1, this in fn1 is fn2, and fn1 is fn1
fn1.call.call(fn2); / / 2
Prototype. Call => call.call(fn2)
// In the second call, context is fn2, context._fn_ is call, and finally fn2.call()
//=> make this undefined in fn2, because fn1.call does not pass the parameter value,
// Then let fn2 execute
Function.prototype.call(fn1) //Function. Prototype () has no output
Function.prototype.call.call(fn1) //fn1() 1
Copy the code
Call (this,arg1,arg2…)
In a child constructor, you can implement inheritance by calling the call method of the parent constructor
In this example, the object instances created using the Food and Toy constructors will have the name and price attributes added to the Product constructor, but the category attributes are defined in the respective constructors
function Product(name,price) {
this.name=name;
this.price=price;
}
function Food(name,price) {
Product.call(this,name,price);
this.category='food';
}
function Toy(name,price) {
Product.call(this,name,price);
this.category='toy';
}
var cheese = new Food('aaa'.5)
var fun = new Toy('bbb'.20)
console.log(cheese.name); //aaa
console.log(fun.price); / / 20
console.log(cheese.name == fun.name); //false
console.log(cheese.category); //food
console.log(fun.category); //toy
console.log(cheese.category == fun.category); //false
Copy the code
Apply: Is basically the same as call, except that the parameters passed to fn (all the parameters after the first one) must be placed in an array
Apply passes the parameters that need to be passed to fn (all the parameters after the first one) in an array (or array-like array). Although it is written as an array, it is equivalent to passing fn one by one
Fn. Call (obj, 10, 20)
Fn. Apply (obj, 10, 20)
Implement Apply manually
Function.prototype.myApply = function(context,arr){
if(context === null || context === undefined){
context = window;
} else {
context = Object(obj);
}
let args = [];
let val ;
// I starts at 0 because arr is a set of parameters
for(let i = 0 ; i<arr.length ; i++){
args.push( 'arr[' + i + '] '); } context._fn_ =this;
val = eval( 'context._fn_(' + args + ') ' )
delete context._fn_;
return val
}
Copy the code
Bind: The syntax is the same as call, with the only difference: execute immediately or wait for the function that calls the method to execute
Fn.call (obj,10,20) changes this in fn and causes fn to execute immediately
Fn. bind(obj,10,20) changes this in fn, fn does not execute, must manually call fn, fn will execute
The bind() method creates a new function. When the new function is called, the first argument to bind() will be this when it runs, and the subsequent sequence of arguments will be passed as its arguments before the arguments passed
The first argument to bind is a function that’s executed when it’s called and it’s the first argument to bind
Bind returns a function
The function is executed using the given this- Bind can accept arguments, bind the arguments to boundF with a closure, and combine the arguments boundF accepts (boundF is the function returned).
Note: Return a function that can be new. If new is used, this should not refer to the given this, because the instance will have problems if the this of new can be changed.
var module = {
x: 42
}
var unboundGetX = function() {
return this.x;
}
var boundGetX = unboundGetX.bind(module);
console.log(boundGetX()); / / 42
Copy the code
function fn(a,b){
console.log(this,a,b)
}
var obj={name:'aaa'}
document.onclick=fn; // bind fn to the click event and click execute
document.onclick=fn(); // Fn is executed first, and the return value of the execution is bound to the event. Undefined is executed when clicked
Copy the code
Implement bind manually
Call with this and return functions with call/apply
Bind can accept two arguments: 1. 2. Parameter passing during execution
You need to pass in the parameters from both calls
/ / the first edition
Function.prototype.myBind = function (context) {
var self = this;
// Get myBind from the second argument to the last argument
var args = Array.prototype.slice.call(arguments.1);
return function () {
// Arguments refer to the function arguments that bind returns.
var bindArgs = Array.prototype.slice.call(arguments);
returnself.apply(context, args.concat(bindArgs)); }}Copy the code
Deal with the new case
The sample
var value = 2;
var foo = {
value: 1
};
function bar(name, age) {
this.habit = 'shopping';
console.log(this.value);
console.log(name);
console.log(age);
}
bar.prototype.friend = 'kevin';
var bindFoo = bar.bind(foo, 'daisy');
var obj = new bindFoo('18');
// undefined
// daisy
/ / 18
console.log(obj.habit);
console.log(obj.friend);
// shopping
// kevin
Copy the code
The performance of the new
- This points to the new object boundF is constructing
- BoundF’s Prototype is on the new object’s prototype chain
solution
- Return this to the new object. FBound is on the new object’s prototype chain
- Make sure that the new prototype is consistent with the original function and not with fBound by attaching the original function’s prototype to fBound’s prototype
/ / the second edition
Function.prototype.bind2 = function (context) {
var self = this;
var args = Array.prototype.slice.call(arguments.1);
var fBound = function () {
var bindArgs = Array.prototype.slice.call(arguments);
// When used as a constructor, this points to the instance, and the result is true. This points to the instance to get the value from the binding function
// This instanceof fBound? Null: context ', the instance is just an empty object, change null to this, the instance will have habit attribute
// When this refers to the window as a normal function, the result is false, and bind the function's this to context
return self.apply(this instanceof fBound ? this : context, args.concat(bindArgs));
}
// Change the return function's prototype to the binding function's prototype, and the instance inherits the value from the binding function's prototype
fBound.prototype = this.prototype;
return fBound;
}
Copy the code
Use Apply for the final implementation
Function.prototype.myBind = function (context) {
// this refers to the original function
var that = this
// Take the arguments to bind and construct the closure, since arguments will be overridden in boundF
var args = Array.prototype.slice.apply(arguments.1)
// The above operation corresponds to the second and subsequent arguments to arguments.slice(1)
var boundF = function () {
// Arguments are arguments to boundF
var bindArgs = Array.prototype.slice.call(arguments)
// There are two types of this:
// boundF (normal call)
// newObj (new)
// Note that boundF instanceof boundF is false
return that.apply(this instanceof boundF ? this : context, args.concat(bindArgs))
}
// Toggle the prototype chain
boundF.prototype = Object.create(this)
return boundF
}
Copy the code
ES6
Function.prototype.myBind = function(context,... arg1){
return (. arg2) = > {
let args = arg1.concat(arg2);
let val ;
context._fn_ = this; val = context._fn_( ... args );delete context._fn_;
return val
}
}
Copy the code
ES5
Function.prototype.myBind = function(context){
if(context === null || context === undefined){
context = window;
} else {
context = Object(context);
}
let _this = this;
let argArr = [];
for(let i = 1 ; i<arguments.length ; i++){
argArr.push( 'arg1[' + (i - 1) + '] '); }return function(){
let val ;
for(let i = 0 ; i<arguments.length ; i++){
argArr.push( 'arguments[' + i + '] '); } context._fn_ = _this;console.log(argArr);
val = eval( 'context._fn_(' + argArr + ') ');delete context._fn_;
return val
};
}
Copy the code
Gets the maximum value in the array
let arr=[12.13.14.15.16.23.24.13.12.15]
//=> Method 1: Sort from largest to smallest, and select the first one
let max=arr.sort(function(a,b){
returnb-a; }) [0];
//=> Method 2: Iterate over the ratio
let max=arr[0]
for(var i=1; i<arr.length; i++){if(arr[i]>max){ max=arr[i]; }}// Eval, math.max; Math.max can't pass arrays in directly; Mosaic "Math. Max (12) which... "
let str="Math.max("+arr.toString()+")";
eval(str);
// The parameters that need to be passed to fn are passed to fn in an array (or an array of classes) by apply
Math.max.apply(null,arr)
// Method 5: use the ES6 expansion operator and math.max
Math.max(... arr)Copy the code
Reference: github.com/mqyqingfeng…