【问题标题】:What is the JavaScript convention for no operation?什么是无操作的 JavaScript 约定?
【发布时间】:2024-05-01 12:55:02
【问题描述】:

什么是无操作的 JavaScript 约定?就像 Python pass 命令一样。

  • 一个选项只是一个空函数:function() {}
  • jQuery 提供$.noop(),它只是调用上面的空函数。
  • 是否可以简单地输入值false0

在上下文中......所有这些工作都不会在 Chrome 中引发错误:

var a = 2;
(a === 1) ? alert(1) : function() {};
(a === 1) ? alert(1) : $.noop();
(a === 1) ? alert(1) : false;
(a === 1) ? alert(1) : 0;

编辑:很多人的回应是,“不要这样做!改变代码结构!”这让我想起了一篇帖子,有人问如何嗅探浏览器。他收到一连串帖子说“不要那样做!这是邪恶的”,但没有人告诉他如何嗅探浏览器。这不是代码审查。想象一下,您正在处理无法更改的遗留代码,并且没有传入某些函数,它会抛出错误。或者,简单地说,这就是客户想要的方式,他们付钱给我。所以,恭敬地,请回答问题:在 JavaScript 中指定“无操作”函数的最佳方法是什么?

EDIT2:其中一个怎么样?

true;
false;
0;
1;
null;

【问题讨论】:

  • 为什么不是简单的 if 语句?
  • 0 的那些替代品实际上都没有更好(或更差)。我会说正确的做法是if (a === 1) doSomething();,而不是在没有意义的情况下使用? :
  • 您在滥用三元运算符并产生副作用。如果必须,请执行if (statement) action; else ;
  • 是的 false0 会起作用; null 是表达无操作的好方法。
  • 我希望三元有一些我们由于某种原因看不到的必要性......看起来你把你的代码复杂化来感觉很酷(我们都做到了!)

标签: javascript function


【解决方案1】:

要回答最初的问题,纯 Javascript 中 noop 函数的最优雅和简洁的实现(也讨论过here)是 Function.prototype。这是因为:

  1. Function.prototype 是一个函数:

typeof Function.prototype === "function" // returns true

  1. 它可以作为函数调用,基本上什么都不做,如下所示:
setTimeout(function() {
      console.log('Start: ', Date.now());
      Function.prototype();
      console.log('End  : ', Date.now());
    }, 1000);

虽然这是一个“真正的 noop”,因为大多数浏览器似乎没有执行以这种方式定义的 noop(从而节省 CPU 周期),但可能存在一些与此相关的性能问题(正如其他人在cmets 或其他答案)。

不过,话虽如此,您可以轻松定义自己的 noop 函数,事实上,许多库和框架也提供了 noop 函数。下面是一些例子:

var noop = function () {};           // Define your own noop in ES3 or ES5
const noop = () => {};               // Define in ES6 as Lambda (arrow function)
setTimeout(noop, 10000);             // Using the predefined noop

setTimeout(function () {} , 10000);  // Using directly in ES3 or ES5
setTimeout(() => {} , 10000);        // Using directly in ES6 as Lambda (arrow function)

setTimeout(angular.noop, 10000);     // Using with AngularJS 1.x
setTimeout(jQuery.noop, 10000);      // Using with jQuery

这里按字母顺序列出了 noop 函数的各种实现(或相关讨论或谷歌搜索):

AngularJS 1.x,Angular 2+(好像没有原生 实现 - 使用你自己的如上所示),EmberjQueryLodashNodeJSRamdaReact(似乎没有原生实现 -使用你自己的,如上所示),RxJSUnderscore

底线:虽然 Function.prototype 是在 Javascript 中表达 noop 的一种优雅方式,但是,它的使用可能存在一些性能问题。因此,您可以定义和使用自己的(如上所示)或使用您可能在代码中使用的库/框架定义的一个。

【讨论】:

  • 这应该是答案。如果您不需要超时并希望它立即执行,甚至可以使用Function.prototype()
  • setTimeout(Function(), 10000);
  • @iegik:+1。是的,你是对的。以下所有内容都是等效的: setTimeout(function () {} , 10000);或 setTimeout(new Function(), 10000);或 setTimeout(Function(), 10000);或 setTimeout(函数,10000);因为 Javascript 语法和 Function 构造函数实现允许这些构造。
  • 在 ES6 中或使用 babel 或其他转译器 (( )=>{};) 在技术上纯 JS 并且比 function () {} 短得多,但完全相同。
  • 我有兴趣找到一个类似 no-op 的构造来有效地关闭我程序中的调试代码。在 C/C++ 中,我会使用宏。我测试了将我的 fn 分配给 Function.prototype 并对其进行计时,将结果与简单地在函数中包含一个 if 语句以立即返回进行比较。我还将这些与从循环中完全删除函数的结果进行了比较。不幸的是,Function.prototype 根本没有表现好。调用一个空函数在性能上要优越得多,甚至比调用内部简单的“if”测试返回的函数还要快。
