简体   繁体   English

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

[英]Trying to understand (function(func) { func(); } in JavaScript

Coming from C, every function needs a name that is used to call that specific function. 来自C的每个函数都需要一个用于调用该特定函数的名称。 but in the JavaScript code below. 但在下面的JavaScript代码中。 There is no name at all. 根本没有名字。 So how does function(func) { func(); } 那么function(func) { func(); } function(func) { func(); } calls the something.push(function() ? function(func) { func(); }调用something.push(function()

Not to mention that something.push(function() doesn't even have an argument, which from C perspective, this is wrong 更不用说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(); }): 

Coming from C, every function needs a name that is used to call that specific function. 来自C的每个函数都需要一个用于调用该特定函数的名称。 but in the JavaScript code below, there is no name at all. 但在下面的JavaScript代码中,根本没有名称。

Well in JavaScript, that's just not the case – functions can indeed be anonymous (nameless) functions. 在JavaScript中,情况并非如此-函数确实可以是匿名 (无名)函数。 Sometimes you'll hear them called "lambdas" or redundantly called "lambda functions". 有时您会听到它们被称为“ lambda”或多余的“ lambda函数”。

But more importantly, functions are first class data members in JavaScript, meaning: 但更重要的是,函数是JavaScript中的一流数据成员,这意味着:

  1. They can be assigned to variables 可以将它们分配给变量

     let y = x => x + 5 y(3) // 8 
  2. They can be passed as arguments to other functions 它们可以作为参数传递给其他函数

     let xs = [1,2,3] let y = x => 2 * x xs.map(y) // [2,4,6] 
  3. They can be returned from a function 它们可以从函数返回

     let add = x => { return y => x + y } let f = add(1) f(2) // 3 
  4. They can be included in data structures 它们可以包含在数据结构中

     let pikachu = { number: 25, shock: enemy => enemy.damage(30), growl: enemy => enemy.sadden(40) } pikachu.growl(meowth) // "It's very effective!" 

So what's the take away here? 那么,这里能带走什么呢? Well, in JavaScript, you need to think of functions as being no different than any other value (eg) 1 , "foo" , or [{user: 'bob'}, {user: 'alice'}] – conceptually, they are just data and they're all first class 嗯,在JavaScript中,您需要认为函数与其他任何值都没有区别(例如1"foo"[{user: 'bob'}, {user: 'alice'}] –从概念上讲,它们只是数据而他们都是一流的


How to understand your code 如何理解你的代码

Here's your code affectionally reformatted by me. 这是我对您的代码的重新格式化。 Since you originally asked about ES6, I'm going to substitute your lambdas with ES6 arrow functions 自从您最初询问ES6以来,我将用ES6 箭头功能替代您的lambda

 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 

Using high level descriptors, the nature of this code is 使用高级描述符,此代码的本质是

  1. iterate thru each key of object , add some value to something 遍历object每个键,为something增加一些价值
  2. iterate thru each value of something , do something with that value 遍历something每个价值,用该价值做某事

In your case, some value is a function, but I want to show you what your code would look like if we used another kind of value 在您的情况下, 某些值是一个函数,但是我想向您展示如果我们使用另一种值,您的代码将是什么样子

 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 

So now we can see how the code works when a different value type (String) is used. 现在,我们可以看到使用其他值类型(字符串)时代码如何工作。 Aside from using a string, the only other thing we did was change 除了使用字符串,我们所做的唯一另一件事就是更改

// from
func => func()

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

The reasoning here is 这里的理由是

  • in your original code, something is an array of functions , so for each of those functions, func , we call func() 在您的原始代码中, something是一个函数数组 ,因此对于每个函数func ,我们称为func()

  • in the modified code, something is an array of strings , so for each of those strings, str , we call console.log(str) 在修改后的代码中, something是一个字符串数组 ,因此对于每个字符串str ,我们称为console.log(str)


Dissection of Array.prototype.forEach 剖析Array.prototype.forEach

The last remaining bit to understand is the super-powered forEach function. 剩下的最后一点要了解的是功能强大的forEach功能。 Remember point number 2 in the first class capabilities: first class data members can be passed as arguments to other functions. 记得在第一级的能力点编号2:第一类数据成员可以作为其他函数的参数进行传递。

forEach expects its argument to be a function. forEach希望其参数是一个函数。 So forEach is a function that accepts another function. 因此forEach是一个接受另一个功能的功能。 This kind of function is called a higher-order function . 这种函数称为高阶函数

Don't let these fancy names distract you from their underlying simplicity. 不要让这些花哨的名字分散您的内在朴素。 Making our own higher-order functions like forEach is actually really easy. 制作像forEach这样的高阶函数实际上非常容易。 Always just remember that functions are like any other value – below, we'll write our own version of forEach so you can see how it works 始终记住函数与其他任何值一样–在下面,我们将编写自己的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 

So now we see the string array working with our own forEach function. 因此,现在我们看到字符串数组与我们自己的forEach函数forEach工作。 Let's make sure it works with your original lambda array, too. 让我们确保它也可以与原始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 


Remarks 备注

And that's it! 就是这样! Functions aren't special snowflakes in JavaScript. 函数不是JavaScript中的特殊雪花。 They're just like all the datums. 它们就像所有基准一样。

Coming from a C perspective, it might take awhile to think about functions in this way. 从C的角度来看,以这种方式考虑功能可能需要一段时间。 But really, you should leave everything you understand about any language at the door when you're walking into the home of any new programming language. 但是,实际上,当您走进任何新的编程语言之家时,都应该把对任何语言的了解都留在门外。 Each language has its own way of expressing itself yielding a unique set of strengths and weaknesses. 每种语言都有自己的表达方式,产生了一套独特的优点和缺点。 By bringing in a "strength" of some language A to another language B, you might be substituting one of B's own strengths for one of it's weaknesses. 通过将某种语言A的“优势”引入另一种语言B,您可能会用B自身的优势代替其自身的劣势。

JavaScript is a multi-paradigm language making it rather suitable for imperative, object-oriented, and functional programming styles. JavaScript是一种多范式语言,因此非常适合命令式,面向对象和函数式编程样式。 Because of its jack-of-all-trades nature, it's probably not the best oop or fp language, but it's still rather impressive that it can express a particular solution to a problem in a wide variety of ways. 由于其千篇一律的性质,它可能不是最好的 oop或fp语言,但它可以以多种方式表示特定问题的解决方案仍然令人印象深刻。

JavaScript is more abstract language than C. In JavaScript interpretter internally saves types and pointers of every variable. JavaScript比C更抽象的语言。在JavaScript解释器中,内部保存每个变量的类型和指针。 When you declare a function, it is internally saved somewhere in memory and pointer of that function is passed to whatever you passed it (here to something.forEach ). 当您声明一个函数时,该函数会在内部保存在内存中的某个位置,并且该函数的指针会传递到您传递的任何内容(此处为something.forEach )。

But, this is not pointer like in C, you cannot access its value (function address) nor you can change it. 但是,这不是C中的指针,您无法访问其值(函数地址),也无法更改它。 In JavaScript these pseudo-pointers are called "references". 在JavaScript中,这些伪指针称为“引用”。 Simply, when you try to call something, interpretter engine will check its type. 简而言之,当您尝试调用某些内容时,解释器引擎将检查其类型。 If its type type is a function, thread will do some preparations and then execute jump to memory location which function pointer is pointing to. 如果其类型类型是函数,则线程将做一些准备,然后执行跳转到函数指针所指向的内存位置。

The something array is storing references to functions, but not invoking them. something数组存储对函数的引用,但不调用它们。 In the forEach loop, each function within the array is being invoked. forEach循环中,将调用数组中的每个函数。

To be fair, the naming is confusing. 公平地说,命名令人困惑。 You could just as easily write: 您可以轻松地编写:

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

since it's a temporary variable 因为这是一个临时变量

So, what's happening here is in the loop, we are pushing a function to something on each iteration. 因此,这里发生的事情是在循环中,我们在每次迭代中将function something上。 Assuming something is an array in this case. 在这种情况下,假设something是一个数组。

Then, we call the collection's foreach() function. 然后,我们调用集合的foreach()函数。 The first argument in this function is always the current item over which we are iterating. 此函数中的第一个参数始终是我们要迭代的当前项。 In this case, we're iterating over the list of functions we built in the loop. 在这种情况下,我们要遍历循环中构建的函数列表。 Since these are functions, we can call them, which is what's happening for each one. 由于这些都是函数,因此我们可以调用它们,这就是每个函数所发生的事情。

Here's an example to help illustrate: 这是一个示例以帮助说明:

// 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

EDIT: I highly recommend you read Mozilla's documentation of the function type . 编辑:我强烈建议您阅读Mozilla的函数类型的文档 If you go there and thoroughly read through it I think you'll have a very solid grasp of this concept. 如果您去那里并仔细阅读它,我想您会对这个概念有很扎实的了解。

In JavaScript, functions are treated as objects. 在JavaScript中,函数被视为对象。 And in C you must be aware that not every object (value) needs to have a name, or doesn't need to be stored in a variable. 而且在C语言中,您必须意识到并非每个对象(值)都需要具有名称,也不需要存储在变量中。 Just like you can have​ sum (4, 5); 就像你可以sum (4, 5); . 4 and 5 are values not stored in variables. 4和5是未存储在变量中的值。

To answer your question about how they will be called if they have no name, the function is passed as an argument to the push function. 要回答有关没有名称时将如何调用它们的问题,该函数将作为参数传递给push函数。 There the formal parameter must have some name. 形式参数必须有一些名称。 So it can be called by that name. 因此,可以使用该名称来调用它。 In this case push doesn't call it but stores it in an array to be called later. 在这种情况下,push不会调用它,而是将其存储在一个数组中以供以后调用。 It can be called by indexing into the array. 可以通过索引数组来调用它。

What you see is the usage of an anonymous function and a closure . 您将看到匿名函数闭包的用法。 It's pretty Javascript in a nutshell. 简而言之,它是漂亮的Javascript。

There I describe the code block per block : 我在这里描述每个块的代码块:

We create an array (like in C) : 我们创建一个数组(类似于C语言):

var something = [], 

We create an object (which is a collection of key=value) 我们创建一个对象(它是键=值的集合)

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

We iterate through the elements of the object object 我们遍历对象 object 的元素

for (let key in object) {

There the show is on : 那里的节目正在进行中:

something.push(function() {

Each object has a push function that allow to put elements dynamically inside of it. 每个对象都有一个push函数,该函数允许在其中动态放置元素。 What you see here is a anonymous function being used as an argument ; 您在这里看到的是一个匿名函数被用作参数 it means that for each executions of this push function, a function that do : 这意味着对于 push函数的每次执行,一个函数都会执行以下操作:

console.log(key);

will be put inside the something array; 将被放置在something数组中;

There after, the only thing that is done is a callback with func() : 之后,唯一要做的就是使用func()进行callback

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

This means forEach elements of something , you will treat it as a function resulting in calling the precedent function stored with push() 这意味着某事物的 forEach元素,您会将其视为一个函数,从而导致调用存储在push()中的先前函数

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM