input参数如何在javascript方法链中填充?

我想要真正了解JavaScript的工作原理。 在方法链接期间,有时一个方法返回到另一个具有命名input参数的方法。

例如,在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? 

我从实际的编码知道,这些input参数的名称可以是任何东西。 但是input参数文件的数据来自哪里呢? 它只是指从链中的先前方法返回的数据?

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

使用函数作为方法参数

首先,一个更正:你给的例子不是 “一个方法返回到具有一个命名input参数的另一个方法”的例子。 它们是一个函数作为另一个方法的input参数的例子。

为了澄清,我将给你一个例子,其中一个函数的返回值用作另一个函数的input。

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

为了创buildc ,按顺序发生以下情况:

  1. 浏览器开始parsingconcat方法,但是需要弄清楚给出的参数。
  2. 该参数包含一个方法调用,所以执行btoUpperCase()方法,返回string“WORLD!”。
  3. 返回的string成为现在可以执行的concat()方法的参数。

concat()方法而言,结果与编写c = a.concat("WORLD!")的结果是一样的 – 它不关心string“WORLD!” 是由另一个函数创build的。

你可以知道返回值 b.toUpperCase()是作为parameter passing的,而不是函数本身,因为函数名的末尾有括号。 函数名称后面的圆括号告诉浏览器执行该函数,只要它已经计算出该函数的任何参数的值。 如果没有括号,函数会被视为其他任何对象,并且可以作为参数或variables传递,而不用做任何事情。

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

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

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

函数parseFloat()是一个内置函数,它接受一个string并试图从中找出一个数字。 请注意,当我将它传递给map函数时,我只是将它作为variables名传递,而不是在末尾用圆括号执行。 它由map函数执行,它是决定它获取什么参数的map函数。 对parseFloat的每个调用的结果都由map函数指定给它们在结果数组中的位置。

