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
,按顺序发生以下情况:
- 浏览器开始parsing
concat
方法,但是需要弄清楚给出的参数。 - 该参数包含一个方法调用,所以执行
b
的toUpperCase()
方法,返回string“WORLD!”。 - 返回的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
函数使用的对象的重要参数。