【问题标题】:context to use call and apply in Javascript?在 Javascript 中使用调用和应用的上下文?
【发布时间】:2012-01-29 08:54:57
【问题描述】:

任何人都可以解释上下文以在 Javascript 中使用 callapply 方法吗?

为什么要使用callapply而不是直接调用函数?

【问题讨论】:

  • This read 在调用apply()call() 时帮助我理解了thisArg 的意义,这似乎是您问题的核心。您需要了解 Javascript 中的函数调用原语

标签: javascript


【解决方案1】:

当您想将不同的this 值传递给函数时,您可以使用callapply。从本质上讲,这意味着您希望像执行特定对象的方法一样执行函数。两者的唯一区别是call 需要用逗号分隔的参数,而apply 需要数组中的参数。

来自Mozilla's apply page 的示例,其中构造函数是链式的:

function Product(name, price) {
    this.name = name;
    this.price = price;

    if (price < 0)
        throw RangeError('Cannot create product "' + name + '" with a negative price');
    return this;
}

function Food(name, price) {
    Product.apply(this, arguments);
    this.category = 'food';
}
Food.prototype = new Product();

function Toy(name, price) {
    Product.apply(this, arguments);
    this.category = 'toy';
}
Toy.prototype = new Product();

var cheese = new Food('feta', 5);
var fun = new Toy('robot', 40);

Product.apply(this, arguments) 的作用如下:Product 构造函数在FoodToy 构造函数中作为函数应用,并且每个对象实例都作为this 传递。因此,FoodToy 现在都具有 this.namethis.category 属性。

【讨论】:

  • 只是一个关于提供的代码 sn-p 的问题,为什么 Toy's and Food 的原型会被 Product 对象替换?
  • 评论只是为了解决@davids的问题:为什么我们需要做Toy.prototype = new Product();?我对此进行了测试,删除这些行似乎对最终结果没有影响。
  • @davids & @adrianp : 需要让 Food 和 Toy 从 Product 继承。看这个例子:jsfiddle.net/Shawn/xNEYf 我删除了Toy.prototype = new Product();,但留下了Food.prototype = new Product();,所以现在 Food 继承自 Product,但 Toy 没有。我还在Product的prototype中添加了一个方法,从Food和Toy调用,让继承的缺失更加明显(打开控制台运行代码,Food可以调用Product继承的方法,但是Toy不能调用,会报错) .
  • 原型与对象字面量一起使用时如何使用?
  • 我想我只是不明白这样做的目的,而不是看起来更容易的事情,例如this
【解决方案2】:

仅当您使用callapply 时,您才能修改函数内部的this 上下文。

与其他语言不同 - 在 JavaScript 中 this 不引用当前对象 - 而是引用执行上下文,并且可以由调用者设置。

如果您使用new 关键字调用函数this 将正确引用新对象(在构造函数内部)..

但在所有其他情况下 - this 将引用全局对象,除非通过调用明确设置

【讨论】:

  • 如果使用obj.whatever();语法,它可能引用调用者obj。
  • 声明“但在所有其他情况下 - this 将引用全局对象,除非通过调用显式设置”应更正为:“但在所有其他情况下,除非通过调用显式设置,否则 this如果在非严格模式下使用 Ecmascript 3 或 Ecmascript 5(或更高版本)将引用全局对象。但是,在具有严格模式的 Ecmascript 5(或更高版本)中,如果未显式设置,则始终未定义,并且 JS 引擎将抛出一个访问此内容时出现异常。”参考:stackoverflow.com/questions/9822561/…
【解决方案3】:

当您想要使函数以不同的this 值执行时,您可以使用.call()。它按指定设置this 值,按指定设置参数,然后调用该函数。 .call() 和仅仅执行函数的区别在于函数执行时this 指针的值。当您正常执行该函数时,javascript 决定 this 指针将是什么(通常是全局上下文 window,除非该函数作为对象上的方法调用)。当您使用.call() 时,您可以准确指定您希望将this 设置为什么。

当您要传递给函数的参数位于数组中时,您可以使用.apply().apply() 还可以使函数以特定的 this 值执行。当您有不确定数量的来自其他来源的参数时,最常使用.apply()。它也经常使用特殊的局部变量 arguments 将参数从一个函数调用传递到另一个函数调用,其中包含传递给当前函数的一组参数。

我发现 .call().apply() 的 MDN 参考页面很有帮助。

【讨论】:

  • 谢谢,你解释得很好。终于明白第一个参数的用法了。
【解决方案4】:

如果您有使用过 jQuery 的经验,您就会知道大多数函数都使用 this 对象。例如,collection.each(function() { ... }); 在这个函数内部,"this" 指的是迭代器对象。这是一种可能的用法。

我个人使用.apply() 来实现请求队列 - 我将一组参数推送到队列中,当需要执行它时,我获取一个元素,并将其作为处理程序的参数传递函数使用.apply(),因此如果必须将参数数组作为第一个参数传递,则代码会更清晰。这是另一个例子。

一般来说,只要记住这些调用函数的方法是存在的,你可能有一天会发现它们很方便用于实现你的程序。

【讨论】:

    【解决方案5】:

    如果您有使用Object Oriented Programming 的经验,那么如果您将其与继承进行比较并从子类覆盖父类的属性或方法/函数,那么调用和应用将是有意义的。这与 javascript 中的调用类似,如下所示:

    function foo () {
      this.helloworld = "hello from foo"
    }
    foo.prototype.print = function () {
      console.log(this.helloworld)
    }
    
    foo.prototype.main = function () {
      this.print()
    }
    
    
    function bar() {
      this.helloworld = 'hello from bar'
    }
    // declaring print function to override the previous print
    bar.prototype.print = function () {
      console.log(this.helloworld)
    }
    
    var iamfoo = new foo()
    
    iamfoo.main() // prints: hello from foo
    
    iamfoo.main.call(new bar()) // override print and prints: hello from bar
    

    【讨论】:

      【解决方案6】:

      我想不出任何正常情况下将 thisArg 设置为不同的值是使用 apply 的目的。

      apply 的目的是将值数组传递给希望这些值作为参数的函数。

      它已被散布运算符取代为所有日常使用。

      例如

      // Finding the largest number in an array
      `Math.max.apply(null, arr)` becomes `Math.max(...arr)`
      
      // Inserting the values of one array at the start of another
      Array.prototype.unshift.apply(arr1, arr2); 
      // which becomes 
      arr1 = [...arr2, ...arr1]
      

      【讨论】:

        猜你喜欢
        • 2012-02-13
        • 1970-01-01
        • 1970-01-01
        • 2011-10-30
        • 1970-01-01
        • 2021-02-03
        • 2017-04-08
        • 2013-01-07
        • 1970-01-01
        相关资源
        最近更新 更多