【解决方案2】:

最简洁和高效的 noop 是一个空箭头函数()=>{}

Arrow functions 可以在除 IE 之外的所有浏览器中本地工作(如果必须,可以使用 babel transform):


()=>{}Function.Prototype

  • 在 Chrome 67 中,()=>{}Function.prototype87%
  • 在 Firefox 60 中,()=>{}Function.prototype25%
  • ()=>{} 在 Edge (2018 年 6 月 15 日)中比 Function.prototype85%
  • ()=>{}Function.prototype65% 的代码

下面的测试使用箭头函数加热到Function.prototype,但箭头函数是明显的赢家:

const noop = ()=>{};
const noopProto = Function.prototype;

function test (_noop, iterations) {
    const before = performance.now();
    for(let i = 0; i < iterations; i++) _noop();
    const after = performance.now();
    const elapsed = after - before;
    console.info(`${elapsed.toFixed(4)}MS\t${_noop.toString().replace('\n', '')}\tISNOOP? ${_noop() === undefined}`);
    return elapsed;
}

const iterations = 10000000
console.info(`noop time for ${iterations.toLocaleString()} iterations`)
const timings = {
    noop: test(noop, iterations),
    noopProto: test(noopProto, iterations)
}

const percentFaster = ((timings.noopProto - timings.noop)/timings.noopProto).toLocaleString("en-us", { style: "percent" });
console.info(`()=>{} is ${percentFaster} faster than Function.prototype in the current browser!`)

【讨论】:

  • 我创建了一个 jsPerf 测试来比较一些选项:noop-function。似乎在 Firefox 54 中,所有替代方案似乎都差不多;在 Chromium 58 中,Function.prototype 的速度要慢得多,但其他选项都没有明显的优势。
  • _=&gt;{} 少一个字符,做同样的事情。
  • @RossAttrill *similar thing - _=&gt;{} 有一个参数。如果有人使用具有相当严格配置的 TypeScript,那将无法编译。如果你调用它,你的 IDE 可能会告诉你它接受一个可以是任何类型的参数(包括 JavaScript,尤其是 vscode 中的 TypeScript)。
【解决方案3】:

无论你想达到什么目的都是错误的。三元表达式不应用作完整语句,只能在表达式中使用,因此您的问题的答案是:

没有你的建议,而是做:

var a = 2;
if (a === 1)
    alert(1)
// else do nothing!

那么代码很容易理解、可读并且尽可能高效。

既然可以简单,为什么要让它变得更困难?

编辑:

那么,“无操作”命令是否基本上表示代码结构较差?

你没有理解我的意思。以上都是关于三元表达式x ? y : z

但是,无操作命令在 Javascript 等高级语言中没有意义。

在诸如汇编或 C 之类的低级语言中,它通常被用作一种使处理器为了计时目的而对一条指令不执行任何操作的方法。

在 JS 中,无论你是做0;null;function () {}; 还是空语句,解释器在读取它时很有可能会忽略它,但是之前 em> 它被解释了,所以最后,你只会让你的程序加载得更慢,时间很短。 Nota Bene:我假设是这样,因为我没有参与任何广泛使用的 JS 解释器,并且每个解释器都有自己的策略。

如果您使用更复杂的东西,例如$.noop()var foo = function () {}; foo(),那么解释器可能会执行无用的函数调用,最终会破坏函数堆栈的几个字节和几个循环。

我看到诸如$.noop() 之类的函数存在的唯一原因是,如果它不能调用该回调,它仍然能够为某个会引发异常的事件函数提供一个回调函数。但是,它必然是您需要提供的功能,并且给它命名 noop 是个好主意,因此您是在告诉您的读者(可能是 6 个月后的您)您故意提供一个空功能。

归根结底,没有“劣”或“优”的代码结构。你使用工具的方式要么是对的,要么是错的。在你的例子中使用三元组就像你想拧螺丝时使用锤子一样。它会起作用,但你不确定你能在那个螺丝上挂什么东西。

可以被认为是“低级”或“高级”的是您在代码中使用的算法和想法。但这是另一回事。

