需要帮助理解JavaScript中的函数调用吗
我很难理解书中的一些示例代码(在线版免费) 示例代码是用于计算给定直径的周长的函数。它显示了用名称绑定值的不同方法。根据这本书,一种方法是:需要帮助理解JavaScript中的函数调用吗,javascript,Javascript,我很难理解书中的一些示例代码(在线版免费) 示例代码是用于计算给定直径的周长的函数。它显示了用名称绑定值的不同方法。根据这本书,一种方法是: ( (diameter) => ((PI) => diameter * PI)(3.14159265) )(2); // calculates circumference given diameter 2 它还指出: 嗯,这样做的缺点是,调用函数通常比计算表达式要昂贵得多。每次调用外部函数时,我们都会调用内部函数。我们可以通过写作来解决
(
(diameter) =>
((PI) => diameter * PI)(3.14159265)
)(2);
// calculates circumference given diameter 2
它还指出:
嗯,这样做的缺点是,调用函数通常比计算表达式要昂贵得多。每次调用外部函数时,我们都会调用内部函数。我们可以通过写作来解决这个问题
我无法理解它是如何通过调用两个函数来避免这种情况的,这两个示例中不是都有两个函数调用吗?
它们之间有什么区别?这本书可能会建议JavaScript编译器更可能在第二种方法中使用PI函数。但这只有在我们使用不同的动态直径多次调用这些方法时才有意义。否则,编译器也可能内联diameter函数 归根结底,从性能角度看,真正重要的是JavaScript引擎到底在用这些函数做什么 下面的测试表明,这两种方法几乎没有任何区别。至少在我的盒子上 您可能希望执行更多的迭代,但请注意,这显然是非常缓慢的
//这是一个热身,以确保两个方法都通过
//即时(JIT)编译,适用于以这种方式进行编译的浏览器。
试验1(1E5);
试验2(1E5);
//进行实际测试
log('Method#1:'+test1(1E6).toFixed(2)+'ms');
log('Method#2:'+test2(1E6).toFixed(2)+'ms');
功能测试1(iter){
var res,n,ts=performance.now();
对于(n=0;n((圆周率)=>直径*圆周率)(3.14159265)
)(Math.random()*10);
}
返回性能。now()-ts;
}
功能测试2(iter){
var res,n,ts=performance.now();
对于(n=0;n(直径)=>直径*圆周率)(3.14159265)
)(Math.random()*10);
}
返回性能。now()-ts;
}
这看起来可能有点让人困惑,因为我认为它解释得不太清楚。或者,更确切地说,我不认为它是用典型的JavaScript方式解释的
让我们把这些例子分解一下
第一个例子
崩溃
这样安排的话,如果你调用这个代码
PI
作为参数,并使用它来计算周长。此函数立即被调用curry
的技术——它并不特定于JavaScript,但在JavaScript世界中仍被广泛称为该名称。对咖喱的简要概述
//non-curried
function add(a, b) { // or, in ES6: (a, b) => a + b;
return a + b;
}
//curried
function curryAdd(a) { //or in ES6: (a) => (b) => a + b;
return function(b) {
return a + b;
}
}
//invocation
add(2, 3); // 5
curryAdd(2)(3); // 5
我将不详细介绍,但本质上,一个接受多个参数的curried函数可以传递得更少,它将返回一个可以接受其余参数的新函数。当所有参数都满足时,您将得到结果-在形式表示法中,curryAdd
函数将表示为curryAdd::Number->Number->Number
-这是一个函数,它接受一个数字并返回另一个函数,该函数接受一个数字,最后返回另一个数字。关于你为什么要这样做,这里有一个例子——虽然很琐碎,但它的要点是:
//add5:: Number -> Number
add5 = curryAdd(5);
add5(3); // 8
add5(10); // 15
[1, 2, 3].map(add5); // [6, 7, 8]
Currying有点像函数的部分分配,但是
崩溃
话虽如此,我们来看第二个例子:
//curryMultiply :: Float -> Float -> Float
(PI) => (diameter) => diameter * PI
//another way to write it:
//(a) => (b) => a * b
希望这能澄清一下到底发生了什么。我将把示例的其余部分重新写入实际发生的情况:
// calculateCircumference :: Float -> Float
var calculateCircumference = curryMultiply(3.14159265);
calculateCircumference(2); //6.2831853
第二个示例的代码与上面的代码相同。它避免了调用函数两次,因为外部函数(我称之为curryMultiply)只被调用一次-无论何时调用calculateCircumference
函数,您都只是在计算内部函数。您应该看看(IIFE);那是一个
基本上:您声明一个函数并立即调用它。。。这有时被用作创建词法范围的权宜之计,只是为了避免全局变量
//我们自信的方式。。。
函数logFoo(){console.log(1,'FOO');}
logFoo();
//使用和生活
(function(){console.log(2,'FOO');}());
//或者为了更好的可读性
(function(){console.log(2,'FOO');})()代码>我认为重点放在短语“每次调用外部函数…”上,这确实令人困惑,因为外部函数在示例中只调用一次(作为IEFE)。通过这个例子,人们应该能够更好地理解差异:
const circumference = (diameter) =>
((PI) =>
diameter * PI
)(3.14159265);
console.log(circumference(2));
console.log(circumference(5));
但显然,作者不想在这里引入变量声明,所以可能会编写它
((circumference) => {
console.log(circumference(2));
console.log(circumference(5));
})(((PI) =>
(diameter) =>
diameter * PI
)(3.14159265));
同样的效果:-)我可能弄错了,但据我所知,他们只是改变了函数的顺序,实际上并没有在性能方面节省任何东西。说一个比另一个“贵得多”有些误导。除了一小部分例外,这根本没有实际意义。我不会相信一本作者似乎不知道的书
const circumference = (diameter) =>
((PI) =>
diameter * PI
)(3.14159265);
console.log(circumference(2));
console.log(circumference(5));
const circumference = ((PI) =>
(diameter) =>
diameter * PI
)(3.14159265);
console.log(circumference(2));
console.log(circumference(5));
((circumference) => {
console.log(circumference(2));
console.log(circumference(5));
})(((PI) =>
(diameter) =>
diameter * PI
)(3.14159265));