Call,apply, and bind

grammar

fun.call(thisArg, param1, param2, ...) fun.apply(thisArg, [param1,param2,...] ) fun.bind(thisArg, param1, param2, ...)Copy the code

parameter

ThisArg (optional) :

  • Fun’s this points to thisArg object
  • In non-strict mode: thisArg is specified as null, undefined, and this in fun points to the window object.
  • This with a primitive value (Number, String, Boolean) points to an auto-wrapped object for that primitive value, such as String, Number, Boolean

Param1,param2(Optional): parameters passed to fun.

  • If param is not passed or null/undefined, no arguments need to be passed.
  • The second argument to apply is an array, and the values in the array are the arguments passed to Fun.

The call to call/apply/bind must be a function

Call, apply, and bind are the three methods that hang on the Function object; only functions have these methods.

As long as is the function, such as: Object. The prototype. ToString is a function, we often see such usage: Object. The prototype. ToString. Call (data).

role

Changing the “this” direction of function execution is the basis for all of their use so far.

The return value

  • Call /apply returns the execution result of fun
  • Bind returns a copy of fun, specifying fun’s this and saving fun’s arguments

Call () and the apply ()

The call() method calls a function with a specified this value and separately supplied arguments (a list of arguments).

The difference between call() and apply() is that the call() method accepts a list of arguments, while the apply() method accepts an array of arguments.

var func = function(arg1, arg2) {... }; func.call(this, arg1, arg2); // Use call, argument list
func.apply(this, [arg1, arg2]) // Use the apply parameter array
Copy the code

Usage scenarios

1. Determine the data type

function isType(data, type) {
    const typeObj = {
        '[object String]': 'string'.'[object Number]': 'number'.'[object Boolean]': 'boolean'.'[object Null]': 'null'.'[object Undefined]': 'undefined'.'[object Object]': 'object'.'[object Array]': 'array'.'[object Function]': 'function'.'[object Date]': 'date'.// Object.prototype.toString.call(new Date())
        '[object RegExp]': 'regExp'.'[object Map]': 'map'.'[object Set]': 'set'.'[object HTMLDivElement]': 'dom'.// document.querySelector('#app')
        '[object WeakMap]': 'weakMap'.'[object Window]': 'window'.// Object.prototype.toString.call(window)
        '[object Error]': 'error'.// new Error('1')
        '[object Arguments]': 'arguments',}let name = Object.prototype.toString.call(data) / / use the Object. The prototype. The toString () to get the data type
    let typeName = typeObj[name] || 'Unknown type' // Match the data type
    return typeName === type // Determine if the data type is an incoming type
}
console.log(
    isType({}, 'object'), // true
    isType([], 'array'), // true
    isType(new Date(), 'object'), // false
    isType(new Date(), 'date'), // true
)
Copy the code

2. Array-like objects use Array methods

var domNodes = document.getElementsByTagName("*");
domNodes.unshift("h1");
// TypeError: domNodes.unshift is not a function

var domNodeArrays = Array.prototype.slice.call(domNodes);
domNodeArrays.unshift("h1"); // 505 Data varies depending on the environment
// (505) ["h1", html.gr__hujiang_com, head, meta, ...] 
Copy the code

Array-like objects have the following two properties:

  • Has: numeric index subscript to object element and length attribute
  • Does not have: Methods that array objects such as push, Shift, forEach, and indexOf have

To be clear, an array-like object is an object. The arguments object and the NodeList object returned by the DOM API are array-like objects. Array-like objects cannot use array methods such as push, pop, shift, and unshift. Through the Array. The prototype. Slice. Call into a true Array, you can use the Array of all the methods.

Other ways to convert an array-like object to an array:

// The above code is equivalent to
var arr = [].slice.call(arguments);ES6:
// array.from () can turn two types of objects into true arrays: array-like objects and iterable objects (including the new data structures Set and Map in ES6).
let arr = Array.from(arguments);
let arr = [...arguments];
Copy the code

PS extension: why through the Array. The prototype. Slice. The call () the class Array object can be turned into an Array?

The following code is MDN’s Polyfill for slice

Array.prototype.slice = function(begin, end) {
      end = (typeofend ! = ='undefined')? end :this.length;

      // For array like object we handle it ourselves.
      var i, cloned = [],
        size, len = this.length;

      // Handle negative value for "begin"
      var start = begin || 0;
      start = (start >= 0)? start :Math.max(0, len + start);

      // Handle negative value for "end"
      var upTo = (typeof end == 'number')?Math.min(end, len) : len;
      if (end < 0) {
        upTo = len + end;
      }

      // Actual expected size of the slice
      size = upTo - start;

      if (size > 0) {
        cloned = new Array(size);
        if (this.charAt) {
          for (i = 0; i < size; i++) {
            cloned[i] = this.charAt(start + i); }}else {
          for (i = 0; i < size; i++) {
            cloned[i] = this[start + i]; }}}return cloned;
    };
  }
Copy the code

PS extension 2: through the Array. The prototype. Slice. The call () enough? What’s the problem?

