【问题标题】:Bind more arguments of an already bound function in Javascript在 Javascript 中绑定已绑定函数的更多参数
【发布时间】:2014-01-22 09:23:32
【问题描述】:

我尝试整理一下我对 javascript 的 bind() 工作原理的想法。

如果我这样做,我会看到

var f = function (a) { ... }
var g = f.bind(obj);
g(1)

然后用obj 作为this1 作为a 调用f。

我认为 g 是 f 的包装函数。

但是当我这样做时

var f = function (a) { ... }
var g = f.bind(obj);
g.call(1)

然后用1 作为thisa undefined 调用f。

所以看起来 g 不仅仅是一个简单的包装器,而且call 以某种方式区分了普通函数和绑定函数。

还有一件事是我不能多次应用一个函数。

var f = function (a) { ... }
var g = f.bind(obj);
var h = g.bind(1);
h();

然后用obj 调用f 作为thisa undefined。

这种行为的原因是什么?

编辑

这个问题中的构造实际上是错误的,请参阅公认的答案,了解它们应该是什么样子(通常我没有注意到callbind 总是需要上下文参数作为第一个参数)。

【问题讨论】:

    标签: javascript call bind


    【解决方案1】:

    你从来没有传递过任何参数——你只是设置了上下文。 call 的第一个参数作为上下文 (this) 接收,参数 2 之后的参数作为被调用函数的参数 1 和之后的参数接收。同时,bind 创建了一个具有新上下文的新函数——在调用它时传递参数。

    以下是将1 作为函数f 的参数a 从您的第一个代码块开始传递的方法:

    f( 1 );
    g( 1 );
    g.call( this, 1 );
    g.apply( this, [ 1 ] );
    

    【讨论】:

      【解决方案2】:

      一旦使用bind 将对象绑定到函数,就无法覆盖它。正如您在MDN documentation 中看到的那样,它清楚地写在规范中:

      “bind() 函数创建一个新函数(绑定函数),其函数体(ECMAScript 5 术语中的内部调用属性)与被调用的函数(绑定函数的目标函数)相同this 值绑定到 bind() 的第一个参数,不能被覆盖。"

      这意味着,如果你这样做:

      g.call(1);
      

      在遵循规范的浏览器上,您将获得 objthis,而不是 1

      你当然可以绑定多个参数,所以:

      var sum = function(a, b, c) { return a + b + c };
      var sumAB = sum.bind(null, 1, 5);
      var sumC = sumAB.bind(null, 2);
      
      console.log(sumC());
      

      但上下文对象将始终是第一个bind 指定的对象,因为它不能被覆盖。

      为了避免混淆,call 的第一个参数是上下文对象(this),然后您将拥有其余的参数。

      意思是:

      var obj = { foo: function(bar) { console.log(bar) } };
      
      obj.foo('hello');
      
      // equivalent to:
      var foo = obj.foo;
      
      foo.call(obj, 'hello');
      

      希望对你有帮助。

      【讨论】:

      • 谢谢,这似乎有点帮助。我的第一个想法是我可以想象每个函数都有this 作为它的第零个参数,然后(部分)在bindcallapply 的调用中提供它的参数。这绝对不正确。但我看到,如果我绑定this,然后想使用call 调用绑定函数,我只需提供一些foo 值作为call 的第一个参数。那太棒了。但我仍然不能接受你的回答,因为它没有解释为什么多个绑定不像部分应用程序那样表现。
      • 我提到了为什么多个绑定的行为不像部分应用程序。您可以多次绑定多个参数,完全作为部分应用程序,但上下文对象只能绑定一次,一旦绑定它就不能覆盖它,因为这是由我链接的规范定义的:“bind() 函数创建一个新函数(绑定函数),其函数体(ECMAScript 5 术语中的内部调用属性)与被调用的函数(绑定函数的目标函数)具有相同的 this 值绑定到bind()的第一个参数,不能被覆盖。"
      • 你说得对,我的小测试弄错了。所以部分应用程序实际上有效,但您必须在后续调用中使用 foo 第一个参数调用 bind。这就是我想知道的 :) 从您的回答中不清楚它是否也适用于进一步论点的部分应用,在我看来,您似乎只是在谈论上下文论点。谢谢;)
      • 我更新了答案,希望现在更清楚了。但是,我建议您拥有自己的partial 函数来应用部分应用程序,并且不要使用bind。这正是因为bind 会改变你的上下文对象,而这并不总是你想要在部分应用程序中发生的事情。
      • MDN 中的文档已更改,您的报价不再存在
      【解决方案3】:

      Function.prototype.call()

      使用 call() 方法,您可以编写可用于不同对象的方法。换句话说,使用 call(),一个对象可以使用属于另一个对象的方法。 More information

      const person = {
        fullName: function() {
          return this.firstName + " " + this.lastName;
        }
      }
      const person1 = {
        firstName:"John",
        lastName: "Doe"
      }
      const person2 = {
        firstName:"Mary",
        lastName: "Doe"
      }
      
      // This will return "John Doe":
      console.log(person.fullName.call(person1));

      call() 允许分配属于一个对象的函数/方法并为不同的对象调用。

      call() 为函数/方法提供了 this 的新值。使用 call(),你可以编写一次方法,然后在另一个对象中继承它,而无需为新对象重写方法。

      > 使用调用链接对象的构造函数

      您可以使用调用来链接对象的构造函数(类似于 Java)。

      在以下示例中,Product 对象的构造函数使用两个参数定义:名称和价格。

      另外两个函数,Food 和 Toy,调用 Product,传递 this、name 和 price。产品初始化属性名称和价格,两个专用函数定义类别。

      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';
      }
      
      const cheese = new Food('feta', 5);
      const fun = new Toy('robot', 40);
      console.log(cheese);
      console.log(fun);

      > 使用 call 调用匿名函数

      在本例中,我们创建了一个匿名函数,并使用 call 在数组中的每个对象上调用它。

      这里匿名函数的主要目的是给每个对象添加一个打印函数,可以打印出该对象在数组中的正确索引。

      const animals = [
        { species: 'Lion', name: 'King' },
        { species: 'Whale', name: 'Fail' }
      ];
      
      for (let i = 0; i < animals.length; i++) {
        (function(i) {
          this.print = function() {
            console.log('#' + i + ' ' + this.species
                        + ': ' + this.name);
          }
          this.print();
        }).call(animals[i], i);
      }

      > 使用 call 调用函数并指定 'this' 的上下文

      在下面的例子中,当我们调用 greet 时,this 的值会被绑定到对象 obj。

      function greet() {
        const reply = [this.animal, 'typically sleep between', this.sleepDuration].join(' ');
        console.log(reply);
      }
      
      const obj = {
        animal: 'cats', sleepDuration: '12 and 16 hours'
      };
      
      greet.call(obj);  // cats typically sleep between 12 and 16 hours

      > 使用 call 调用一个函数并且不指定第一个参数

      在下面的示例中,我们调用 display 函数而不传递第一个参数。如果第一个参数没有传递,则 this 的值绑定到全局对象。

      var sData = 'Wisen';
      
      function display() {
        console.log('sData value is %s ', this.sData);
      }
      
      display.call();  // sData value is Wisen

      注意:在严格模式下, this 的值是未定义的。见下文。

      'use strict';
      
      var sData = 'Wisen';
      
      function display() {
        console.log('sData value is %s ', this.sData);
      }
      
      display.call(); // Cannot read the property of 'sData' of undefined

      更多阅读可以访问reference

      Function.prototype.bind()

      bind() 方法创建一个新函数,在调用该函数时,将其 this 关键字设置为提供的值,并在调用新函数时提供的任何参数之前具有给定的参数序列。

      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

      > 创建绑定函数

      bind() 最简单的用法是创建一个函数,无论它如何调用,都使用特定的 this 值调用。

      新 JavaScript 程序员的一个常见错误是从对象中提取方法,然后调用该函数并期望它使用原始对象作为它的 this(例如,通过在基于回调的代码中使用该方法)。

      但是,如果不特别注意,原始对象通常会丢失。从函数中创建一个绑定函数,使用原始对象,巧妙地解决了这个问题:

      this.x = 9;    // 'this' refers to global 'window' object here in a browser
      const module = {
        x: 81,
        getX: function() { return this.x; }
      };
      
      module.getX();
      //  returns 81
      
      const retrieveX = module.getX;
      retrieveX();
      //  returns 9; the function gets invoked at the global scope
      
      //  Create a new function with 'this' bound to module
      //  New programmers might confuse the
      //  global variable 'x' with module's property 'x'
      const boundGetX = retrieveX.bind(module);
      console.log(boundGetX());
      //  returns 81

      > 部分应用函数

      bind() 的下一个最简单的用法是使用预先指定的初始参数创建一个函数。

      这些参数(如果有)跟随提供的 this 值,然后插入到传递给目标函数的参数的开头,然后是在调用绑定函数时传递给绑定函数的任何参数。

      function list() {
        return Array.prototype.slice.call(arguments);
      }
      
      function addArguments(arg1, arg2) {
        return arg1 + arg2;
      }
      
      const list1 = list(1, 2, 3);
      //  [1, 2, 3]
      
      const result1 = addArguments(1, 2);
      //  3
      
      // Create a function with a preset leading argument
      const leadingThirtysevenList = list.bind(null, 37);
      
      // Create a function with a preset first argument.
      const addThirtySeven = addArguments.bind(null, 37);
      
      const list2 = leadingThirtysevenList();
      //  [37]
      
      const list3 = leadingThirtysevenList(1, 2, 3);
      //  [37, 1, 2, 3]
      
      const result2 = addThirtySeven(5);
      //  37 + 5 = 42
      
      const result3 = addThirtySeven(5, 10);
      //  37 + 5 = 42
      //  (the second argument is ignored)

      > 使用 setTimeout()

      默认情况下,在 setTimeout() 中,this 关键字将设置为窗口(或全局)对象。当使用需要 this 引用类实例的类方法时,您可以显式地将 this 绑定到回调函数,以维护实例。

      function LateBloomer() {
        this.petalCount = Math.floor(Math.random() * 12) + 1;
      }
      
      // Declare bloom after a delay of 1 second
      LateBloomer.prototype.bloom = function() {
        window.setTimeout(this.declare.bind(this), 1000);
      };
      
      LateBloomer.prototype.declare = function() {
        console.log(`I am a beautiful flower with ${this.petalCount} petals!`);
      };
      
      const flower = new LateBloomer();
      flower.bloom();
      //  after 1 second, calls 'flower.declare()'

      如果您想了解更多关于绑定方法的信息,请阅读this resource

      【讨论】:

        猜你喜欢
        • 2018-12-30
        • 1970-01-01
        • 2021-10-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-11-10
        • 1970-01-01
        相关资源
        最近更新 更多