微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

输入参数如何填充在JavaScript方法链中?

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

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

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中,模式如下所示:

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

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

解决方法

两个不同的话题,所以我会分别解释一下:

使用函数作为方法参数

首先,修正:您给出的示例不是“一个方法返回到另一个具有命名输入参数的方法”的示例.它们是将函数作为另一种方法的输入参数给出的示例.

为了弄清楚,我会给你一个例子,其中一个函数的返回值被用作另一个函数的输入.

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

var c = a.concat( b.toupperCase() ); //c = "Hello World!"

为了创建c,按照以下顺序发生:

>浏览器开始解析concat方法,但需要弄清楚给出什么参数.
>该参数包括一个方法调用,所以b的toupperCase()方法被执行,返回字符串“WORLD!”.
>返回的字符串成为一个现在可以执行的concat()方法的参数.

就concat()方法而言,结果与你写的是c = a.concat(“WORLD!”)一样 – 它不在乎字符串“WORLD!”是由另一个功能创建的.

您可以知道b.toupperCase()的返回值作为参数传递,而不是函数本身,因为函数名末尾的括号.函数名后面的括号会告诉浏览器执行该函数,只要它已经找出了该函数的任何参数的值.没有括号,该函数被视为任何其他对象,并且可以作为参数或变量传递,而不做任何事情.

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

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

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

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

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

具体来说,map函数使用三个参数执行parseFloat:数组中的元素,数组中元素的索引以及整个数组. parseFloat函数仅使用一个参数,因此第二和第三个参数将被忽略. (如果您尝试使用parseInt做同样的事情,则会得到意想不到的结果,因为parseInt会使用第二个参数 – 它被视为整数的基数(“base”)).

映射函数不关心传入函数所期望的参数的数量,并且它无疑在该函数内部使用哪些变量名.如果你自己在编写地图函数,就会看起来像这样:

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函数调用,并给出参数,而不知道它是什么或它做什么.参数以特定顺序给出 – 元素,索引,数组 – 但不链接到任何特定的参数名称.

现在,像map方法这样的限制就是可以直接调用很少的Javascript函数,只是传递一个值作为参数.大多数函数是特定对象的方法.例如,我们不能使用toupperCase方法作为映射的参数,因为toupperCase仅作为字符串对象的方法存在,并且仅对该特定字符串对象起作用,而不是映射函数可能赋予的任何参数.为了将字符串数组映射成大写,您需要创建自己的函数,该函数的工作方式是使用map函数.

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

function myUpperCase(s,array) {

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

}

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

但是,如果你一直使用这个函数myUpperCase,那么你不需要单独声明它并给它一个名字.您可以直接使用它作为匿名函数.

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

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

开始看起来很熟悉?这是由许多d3和jQuery函数使用的结构 – 您可以传递函数作为函数名称或作为匿名函数,并且d3 / JQuery方法在选定的每个元素上调用您的函数,传递指定的值作为第一,第二和第三参数.

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

function myUpperCase(stringElementFromArray,indexOfStringElementInArray,ArrayContainingStrings) { 

         return stringElementFromArray.toupperCase();
}

传递给函数的值将是相同的,完全基于地图函数通过它们的顺序.实际上,由于我从不使用索引参数或数组参数,我可以将它们从我的函数声明,并使用第一个参数.其他值仍然通过地图传递,但它们被忽略.但是,如果我想使用map传递的第二或第三个参数,我必须为第一个参数声明一个名称,以保持编号的顺序:

function indirectUpperCase (stringIDontUse,index,array) {

         return array[index].toupperCase();
}

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

必需的注意事项和例外:

>我说地图函数不关心传入函数所期望的参数的数量.这是真的,但其他外部函数可以使用传入函数的.length属性来确定预期的参数数量,并相应地传递不同的参数值.
>您不必为函数命名参数,以便访问传入的参数值.您也可以使用该函数中的参数列表访问它们.所以写大写映射函数的另一种方式是:

function noparamUpperCase() {

       return arguments[0].toupperCase();
}

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

方法

你会注意到,上面没有提到方法链接.这是因为它是一个完全独立的代码模式,恰恰也在d3和jQuery中也被使用了很多.

我们回到第一个例子,创建了“Hello WORLD!”出自“你好”和“世界!”.如果你想创建“HELLO World!”怎么办?代替?你可以做

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

var c = a.toupperCase().concat( b ); //c = "Hello World!"

在创建c时发生的第一件事是a.toupperCase()方法调用.该方法返回一个字符串(“HELLO”),其中所有其他字符串都具有.concat()方法.所以.concat(b)现在被调用为返回的字符串的方法,而不是原来的字符串a.结果是b被连接到大写版本的结尾:“HELLO World!”.

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

var numberArray = [5,15];

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

我们从数组开始,[5,15].我们调用数组的toString()方法,它产生一个格式很好的字符串版本的数组:“5,15”.此字符串现在拥有所有的字符串方法可用,包括.split(),它将一个字符串分成一个指定的分割字符周围的子字符串数组(在本例中为逗号).

你可以称之为一种方法链接,调用另一种方法返回的值的方法.然而,当方法链接用于描述Javascript库的特征时,关键的方面是方法的返回值是与该方法相同的对象.

所以当你做

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

d3.select(“body”)方法创建一个d3选择对象.该对象具有一个style()方法.如果您使用style方法设置样式,则不需要任何信息.它可能被设计为不返回任何价值.相反,它返回该方法所属的对象(该对象).所以你可以调用该对象的另一种方法.或者您可以将其分配给变量.或两者.

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

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

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

现在,append(“g”)方法不返回< svg>元件.它返回由< g>元素,然后给出一个transform属性.分配给变量svg的值是来自链的最后一个返回值.所以在后面的代码中,你必须记住,变量svg实际上并不指向< svg>元素,但是与< g&gt ;.我觉得很混乱,所以我尽量避免使用变量名svg作为实际上并不是< svg>的选择.元件.

当然,这些d3 .attr()或.style()方法中的任何一个都可以作为第二个参数而不是一个字符串.但是,这样做并不会改变方法链接的工作原理.

原文地址:https://www.jb51.cc/js/152827.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