Under the low version of Internet explorer does not support through the Array. The prototype. Slice. The call (args) transform class Array object into an Array, because low version of Internet explorer (IE < 9) under the DOM object is implemented in the form of com objects, js object with the com object can not convert.

function toArray(nodes){
    try {
        // works in every browser except IE
        return Array.prototype.slice.call(nodes);
    } catch(err) {
        // Fails in IE < 9
        var arr = [],
            length = nodes.length;
        for(var i = 0; i < length; i++){
            // arr.push(nodes[i]); // Either is fine
            arr[i] = nodes[i];
        }
        returnarr; }}Copy the code

3. Get the maximum and minimum values in the array

var numbers = [5.458 , 120 , -215 ]; 
Math.max.apply(Math, numbers);   / / 458
Math.max.call(Math.5.458 , 120 , -215); / / 458

// ES6
Math.max.call(Math. numbers);/ / 458
Math.max(... numbers);/ / 458
Copy the code

4. Invoke the parent constructor to implement inheritance

function  SuperType(){
    this.color=["red"."green"."blue"];
}
function  SubType(){
    // Core code, inherited from SuperType
    SuperType.call(this);
}

var instance1 = new SubType();
instance1.color.push("black");
console.log(instance1.color);
// ["red", "green", "blue", "black"]

var instance2 = new SubType();
console.log(instance2.color);
// ["red", "green", "blue"]
Copy the code

In the child constructor, inheritance is achieved by calling the call method of the parent constructor, so that each instance of SubType makes a copy of the properties in SuperType.

Disadvantages:

  • Only instance properties and methods of the parent class can be inherited, not stereotype properties/methods
  • Unable to reuse, each subclass has a copy of the parent class instance function, affecting performance

bind()

The bind() method creates a new function. When the new function is called, its this value is the first argument passed to Bind (), and the second and subsequent arguments passed to bind, plus arguments from the bind function itself, are called as arguments to the original function in that order.

Fun. bind(thisArg, [, arg1[, arg2[,…]]])

bindThe main difference between call/apply and call/apply is that the former returns a function in the binding context, while the latter executes the function directly.

Examples:

var value = 2;

var foo = {
    value: 1
};

function bar(name, age) {
    return {
		value: this.value,
		name: name,
		age: age
    }
};

bar.call(foo, "Jack".20); // Execute the function directly
// {value: 1, name: "Jack", age: 20}

var bindFoo1 = bar.bind(foo, "Jack".20); // Return a function
bindFoo1();
// {value: 1, name: "Jack", age: 20}

var bindFoo2 = bar.bind(foo, "Jack"); // Return a function
bindFoo2(20);
// {value: 1, name: "Jack", age: 20}
Copy the code

Bind ️ has the following features:

  • You can specify this
  • Return a function
  • You can pass in parameters
  • Currie,

Usage scenarios

1. Service scenarios

There are often the following business scenarios

var nickname = "Kitty";
function Person(name){
    this.nickname = name;
    this.distractedGreeting = function() {

        setTimeout(function(){
            console.log("Hello, my name is " + this.nickname);
        }, 500); }}var person = new Person('jawil');
person.distractedGreeting();
//Hello, my name is Kitty
Copy the code

The nickname is global, not the parameter we passed when we created person, because setTimeout is executed in the global environment, so this points to the window. It is the same here to replace setTimeout with an asynchronous callback, such as an interface request callback.

Solutions are as follows:

Solution 1: Cache the this value

var nickname = "Kitty";
function Person(name){
    this.nickname = name;
    this.distractedGreeting = function() {
        
		var self = this; // Cache this value
        setTimeout(function(){
            console.log("Hello, my name is " + self.nickname); // changed
        }, 500); }}var person = new Person('jawil');
person.distractedGreeting();
// Hello, my name is jawil
Copy the code

Solution 2: Use bind

var nickname = "Kitty";
function Person(name){
    this.nickname = name;
    this.distractedGreeting = function() {

        setTimeout(function(){
            console.log("Hello, my name is " + this.nickname);
        }.bind(this), 500); / / use the bind}}var person = new Person('jawil');
person.distractedGreeting();
// Hello, my name is jawil
Copy the code

2. Curry

Call a function by passing it only a few arguments and have it return a function to process the rest.

You can call the coriolization function once, or you can call it multiple times with one argument at a time.

var add = function(x) {
  return function(y) {
    return x + y;
  };
};

var increment = add(1);
var addTen = add(10);

increment(2);
/ / 3

addTen(2);
/ / 12

add(1) (2);
/ / 3
Copy the code

The above code defines an Add function that takes overnight arguments and returns a new function. After you call Add, the returned function remembers the first argument to add in the form of a closure. So bind itself is also a use case for closures.

Topic:

Implement an infinite accumulation function add with JS as shown in the following example

add(1); // 1 add(1)(2); / / 3 add (1) (2) (3); / / 6 add (1) (2) (3) (4); // 10 // etcCopy the code
function add(a){
    function sum(b){
        a = a + b;
        return sum;
    }
    sum.toString = function(){
        return a;
    }
    return sum;
}
Copy the code

Reference:

Github.com/yygmind/blo… Juejin. Cn/post / 684490…