具体而言,map函数使用三个参数执行parseFloat :数组中的元素,数组中的元素的索引以及整个数组。 parseFloat函数只使用一个参数,所以第二个和第三个参数被忽略。 (然而,如果你尝试用parseInt做同样的事情,你会得到意想不到的结果,因为parseInt 确实使用了第二个参数 – 它被视为整数的基数(“base”)。

map函数并不关心传入函数需要多less个参数,当然也不关心在函数中使用哪个variables名。 如果你自己写地图函数,它看起来像这样:

 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方法的局限性是只有很less的Javascript函数可以直接调用,只是传递一个值作为参数。 大多数函数是特定对象的方法。 例如,我们不能使用toUpperCase方法作为map的参数,因为toUpperCase只作为string对象的一个​​方法存在,并且只作用于特定的string对象,而不是map函数可能赋予的任何参数。 为了将一个string数组映射为大写字母,你需要创build你自己的函数,它将以地图函数使用它的方式工作。

 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这一次,你不需要单独声明它,并给它一个名字。 你可以直接使用它作为匿名函数

 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方法在select的每个元素上调用你的函数,指定的值作为第一个,第二个和第三个参数。

那么参数名称呢? 正如你所提到的,他们可以是任何你想要的。 我可以在我的函数中使用非常长的描述性参数名称:

 function myUpperCase(stringElementFromArray, indexOfStringElementInArray, ArrayContainingStrings) { return stringElementFromArray.toUpperCase(); } 

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

 function indirectUpperCase (stringIDontUse, index, array) { return array[index].toUpperCase(); } 

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


必要的注意事项和例外:

  • 我说map函数并不关心传入函数期望的参数。 这是真的,但其他外部函数可以使用传入函数的.length属性来计算出预期的参数数量,并相应地传递不同的参数值。

  • 您不必为函数命名参数以访问传入的参数值。 您也可以使用该arguments中的arguments列表来访问它们。 所以写大写映射函数的另一种方法是:

     function noParamUpperCase() { return arguments[0].toUpperCase(); } 

    但是,请注意,如果一个外部函数正在使用参数数量来确定传递给内部函数的值,则此函数将不会接受任何参数。


方法链接

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

我们回到第一个例子,它创build了“Hello WORLD!” 出“你好”和“世界!”。 如果你想创造“HELLO世界”呢? 代替? 你可以做

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

在创buildc中发生的第一件事就是调用a.toUpperCase()方法。 该方法返回一个string(“HELLO”),像所有其他string有一个.concat()方法。 所以.concat(b)现在被调用作为返回string的方法,而不是原始stringa 。 结果是b被连接到大写的“HELLO WORLD!”的末尾。

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

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

我们从数字开始, [5,15] 。 我们调用数组的toString()方法,该方法产生一个格式良好的数组string版本:“5,15”。 这个string现在已经有了所有可用的string方法,包括.split() ,它将string拆分为指定分隔字符(本例中为逗号)的子string数组。

你可以调用这种方法链接,调用另一个方法返回的值的方法。 但是,当使用方法链来描述Javascript库的一个特性时,关键的一点是方法的返回值是调用方法的同一个对象

所以,当你这样做

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

d3.select("body")方法创build一个d3select对象。 该对象有一个style()方法。 如果使用样式方法来设置样式,则不需要任何信息。 它本来可以被devise成不会返回任何价值的。 而是返回方法所属的对象( this对象)。 所以你现在可以调用该对象的另一种方法。 或者你可以把它分配给一个variables。 或两者。

 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")方法append("g") 不会返回与<svg>元素相同的select。 它返回一个由<g>元素组成的新的select,然后给它一个transform属性。 分配给variablessvg的值是链中的最后一个返回值。 所以在后面的代码中,你必须记住,variablessvg实际上并不是指向<svg>元素的select,而是指向<g> 。 我感到困惑,所以我尽量避免使用variables名svg作为一个实际上不是<svg>元素的select。

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

如果我理解正确

这些数据是指什么? 它是如何填充的?

你是说它是如何工作的? 这取决于如何调用callback。 例如:

 function Lib() {} Lib.prototype.text = function(callback) { var data = 'hello world'; callback(data); // `data` is the first parameter return this; // chain }; var lib = new Lib(); lib.text(function(data){ console.log(data); //=> "hello world" }); 

d对应于先前方法调用中首先由.data(data).enter()传入的数据集中的单个元素。 发生什么事是d3隐式地循环了映射到数据的相应dom元素,因此是数据驱动文档或d3的整个概念。

也就是说,对于数据中的每个元素,当您调用selectAll()您应该会看到在数据集中存在与dom相同数量的元素。 所以当你打一个.attr()调用时,与dom中的一个元素对应的单个数据被传递给该函数。 当你正在阅读

 .append("rect").attr("width",function(datum){})... 

你应该把它看作是你的整个数据绑定到你的select的一个元素的单一迭代。

我有点不确定,我已经回答了你的问题,因为它似乎是关于声明性/函数式编程以及方法链的混合问题。 上面的答案是关于d3的声明性/function性方面。 下面的答案是参考方法链接。

下面是一个很好的例子,它提供了对Mike Bostock在其精彩的文章http://bost.ocks.org/mike/chart/中概述的方法链接的更深入的了解。;

我build议你阅读这里find的d3 api( https://github.com/mbostock/d3/wiki/API-Reference ),以便进一步了解每个函数的运行方式。 例如selection.filter另外,我强烈build议你进入一个玩具的例子,比如你已经给出的玩具来进一步理解发生了什么。

方法链接关于d3可重复使用的图表。

 function SomeChart() { var someProperty = "default value of some sort"; function chartObject() { } chartObject.setSomeProperty(propertyValue) { if (!arguments.length) return someProperty; someProperty = property; return chartObject; } return chartObject; } var chart = SomehChart(); 

现在什么是图表?

 chart = chart.setSomeProperty("some special value"); 

现在什么是图表?

 chart = chart.setSomeProperty(); 

现在什么是图表?

以下是什么区别?

 var chart = SomeChart().setSomeProperty("some special value"); 

 var chart = SomeChart(); chart.setSomeProperty(); 

答案是,除了chart = chart.setSomeProperty();

在d3中,d3.select(“body”)会返回一些{search_element:document.body,selectAll:function,...}所以用点符号来调用前一个对象的可用函数。 它可能只是返回类本身。 所以所有的方法都可以以任何顺序用下一个点。 但是在Ajax中,必须调用一些函数,以填充done函数使用的对象的重要参数。