JavaScript调用 - 我可以直接访问对象属性

时间:2015-02-26 00:31:16

标签: javascript scope call

我想了解使用call()的目的。我读了this example关于它的用途,并从Mozilla收集了一些例子,即:

var animals = [
  { species: 'Lion', name: 'King' },
  { species: 'Whale', name: 'Fail' }
];

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

我知道他们的示例仅用于演示,但我可以轻松地直接在循环中访问对象的属性:

        var animals = [
          { species: 'Lion', name: 'King' },
          { species: 'Whale', name: 'Fail' }
        ];

        for (var i = 0; i < animals.length; i++) {
            console.log('#' + i + ' ' + animals[i].species  + ': ' + animals[i].name);        
        }

由此我感到困惑。当我需要将不同的this上下文传递给call()时,我仍然没有看到原因。


另外,有了这个mozilla示例,在.call(animals[i], i);函数中,我有两个问题:

  • this这里是animals数组。我猜你需要在这里打电话,因为animals超出了内部匿名函数的范围?

  • 传递索引i作为.call(animals[i], i);中的第二个参数的目的是什么?


这个问题是由我试图理解的以下代码段引发的。此代码用于从文档中的某些跨度收集所有内部值并将它们连接在一起。为什么我需要在地图上执行call?是因为document.querySelectorAll是否超出了上下文的范围?

        console.log([].map.call(document.querySelectorAll("span"), 
            function(a){ 
                return a.textContent;
            }).join(""));

4 个答案:

答案 0 :(得分:3)

我将从问题的底部开始,询问为什么要使用&#34;来电&#34;在这段代码中:

    console.log([].map.call(document.querySelectorAll("span"), 
        function(a){ 
            return a.textContent;
        }).join(""));

所以,这就是问题所在。这段代码真正想做的是在数组上使用map方法。但是,document.querySelectorAll返回NodeList,而不是数组。最重要的是,NodeList 上有一个map函数。所以你要坚持使用经典的for循环。

然而,事实证明,Array.map实际上适用于任何具有length属性并支持数字索引的东西(例如nodes[i])。但是,map的实现在内部专门使用this.length

所以,通过在这里使用call,你可以借用Array.map的实现,传递一个this引用,它实际上不是一个数组,但是工作得很像一个工作这些目的。

所以基本上,这是在呼唤:

document.querySelectorAll("span").map( 
  function(a){ 
    return a.textContent;
  }).join("");

除了返回实际上没有 map方法,所以我们从Array中借一个。这通常被称为&#34;鸭子打字&#34;。

动物的例子就像其中有动物的任何例子一样,都是人为的。您在通话中传递了i,因为您正在调用的函数(匿名函数)需要一个参数(请注意function(i)),因此您需要传递一个值。正如我所说,它是人为的,在现实生活中,你不会这样做。

因此,一般来说,调用对于借用一种类型的方法以在另一种类型上使用是最有用的。

答案 1 :(得分:2)

这个例子是正确的,但很差,因为完全没必要达到他们的结果。

  

另外,有了这个mozilla示例,在.call(animals[i], i);函数中,我有两个问题:

     
      
  • this这里是animals数组。我猜你需要在这里打电话,因为animals超出了内部匿名函数的范围?
  •   

实际上,this是每个项目。在.call(animals[i], i);中,第一项是将映射到this的对象,因此函数内的this是当前动物项,而不是动物数组。

此外,内部函数可以在当前闭包中访问animals。再次,证明他们的榜样很差。

  
      
  • 传入索引i的目的是什么作为.call(animals [i],i)中的第二个参数;?
  •   

第二个参数是call是传递给被调用函数的第一个参数(第三个参数是第二个参数,等等)。 这与apply相对,后者将数组作为第二个参数并将其作为单独的参数应用。但是,它们的被调用函数可以访问当前{ {1}}再一次表明他们的例子是不必要的。


现在,第二部分。您需要使用i,因为calldocument.querySelectorAll而不是NodeList,不幸的是,Array没有NodeList方法,而map确实如此。

对于非常简化的版本,假设我们Array定义了这样的内容:

Array.prototype.map

您可以在此处看到我们致电:

Array.prototype.map = function(fn) {
  var copy = [];
  for (var i = 0; i < this.length; i++) {
    copy.push(fn(this[i]));
  }
  return copy;
};

您可以在我们定义的var timesTwo = [1,2,3].map(function(n) { return n * 2; }); // timesTwo is [2,4,6]; 中看到我们将数组实例称为Array.prototype.map。现在,回到上面的this:我们没有NodeList方法,但我们仍然可以使用map拨打Array.prototype.map并强制{{1引用我们的call。这很好,因为它确实具有this属性,正如我们所使用的那样。

因此,我们可以通过使用:

来实现
NodeList

希望有所帮助。

答案 2 :(得分:1)

document.querySelectorAll("span")返回一个NodeList而不是一个数组,所以如果你尝试document.querySelectorAll("span").map(...),你会收到一个关于map没有定义的错误(如果你试过.join也一样)< / p>

[].map.call(document.querySelectorAll("span"), 
    function(a){ 
        return a.textContent;
    }
)

这只是使用call来实现它,因此NodeList用作map函数的数组


for (var i = 0; i < animals.length; i++) {
  (function(i) {
    this.print = function() {
      console.log('#' + i + ' ' + this.species
                  + ': ' + this.name);
    }
    this.print();
  }).call(animals[i], i);
}
  1. this这里是动物阵列。我猜你需要在这里打电话,因为动物在内部匿名功能范围之外?

    实际上这里的this将引用位于i的animals数组中的对象。但animals并非超出范围,this.speciesanimals[i].species更容易。他们可以轻松完成)(i)并完成animals[i].species而不是).call(...)this.species

  2. 传入索引i的目的是什么作为.call(animals [i],i)中的第二个参数;?

    由于他们使用匿名函数,如果他们没有使用IIFE并传入索引i参数,则匿名函数将使用for循环的增量变量i最后设置的任何值至。因此,打印功能将记录相同的信息而不是每个对象信息。

答案 3 :(得分:0)

你的例子与Mozilla不同。在Mozilla中,它创建了一个功能。如果没有循环中的匿名函数来创建该函数,您将遇到closure problem。有效地意味着功能不会像您期望的那样表现。例如,尝试在没有匿名函数的情况下重新创建函数,并调用animals[0].print()

  

这里是动物阵列。我猜你需要打电话   因为动物是超出内心匿名的范围   功能

this值是单个动物对象。 animals数组不超出内部匿名函数的范围 - 这是JavaScript的本质,除非another variable is shadowing it(具有相同的名称,因此涵盖了一个外部函数),否则您将始终可以访问外部函数的变量。外部变量)。

考虑没有调用方法的示例:

var animals = [
    { species: 'Lion', name: 'King' },
    { species: 'Whale', name: 'Fail' }
];

for (var i = 0; i < animals.length; i++) {
    (function(i) {
        this.print = function() {
            console.log('#' + i + ' ' + this.species
              + ': ' + this.name);
        }
        this.print();
    })(i);
}

在此示例中,this变量将引用window对象或null,具体取决于某些因素,例如是否使用use strict。但我们想要做的是将print方法附加到动物对象,因此我们需要使用调用方法。

  

将索引i作为第二个参数传递的目的是什么   在.call(animals [i],i);?

如上所述,由于闭包问题,您不希望在外部闭包中引用i变量,但是您希望将其本地化到匿名函数内部以便{ {1}}方法是理智的。