关于call、apply、bind的一些零零散散

95 阅读4分钟
  • call、apply、bind相关

    • apply

      • 定义:apply(thisArg)apply(thisArg, argsArray) 【apply() 方法会以给定的 this 值和作为数组(或类数组对象)提供的 arguments 调用该函数。】
      • 参数说明:
        • thisArg:The value of this provided for the call to func. If the function is not in strict mode, null and undefined will be replaced with the global object, and primitive values will be converted to objects.【调用 func 时提供的 this 值。如果函数不处于严格模式,则 null 和 undefined 会被替换为全局对象,原始值会被转换为对象。】
        • argsArray:An array-like object, specifying the arguments with which func should be called, or null or undefined if no arguments should be provided to the function.【一个类数组对象,用于指定调用 func 时的参数,或者如果不需要向函数提供参数,则为 null 或 undefined。】
      • 返回值:使用指定的 this 值和参数调用函数的结果。
      • 说明:传入的必须是个数组或者类数组
      • 示例:
          /*********** Base ***********/
          const numbers = [5, 6, 2, 3, 7];
          const max = Math.max.apply(null, numbers);
          console.log(max);
          // Expected output: 7
          const min = Math.min.apply(null, numbers);
          console.log(min);
          // Expected output: 2
      
          /*********** Base ***********/
          const array = ["a", "b"];
          const elements = [0, 1, 2];
          array.push.apply(array, elements);
          console.info(array); // ["a", "b", 0, 1, 2]
      
          /*********** Base ***********/
          // 数组中的最小/最大值
          const numbers = [5, 6, 2, 3, 7];
          // 用 apply 调用 Math.min/Math.max
          let max = Math.max.apply(null, numbers);
          // 这等价于 Math.max(numbers[0], …) 或 Math.max(5, 6, …)
          let min = Math.min.apply(null, numbers);
      
          /*********** Base ***********/
          var a = {
              name : "Cherry",
              func1: function () {
                  console.log(this.name)
              },
              func2: function () {
                  setTimeout(  function () {
                      this.func1()
                  }.apply(a),100);
              }
          };
          a.func2() // "Cherry"
      
    • call

      • 定义:call(thisArg)call(thisArg, arg1, arg2, /* …, */ argN) 【call() 方法会以给定的 this 值和逐个提供的参数调用该函数】
      • 参数说明:
        • thisArg:The value to use as this when calling func. If the function is not in strict mode, null and undefined will be replaced with the global object, and primitive values will be converted to objects.【在调用 func 时要使用的 this 值。如果函数不在严格模式下,null 和 undefined 将被替换为全局对象,并且原始值将被转换为对象。】
        • argsArray:Arguments for the function.【函数的参数。】
      • 返回值:使用指定的 this 值和参数调用函数后的结果。
      • 说明:传入的必须是单个单个元素。
      • 示例:
          /*********** Base ***********/
          function Product(name, price) {
              this.name = name;
              this.price = price;
          }
          function Food(name, price) {
              Product.call(this, name, price);
              this.category = 'food';
          }
          console.log(new Food('cheese', 5).name);
          // Expected output: "cheese"
      
          /*********** Base ***********/
          function greet() {
              console.log(this.animal, "的睡眠时间一般在", this.sleepDuration, "之间");
          }
          const obj = {
              animal: "猫",
              sleepDuration: "12 到 16 小时",
          };
          greet.call(obj); // 猫 的睡眠时间一般在 12 到 16 小时 之间
      
          /*********** Base ***********/
          globalThis.globProp = "Wisen";
          function display() {
              console.log(`globProp 的值是 ${this.globProp}`);
          }
          display.call(); // 输出“globProp 的值是 Wisen”
      
          /*********** super ***********/
          const unboundSlice = Array.prototype.slice;
          const slice = Function.prototype.call.bind(unboundSlice);
          // ...
          slice(arguments);
          // 以下代码是错误的,必要要经过上述处理
          const slice = Array.prototype.slice;
          slice.call(arguments);
          // 这里思考一个问题,为什么要这么处理?【不能将 slice.call 保存并将其作为普通函数调用,因为 call() 方法也会读取它的 this 值,而这个值应该是它要调用的函数。在这种情况下,你可以使用 bind() 来绑定 call() 的 this 值。在下面的代码片段中,slice() 是一个绑定了 this 值为 Array.prototype.slice() 的 Function.prototype.call() 的版本。这意味着额外的 call() 调用可以被省略】
      
    • bind

      • 定义:bind(thisArg)bind(thisArg, arg1, arg2, /* …, */ argN) 【bind() 方法创建一个新函数,当调用该新函数时,它会调用原始函数并将其 this 关键字设置为给定的值,同时,还可以传入一系列指定的参数,这些参数会插入到调用新函数时传入的参数的前面】

      • 参数说明:

        • thisArg:The value to be passed as the this parameter to the target function func when the bound function is called. If the function is not in strict mode, null and undefined will be replaced with the global object, and primitive values will be converted to objects. The value is ignored if the bound function is constructed using the new operator.【在调用绑定函数时,作为 this 参数传入目标函数 func 的值。如果函数不在严格模式下,null 和 undefined 会被替换为全局对象,并且原始值会被转换为对象。如果使用 new 运算符构造绑定函数,则忽略该值。】
        • argsArray:Arguments to prepend to arguments provided to the bound function when invoking func.【在调用 func 时,插入到传入绑定函数的参数前的参数。】
      • 返回值:使用指定的 this 值和初始参数(如果提供)创建的给定函数的副本。

      • 说明:

        • 传入的必须是单个单个元素。bind前置插入的参数个数必须小于等于函数入参的个数,多余,则多余部分会被忽略。
        • bind() 函数创建一个新的绑定函数(bound function)。调用绑定函数通常会执行其所包装的函数,也称为目标函数(target function)。绑定函数将绑定时传入的参数(包括 this 的值和前几个参数)提前存储为其内部状态。而不是在实际调用时传入。通常情况下,你可以将 const boundFn = fn.bind(thisArg, arg1, arg2) 和 const boundFn = (...restArgs) => fn.call(thisArg, arg1, arg2, ...restArgs) 构建的绑定函数的调用效果视为等效(但就构建 boundFn 的过程而言,不是二者等效的)。

        绑定函数可以通过调用 boundFn.bind(thisArg, /* more args */) 进一步进行绑定,从而创建另一个绑定函数 boundFn2。新绑定的 thisArg 值会被忽略,因为 boundFn2 的目标函数是 boundFn,而 boundFn 已经有一个绑定的 this 值了。当调用 boundFn2 时,它会调用 boundFn,而 boundFn 又会调用 fn。fn 最终接收到的参数按顺序为:boundFn 绑定的参数、boundFn2 绑定的参数,以及 boundFn2 接收到的参数。

        • 如果目标函数是可构造的,绑定函数也可以使用 new 运算符进行构造。这样做的效果就好像目标函数本身被构造一样。前置的参数会像通常一样传递给目标函数,而提供的 this 值会被忽略(因为构造函数会准备自己的 this,如 Reflect.construct 的参数所示)。如果直接构造绑定函数,new.target 将指向目标函数(也就是说,绑定函数对 new.target 是透明的)
      • 示例:

          /*********** Base ***********/
          const module = {
              x: 42,
              getX: function () {
                  return this.x;
              },
          };
          const unboundGetX = module.getX;
          console.log(unboundGetX()); // The function gets invoked at the global scope
          // Expected output: undefined
          const boundGetX = unboundGetX.bind(module);
          console.log(boundGetX());
          // Expected output: 42
      
          /*********** Base ***********/
          // bind是创建新的函数,必须要手动调用,而apply 和 call 则不需要手动调用
          var a ={
              name : "Cherry",
              fn : function (a,b) {
                  console.log( a + b)
              }
          }
          var b = a.fn;
          b.bind(a,1,2)() // 3
      
          /*********** Base ***********/
          function list(...args) {
              return args;
          }
          function addArguments(arg1, arg2) {
              return arg1 + arg2;
          }
          console.log(list(1, 2, 3)); // [1, 2, 3]
          console.log(addArguments(1, 2)); // 3
          // 创建一个带有预设前导参数的函数
          const leadingThirtySevenList = list.bind(null, 37);
          // 创建一个带有预设第一个参数的函数。
          const addThirtySeven = addArguments.bind(null, 37);
          console.log(leadingThirtySevenList()); // [37]
          console.log(leadingThirtySevenList(1, 2, 3)); // [37, 1, 2, 3]
          console.log(addThirtySeven(5)); // 42
          console.log(addThirtySeven(5, 10)); // 42 【【【这里得注意函数addArguments只能接受两个参数,bind时已经前置传入了一个参数,执行时只能在接受一个,所以多余的10这个参数会被忽略】】】
      
          /*********** Base ***********/
          // 在默认情况下,在 setTimeout() 内部,this 关键字将被设置为 globalThis,在浏览器中它是 window 对象。当处理需要将 this 引用类实例的类方法时,你可以显式地将 this 绑定到回调函数,以便保持实例的引用。
          class LateBloomer {
              constructor() {
                  this.petalCount = Math.floor(Math.random() * 12) + 1;
              }
              bloom() {
                  setTimeout(this.declare.bind(this), 1000);
              }
              declare() {
                  console.log(`I am a beautiful flower with ${this.petalCount} petals!`);
              }
          }
          const flower = new LateBloomer();
          flower.bloom();//1 秒后调用“flower.declare()”
      
          // and also you can use arrow function to do this problem
          bloom() {
              setTimeout(() => this.declare, 1000);
          }
      
          /*********** Base ***********/
          // 当将绑定函数用作 instanceof 运算符右操作数时,instanceof 会访问绑定函数内部存储的目标函数,并读取其 prototype 属性。
          class Base {}
          const BoundBase = Base.bind(null, 1, 2);
          console.log(new Base() instanceof BoundBase); // true
      
          /*********** Base ***********/
          // 如果目标函数是可构造的,绑定函数也可以使用 new 运算符进行构造。这样做的效果就好像目标函数本身被构造一样。前置的参数会像通常一样传递给目标函数,而提供的 this 值会被忽略(因为构造函数会准备自己的 this,如 Reflect.construct 的参数所示)。如果直接构造绑定函数,new.target 将指向目标函数(也就是说,绑定函数对 new.target 是透明的)
          class Base {
              constructor(...args) {
                  console.log(new.target === Base);
                  console.log(args);
              }
          }
          const BoundBase = Base.bind(null, 1, 2);
          new BoundBase(3, 4); // true, [1, 2, 3, 4]
      
          /*********** Base ***********/
          // 绑定类:在类上使用 bind() 会保留大部分类的语义,只是当前类的所有静态自有属性会丢失。然而,由于原型链被保留,你仍然可以访问从父类继承的静态属性。这意味着绑定后的类实例仍然可以享受到继承自父类的静态属性的功能。
          class Base {
              static baseProp = "基类属性";
          }
          class Derived extends Base {
              static derivedProp = "派生类属性";
          }
          const BoundDerived = Derived.bind(null);
          console.log(BoundDerived.baseProp); // "基类属性"
          console.log(BoundDerived.derivedProp); // undefined
          console.log(new BoundDerived() instanceof Derived); // true
      
          /*********** Base ***********/
          // 作为构造函数使用的绑定函数:绑定函数自动适用于与 new 运算符一起使用,以用于构造目标函数创建的新实例。当使用绑定函数是用来构造一个值时,提供的 this 会被忽略。然而,提供的参数仍会被插入到构造函数调用时的参数列表之前。
          function Point(x, y) {
              this.x = x;
              this.y = y;
          }
          Point.prototype.toString = function () {
              return `${this.x},${this.y}`;
          };
          const p = new Point(1, 2);
          p.toString();
          // '1,2'
          // thisArg 的值并不重要,因为它被忽略了
          const YAxisPoint = Point.bind(null, 0 /*x*/);
          const axisPoint = new YAxisPoint(5);
          axisPoint.toString(); // '0,5'
          axisPoint instanceof Point; // true
          axisPoint instanceof YAxisPoint; // true
          new YAxisPoint(17, 42) instanceof Point; // true
          // 请注意,你无需采取任何特殊措施来创建一个绑定函数,以便与 new 运算符一起使用。new.target、instanceof、this 等都如预期工作,就好像构造函数从未被绑定一样。唯一的区别是它不能再用于 extends。
      
  • 注意:

    • 当 call(null)、call(undefined)、apply(null)、apply(undefined)、bind(null)、bind(undefined) 在非严格模式下为全局对象【浏览器:window, node: global】,严格模式下为undefined
    • bind是创建新的函数,必须要手动调用,而apply 和 call 则不需要手动调用