javascript方法链中的输入参数是如何填充的? [英] How are input parameters filled in javascript method chains?

查看:20
本文介绍了javascript方法链中的输入参数是如何填充的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图真正了解 javascript 工作原理的细节.在方法链接期间,有时一个方法返回到另一个具有命名输入参数的方法.

I am trying to really understand the details of how javascript works. During method chaining, sometimes one method returns to another method that has a named input parameter.

例如,在 D3 中,模式如下所示:

For instance, in D3, the pattern looks like this:

d3.select("body").selectAll("p")
    .data(dataset)
    .enter()
    .append("p")
    .text(function(d) { return d; }); //what does this d refer to? How is it filled?

在jquery中,模式是这样的:

In jquery, the pattern looks like this:

$.ajax({
  ...
})
  .done(function( data ) {  //what does this data refer to? How is it filled? 

我从实际编码中知道这些输入参数的名称可以是任何东西.但是归档输入参数的数据从何而来?它是否只引用链中先前方法返回的数据?

I know from practical coding that the name of these input parameter can be anything. But where does the data that files the input parameter come from? Does it just refer to the data returned from the prior method in the chain?

推荐答案

两个不同的主题,所以我会分开解释:

Two different topics, so I'll explain them separately:

首先,更正:您提供的示例不是一个方法返回到具有命名输入参数的另一个方法"的示例.它们是一个函数作为另一个方法的输入参数的例子.

First, a correction: The examples you are giving are not examples where "one method returns to another method that has a named input parameter". They are examples where a function is given as the input parameter to another method.

为了澄清起见,我将举一个例子,其中一个函数的返回值用作另一个函数的输入.

To clarify, I'll give you an example where the return value of one function is used as the input to another.

var a = "Hello ",
    b = "World!";

var c = a.concat( b.toUpperCase() ); //c = "Hello WORLD!"

为了创建c,顺序如下:

  1. 浏览器开始解析concat方法,但需要弄清楚给它什么参数.
  2. 该参数包含一个方法调用,因此btoUpperCase()方法被执行,返回字符串WORLD!".
  3. 返回的字符串成为 aconcat() 方法的参数,现在可以执行该方法.
  1. The browser starts to parse the concat method, but needs to figure out what parameter to give it.
  2. That parameter includes a method call, so the toUpperCase() method of b is executed, returning the string "WORLD!".
  3. The returned string becomes the parameter for a's concat() method which can now be executed.

concat()方法而言,结果和你写的一样c = a.concat("WORLD!") --它不在乎字符串WORLD!"由另一个函数创建.

As far as the concat() method is concerned, the result is the same as if you wrote c = a.concat("WORLD!") -- it doesn't care that the string "WORLD!" was created by another function.

你可以看出b.toUpperCase()返回值是作为参数传递的,而不是函数本身,因为函数名末尾的括号.函数名称后的括号告诉浏览器执行该函数,只要它已经计算出该函数的任何参数的值.如果没有括号,函数将被视为任何其他对象,并且可以作为参数或变量传递,而无需实际执行任何操作.

You can tell that the returned value of b.toUpperCase() is being passed as the parameter, and not the function itself, because of the parentheses at the end of the function name. Parentheses after a function name tell the browser to execute that function, just as soon as it has figured out the values of any parameters to that function. Without the parentheses, the function is treated as any other object, and can be passed around as a parameter or variable without actually doing anything.

当一个未执行的函数对象被用作另一个函数的参数时,发生的事情完全取决于第二个函数中的指令.如果您将该函数对象传递给 console.log(),则该函数的字符串表示将被打印到控制台,而不会执行您传入的函数.但是,大多数接受另一个函数的方法作为input 被设计为使用指定的参数调用该函数.

When a function object, unexecuted, is used as the parameter for another function, what happens is entirely dependent on the instructions inside that second function. If you pass that function object to console.log(), the string representation of the function will be printed to the console without ever executing the function you passed in. However, most methods that accept another function as input are designed to call that function with specified parameters.

一个例子是数组的 map() 方法.map 方法创建一个新数组,其中每个元素都是对原始数组的对应元素运行映射函数的结果.

One example is the map() method of arrays. The map method creates a new array in which every element is the result of running the mapping function on the corresponding element of the original array.

var stringArray = ["1", "2!", "3.0", "?"];

var numberArray = stringArray.map(parseFloat); //numberArray = [1, 2, 3, NaN]

函数 parseFloat() 是一个内置函数,它接受一个字符串并试图从中找出一个数字.请注意,当我将它传递给 map 函数时,我只是将它作为变量名传递,而不是在最后用括号执行它.它由map函数执行,由map函数决定它得到什么参数.每次调用 parseFloat 的结果都由 map 函数分配到它们在结果数组中的位置.

The function parseFloat() is a built-in function that takes a string and tries to figure out a number from it. Note that when I pass it in to the map function, I'm just passing it in as a variable name, not executing it with parentheses at the end. It is executed by the map function, and it is the map function that decides what parameters it gets. The results of each call to parseFloat are assigned by the map function to their place in the result array.

具体来说,map 函数使用三个参数执行parseFloat:数组中的元素、该元素在数组中的索引以及整个数组.parseFloat 函数只使用了一个参数,因此忽略第二个和第三个参数.(如果你尝试用 parseInt 做同样的事情,你会得到意想不到的结果,因为 parseInt does 使用了第二个参数——它将其视为整数的基数(基数").)

Specifically, the map function executes parseFloat with three parameters: the element from the array, the index of that element in the array, and the array as a whole. The parseFloat function only uses one parameter, however, so the second and third parameters are ignored. (If you try to do the same thing with parseInt, however, you'll get unexpected results because parseInt does use a second parameter -- which it treats as the radix ("base") of the integer.)

map 函数并不关心传入函数需要多少个参数,当然也不关心该函数内部使用了哪些变量名.如果您自己编写 map 函数,它看起来像这样:

The map function doesn't care how many parameters the passed-in function is expecting, and it certainly doesn't care which variable names are used inside that function. If you were writing the map function yourself, it would look something like this:

Array.prototype.myMap = function(f) {

    var result = [];

    for (var i = 0, n=this.length; i<n; i++) {

         result[i] = f( this[i], i, this);
                  //call the passed-in function with three parameters
    }
    return result;
};

f 函数被调用,并给定参数,但不知道它是什么或它做什么.参数按特定顺序给出——元素、索引、数组——但不链接到任何特定的参数名称.

The f function is called, and given parameters, without knowing anything about what it is or what it does. The parameters are given in a specific order -- element, index, array -- but are not linked to any particular parameter name.

现在,像 map 方法的一个限制是可以直接调用的 Javascript 函数很少,只需传递一个值作为参数.大多数函数都是特定对象的方法.例如,我们不能使用 toUpperCase 方法作为 map 的参数,因为 toUpperCase 只作为字符串对象的方法存在,并且只作用于那个特定的字符串对象,而不作用于 map 函数可能给它的任何参数.为了将字符串数组映射为大写,您需要创建自己的函数,该函数的工作方式与 map 函数将使用它的方式相同.

Now, a limitation of something like the map method is that there are very few Javascript functions which can be called directly, just passing a value as a parameter. Most functions are methods of a specific object. For example, we couldn't use the toUpperCase method as a parameter to map, because toUpperCase only exists as a method of a string object, and only acts on that particular string object, not on any parameter that the map function might give it. In order to map an array of strings to uppercase, you need to create your own function that works in the way the map function will use it.

var stringArray = ["hello", "world", "again"];

function myUpperCase(s, i, array) {

     return s.toUpperCase(); //call the passed-in string's method

}

var uppercaseStrings = stringArray.map( myUpperCase );
   //uppercaseStrings = ["HELLO", "WORLD", "AGAIN"]

但是,如果您只打算使用函数 myUpperCase 一次,则无需单独声明它并为其命名.您可以直接将其用作匿名函数.

However, if you're only ever going to use the function myUpperCase this once, you don't need to declare it separately and give it a name. You can use it directly as an anonymous function.

var stringArray = ["hello", "world", "again"];

var uppercaseStrings = stringArray.map( 
                           function(s,i,array) {
                               return s.toUpperCase();
                           }
                       );
   //uppercaseStrings still = ["HELLO", "WORLD", "AGAIN"]

开始看起来很眼熟?这是许多 d3 和 JQuery 函数使用的结构——您传入一个函数,作为函数名或匿名函数,d3/JQuery 方法在选择的每个元素上调用您的函数,传入指定值作为第一个、第二个甚至第三个参数.

Starting to look familiar? This is the structure used by so many d3 and JQuery functions -- you pass-in a function, either as a function name or as an anonymous function, and the d3/JQuery method calls your function on each element of a selection, passing in specified values as the first, second and maybe third parameter.

那么参数名称呢?正如您所提到的,它们可以是您想要的任何东西.我可以在我的函数中使用很长的描述性参数名称:

So what about the parameter names? As you mentioned, they can be anything you want. I could have used very long and descriptive parameter names in my function:

function myUpperCase(stringElementFromArray, indexOfStringElementInArray, ArrayContainingStrings) { 

         return stringElementFromArray.toUpperCase();
}

传入函数的值是一样的,纯粹是根据map函数传入的顺序. 事实上,因为我从来没有使用过索引参数或数组参数,我可以保留它们在我的函数声明之外,只使用第一个参数.map 仍然会传入其他值,但它们会被忽略.但是,如果我想要使用 map 传入的第二个或第三个参数,我将不得不为第一个参数声明一个名称,以保持编号正确:

The values that get passed in to the function will be the same, based purely on the order in which the map function passes them in. In fact, since I never use the index parameter or the array parameter, I can leave them out of my function declaration and just use the first parameter. The other values still get passed in by map, but they are ignored. However, if I wanted to use the second or third parameter passed in by map, I would have to declare a name for the first parameter, just to keep the numbering straight:

function indirectUpperCase (stringIDontUse, index, array) {

         return array[index].toUpperCase();
}

这就是为什么,如果你想在d3中使用索引号,你必须写function(d,i){return i;}.如果你只是做了 function(i){return i;} 的值 i 将是数据对象,因为这就是 d3 函数总是作为第一个参数传递的,不管你叫它什么.传入参数值的是外部函数.参数名只存在于内部函数中.

That's why, if you want to use the index number in d3, you have to write function(d,i){return i;}. If you just did function(i){return i;} the value of i would be the data object, because that's what the d3 functions always pass as the first parameter, regardless of what you call it. It's the outside function that passes in the values of the parameters. The parameter names only exist inside the inner function.

必要的警告和例外:

  • 我说过 map 函数并不关心传入函数需要多少个参数.确实如此,但其他外部函数可以使用传入函数的 .length 属性来确定预期的参数数量并相应地传递不同的参数值.

  • I said that the map function doesn't care how many parameters a passed-in function expects. That's true, but other outer functions could use the passed-in function's .length property to figure out how many parameters are expected and pass different parameter values accordingly.

您不必为函数命名参数来访问传入的参数值.您还可以使用该函数内的 arguments 列表访问它们.因此,另一种编写大写映射函数的方法是:

You don't have to name arguments for a function in order to access passed-in parameter values. You can also access them using the arguments list inside that function. So another way of writing the uppercase mapping function would be:

function noParamUpperCase() {

       return arguments[0].toUpperCase();
}

但是,请注意,如果外部函数使用参数的数量来确定要传递给内部函数的值,则该函数似乎不接受任何参数.

However, note that if an outer function is using the number of parameters to determine what values to pass to the inner function, this function will appear not to accept any arguments.

你会注意到我上面没有提到方法链.那是因为它是一个完全独立的代码模式,恰好在 d3 和 JQuery 中也经常使用.

You'll notice that nowhere above did I mention method chaining. That's because it's a completely separate code pattern, that just happens to also be used a lot in d3 and JQuery.

让我们回到第一个例子,它创建了Hello WORLD!"来自你好"和世界!".如果您想创建HELLO World!"怎么办?反而?你可以这样做

Let's go back to the first example, which created "Hello WORLD!" out of "Hello " and "World!". What if you wanted to create "HELLO World!" instead? You could do

var a = "Hello ",
    b = "World!";

var c = a.toUpperCase().concat( b ); //c = "HELLO World!"

在创建 c 时发生的第一件事是调用 a.toUpperCase() 方法.该方法返回一个字符串(HELLO"),它像所有其他字符串一样有一个 .concat() 方法.所以 .concat(b) 现在被作为 返回字符串 的方法调用,而不是原始字符串 a 的方法.结果是 b 被连接到 a 的大写版本的末尾:HELLO World!".

The first thing that happens in the creation of c is the a.toUpperCase() method gets called. That method returns a string ("HELLO "), which like all other strings has a .concat() method. So .concat(b) is now getting called as a method of that returned string, not of the original string a. The result is that b gets concatenated to the end of the uppercase version of a: "HELLO World!".

在那种情况下,返回的值是一个与起始对象类型相同的 new 对象.在其他情况下,它可能是完全不同类型的数据.

In that case, the returned value was a new object of the same type as the starting object. In other cases, it could be a completely different type of data.

var numberArray = [5, 15];

var stringArray = numberArray.toString().split(","); //stringArray = ["5", "15"]

我们从一组数字开始,[5,15].我们调用数组的 toString() 方法,该方法生成数组的格式良好的字符串版本:5,15".该字符串现在具有所有可用的字符串方法,包括 .split(),它将字符串拆分为围绕指定拆分字符(在本例中为逗号)的子字符串数组.

We start with an array of numbers, [5,15]. We call the array's toString() method, which produces a nicely formatted string version of the array: "5,15". This string now has all it's string methods available, including .split(), which splits a string into an array of substrings around a specified split character, in this case the comma.

您可以将此称为一种方法链,即调用另一个方法返回的值的方法.但是,当使用方法链来描述 Javascript 库的特性时,关键在于方法的返回值是调用该方法的同一对象.

You could call this a type of method chaining, calling a method of a value returned by another method. However, when method chaining is used to describe a feature of a Javascript library, the key aspect is that the returned value of the method is the same object that called the method.

所以当你这样做时

d3.select("body").style("background", "green");

d3.select("body") 方法创建一个 d3 选择对象.该对象有一个 style() 方法.如果您使用 style 方法来设置一个样式,那么您实际上不需要从中获取任何信息.它本可以设计为根本不返回任何值.相反,它返回该方法所属的对象(this 对象).因此,您现在可以调用该对象的另一个方法.或者你可以将它分配给一个变量.或者两者兼而有之.

The d3.select("body") method creates a d3 selection object. That object has a style() method. If you use the style method to set a style, you don't really need any information back from it. It could have been designed not to return any value at all. Instead, it returns the object that the method belongs to (the this object). So you can now call another method of that object. Or you could assign it to a variable. Or both.

var body = d3.select("body").style("background", "green")
                            .style("max-width", "20em");

然而,您必须始终注意返回相同对象的方法.比如在很多d3代码示例中你会看到

However, you always have to be aware of the methods which don't return the same object. For example, in a lot of d3 code examples you see

var svg = d3.select("svg").attr("height", "200")
                          .attr("width", "200")
                          .append("g")
                          .attr("transform", "translate(20,20)");

现在,方法 append("g") 不会返回 元素的相同选择.它返回一个由 元素组成的 new 选择,然后给它一个转换属性.分配给变量 svg 的值是链的 最后 返回值.因此,在后面的代码中,您必须记住变量 svg 实际上并不是引用 <svg> 元素的选择,而是引用 .我觉得这很令人困惑,所以我尽量避免使用变量名 svg 来选择实际上不是 <svg> 元素的选择.

Now, the method append("g") doesn't return the same selection of the <svg> element. It returns a new selection consisting of the <g> element, which is then given a transform attribute. The value that gets assigned to the variable svg is the last return value from the chain. So in later code, you would have to remember that the variable svg doesn't actually refer to a selection of the <svg> element, but to the <g>. Which I find confusing, so I try to avoid ever using the variable name svg for a selection that isn't actually an <svg> element.

当然,任何那些 d3 .attr().style() 方法都可以将函数作为第二个参数而不是字符串.但这不会改变方法链的工作方式.

Of course, any of those d3 .attr() or .style() methods could have taken a function as the second parameter instead of a string. But that wouldn't have changed how method chaining works.

这篇关于javascript方法链中的输入参数是如何填充的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