试图在JavaScript中理解(function(func){func();}

时间:2017-03-30 02:36:18

标签: javascript anonymous-function

来自C,每个函数都需要一个用于调用该特定函数的名称。但是在下面的JavaScript代码中。根本没有名字。那么function(func) { func(); }如何调用something.push(function()

更不用说something.push(function()甚至没有参数,从C角度来看,这是错误的

var something = [], 
  object = {
    a: true,
    b: true,
    c: true
}

 for (let key in object){
   something.push(function() {
   console.log(key);
   });
 }

 something.forEach(function(func) { func(); }): 

6 个答案:

答案 0 :(得分:2)

  

来自C,每个函数都需要一个用于调用该特定函数的名称。但在下面的JavaScript代码中,根本没有名称。

在JavaScript中,情况并非如此 - 函数确实可以是匿名(无名)函数。有时你会听到它们被称为“lambdas”或冗余称为“lambda函数”。

但更重要的是,JavaScript中的函数是第一类数据成员,这意味着:

  1. 可以分配给变量

    let y = x => x + 5
    y(3)
    // 8
    
  2. 他们可以作为参数传递给其他函数

    let xs = [1,2,3]
    let y = x => 2 * x
    xs.map(y)
    // [2,4,6]
    
  3. 他们可以从函数

    返回
    let add = x => {
      return y => x + y
    }
    let f = add(1)
    f(2)
    // 3
    
  4. 它们可以包含在数据结构中

    let pikachu = {
      number: 25,
      shock: enemy => enemy.damage(30),
      growl: enemy => enemy.sadden(40)
    }
    pikachu.growl(meowth)
    // "It's very effective!"
    
  5. 那么这里拿走了什么?好吧,在JavaScript中,您需要将函数视为与任何其他值(例如)1"foo"[{user: 'bob'}, {user: 'alice'}]没有区别 - 从概念上讲,它们只是数据而且他们都是一流的

    如何理解您的代码

    这是我亲切重新格式化的代码。既然你最初询问了ES6,我将用ES6替换你的lambdas arrow functions

    let something = []
    let object = { a: true, b: true, c: true }
    
    for (let key in object) {
      something.push(() => console.log(key))
    }
    
    something.forEach(func => func())
    // a
    // b
    // c

    使用高级描述符,此代码的性质是

    1. 遍历object的每个密钥,将某些值添加到something
    2. 通过something的每个值进行迭代,使用该值执行某些操作
    3. 在你的情况下,某个值是一个函数,但我想告诉你如果我们使用另一种值,你的代码会是什么样的

      let something = []
      let object = { a: true, b: true, c: true }
      
      for (let key in object) {
        // this time push a string, instead of an anonymous function
        something.push("the key is: " + key)
      }
      
      something.forEach(str => console.log(str))
      // the key is: a
      // the key is: b
      // the key is: c

      现在我们可以看到当使用不同的值类型(String)时代码如何工作。除了使用字符串之外,我们唯一做的另一件事就是改变

      // from
      func => func()
      
      // to
      str => console.log(str)
      

      这里的推理是

        原始代码中的
      • something是一个函数数组,因此对于每个函数func,我们调用func() < / p>

      • 在修改过的代码中,something是一个字符串数组,因此对于每个字符串str,我们称之为console.log(str) < / p>

      解剖Array.prototype.forEach

      要理解的最后一点是超级电源forEach功能。请记住第一类功能中的点号 2 :第一类数据成员可以作为参数传递给其他函数。

      forEach期望它的论证是一个函数。所以forEach是一个接受另一个函数的函数。这种功能称为higher-order function

      不要让这些花哨的名字分散你的注意力。制作像forEach这样的高阶函数实际上非常简单。永远只记得功能与任何其他值一样 - 下面,我们将编写我们自己的forEach版本,以便您可以看到它是如何工作的

      let something = []
      let object = { a: true, b: true, c: true }
      
      // forEach accepts an array and some function, f
      let forEach = (arr, f) => {
        // iterate thru each value of arr
        for (let value of arr) {
          // call f on each value
          f(value)
        }
      }
      
      for (let key in object) {
        something.push("the key is: " + key)
      }
      
      forEach(something, str => console.log(str))
      // the key is: a
      // the key is: b
      // the key is: c

      现在我们看到字符串数组使用我们自己的forEach函数。让我们确保它也适用于原始的lambda数组。

      let something = []
      let object = { a: true, b: true, c: true }
      
      let forEach = (arr, f) => {
        for (let value of arr) {
          f(value)
        }
      }
      
      for (let key in object) {
        something.push(() => console.log(key))
      }
      
      forEach(something, func => { func() })
      // a
      // b
      // c

      <强>说明

      就是这样!函数不是JavaScript中的特殊雪花。它们就像所有的基准一样。

      从C角度来看,以这种方式思考功能可能需要一段时间。但实际上,当你走进任何新编程语言的家时,你应该把你理解的所有语言都放在门口。每种语言都有自己的表达方式,产生一系列独特的优点和缺点。通过将某种语言A的“力量”引入另一种语言B,你可能会将B的自身优势之一替换为其中一个弱点。

      JavaScript是一种多范式语言,使其非常适合命令式,面向对象和函数式编程风格。由于它具有万能的性质,它可能不是最好的 oop或fp语言,但它仍然相当令人印象深刻,它可以通过各种方式表达问题的特定解决方案

答案 1 :(得分:0)

JavaScript是比C更抽象的语言。在JavaScript解释器内部保存每个变量的类型和指针。声明一个函数时,它会在内部保存在内存中,并且该函数的指针将传递给您传递给它的任何内容(此处为something.forEach)。

但是,这不是C中的指针,您无法访问其值(函数地址),也无法更改它。在JavaScript中,这些伪指针被称为&#34;引用&#34;。简单地说,当你尝试调用某些东西时,解释器引擎会检查它的类型。如果它的类型类型是一个函数,线程将做一些准备,然后执行跳转到函数指针所指向的内存位置。

答案 2 :(得分:0)

something数组存储对函数的引用,但不调用它们。在forEach循环中,正在调用数组中的每个函数。

公平地说,命名令人困惑。你可以这么容易地写:

something.forEach(function(storedFunction) { storedFunction(); }):

因为它是一个临时变量

答案 3 :(得分:0)

所以,这里发生了什么,我们在每次迭代时都pushing functionsomething。假设在这种情况下something是一个数组。

然后,我们调用集合的foreach()函数。此函数中的第一个参数始终是我们迭代的当前项。在这种情况下,我们将迭代遍历在循环中构建的函数列表。由于这些是函数,我们可以调用它们,这是每个函数发生的事情。

以下是一个帮助说明的例子:

// declare some functions
function a(){console.log('hi');}
function b(){console.log('there');}
function c(){console.log('friend');}
const ar = [];

// push the functions to the array
ar.push(a);
ar.push(b);
ar.push(c);
ar.push(a);

ar.forEach(function(func) {func();});

// console output:
// hi
// there
// friend
// hi

编辑:我强烈建议您阅读Mozilla's documentation of the function type。如果你去那里并彻底阅读它,我想你会对这个概念有一个非常扎实的把握。

答案 4 :(得分:0)

在JavaScript中,函数被视为对象。在C中你必须意识到并非每个对象(值)都需要有一个名称,或者不需要存储在一个变量中。就像你可以拥有sum (4, 5);一样。 4和5是未存储在变量中的值。

要回答关于如果没有名称将如何调用它们的问题,该函数将作为参数传递给push函数。那里的形式参数必须有一些名称。所以它可以被这个名字调用。在这种情况下,push不会调用它,但会将其存储在稍后要调用的数组中。它可以通过索引到数组来调用。

答案 5 :(得分:0)

您看到的是使用匿名函数闭包。简而言之,它非常简洁。

在那里我描述了每个块的代码块:

我们创建一个数组(如C中所示):

var something = [], 

我们创建一个对象(key = value的集合)

object = {
    a: true,
    b: true,
    c: true
}

我们遍历对象的元素 对象

for (let key in object) {

该节目已开启:

something.push(function() {

每个对象都有一个push函数,允许在其中动态放置元素。 你在这里看到的是匿名函数被用作参数;这意味着对于推送功能的每次执行,执行以下操作:

console.log(key);

将被放入某些数组;

之后,唯一做的是使用 func()callback

something.forEach(function(func) { func(); }): 

这意味着某些 forEach 元素,您将其视为函数,从而导致调用 push存储的先前函数()