【讨论】:

  • 那么,“无操作”命令是否基本上表明代码结构较差?
  • @kmiklas:我不能说我在汇编程序之外找到了对 NOOP 的合法需求。
  • @zmo:我理解你关于你的 cmets 如何与三元表达式相关的观点;每个人似乎都忽略了我的观点,这只是调用“无操作”功能的说明性示例。
  • 当然有理由想要/需要无操作功能。不要根据你对某事的 12 秒深入思考做出广泛的概括。 JavaScript 是一种使函数作为变量完全可控的语言。假设您需要在没有明确测试的情况下禁用某个功能?这将是完美的。如果您有一个经常出现的函数,例如调试语句,最好能够通过将函数重新分配给 no-op 来关闭它,而不必在每次函数出现时添加额外的代码来测试某个变量.
  • @bearvarine 感谢您对我的高度评价:-D。您的评论绝对正确,但这并不是真正的代码设计,只是使用黑客的模型设计。您所描述的也称为猴子补丁,这是有原因的;-)
【解决方案4】:

我认为 jQuery noop() 的主要目的是通过在请求的函数不可用时提供默认函数来防止代码崩溃。例如,考虑以下代码示例,如果未定义 fakeFunction,则选择 $.noop,从而防止对 fn 的下一次调用崩溃:

var fn = fakeFunction || $.noop;
fn() // no crash

然后,noop() 允许通过避免在代码中各处多次编写相同的空函数来节省内存。顺便说一句,$.noopfunction(){} 短一点(每个令牌节省 6 个字节)。因此,您的代码与空函数模式之间没有任何关系。如果您愿意,请使用nullfalse0,在您的情况下不会有副作用。此外,值得注意的是这段代码...

true/false ? alert('boo') : function(){};

... 完全没用,因为您永远不会调用该函数,而这个...

true/false ? alert('boo') : $.noop();

... 更没用,因为你调用了一个空函数,这和...完全一样

true/false ? alert('boo') : undefined;

让我们将三元表达式替换为if 语句,看看它有多没用:

if (true/false) {
    alert('boo');
} else {
    $.noop(); // returns undefined which goes nowhere
}

你可以简单地写:

if (true/false) alert('boo');

甚至更短:

true/false && alert('boo');

为了最终回答你的问题,我想“传统的无操作”是从未写过的。

【讨论】:

  • 但是有时你必须提供一个函数。例如,使用 promise then(fn, fn) 有时你想提供第二个函数而不是第一个函数。因此,您需要一些东西作为占位符...mypromise.then($.noop, myErrorhandler);
【解决方案5】:

使用Function.prototype 超过() =&gt; {} 绝对没有问题或性能损失。

Function.prototype 的主要好处是拥有一个单例函数,而不是每次都重新定义一个新的匿名函数。在定义默认值和记忆时使用像Function.prototype 这样的空操作尤其重要,因为它为您提供了一个永不改变的一致对象指针。

我推荐Function.prototype而不是Function的原因是因为它们不一样:

Function() === Function()
// false

Function.prototype() === Function.prototype()
// true

此外,其他答案的基准具有误导性。事实上,Function.prototype 的执行速度比 () =&gt; {} 快,具体取决于您编写和运行基准测试的方式:

You can’t trust JS benchmarks

不要根据基准设置代码样式;做任何可维护的事情,让解释器找出长期优化的方法。

【讨论】:

    【解决方案6】:

    我用:

     (0); // nop
    

    测试本次运行的执行时间为:

    console.time("mark");
    (0); // nop
    console.timeEnd("mark");
    

    结果:标记:0.000ms

    使用Boolean( 10 &gt; 9) 可以将其简化为简单的( 10 &gt; 9),它返回true。提出使用单个操作数的想法,我完全期望(0); 会返回false,但它只是返回参数,可以通过在控制台执行此测试来查看。

    > var a = (0);
    < undefined
    > a
    < 0
    

    【讨论】:

    • 许多尝试此操作的人将获得argument 0 is not a function
    • 您只是分配了 0。IMO noop 应该是一个使用 undefined 结果评估的函数。
    • 这个说法如何(0);分配一个 0?,var 和等号在哪里? (0) 前面没有函数名,所以我看不出这怎么可能是一个参数?如果您尝试 typeof ( (0) ),它会以数字形式返回。当我回答这个问题时,我提供了一个用来模仿 NOP 的语句,但我愿意将我的答案更改为简单地使用 ;表示一个空语句
    • 我认为 (0);是OP问题的可接受解决方案。我认为人们会感到困惑,因为他们在寻找一个什么都不做的函数而不是什么都不做的声明时找到了这个页面。OP最初建议function(){}作为解决方案并没有帮助。调用(0)() 会导致TypeError: 0 is not a function
    • 我指的是var a = (0);。无操作不应更改内存,而您的分配示例就是这样做的。 (0) 本身可能被认为是无用的空操作(在 JavaScript 中有无数这样的操作,因此没有什么可以使您的示例在这里特别)。
    最近更新 更多