¿Cuál es la convención de JavaScript para ninguna operación?

121

¿Cuál es la convención de JavaScript para ninguna operación? Como un passcomando de Python .

  • Una opción es simplemente una función vacía: function() {}
  • jQuery ofrece $.noop(), que simplemente llama a la función vacía de arriba.
  • ¿Es aceptable simplemente ingresar un valor de falseo 0?

En contexto ... todos estos funcionan sin arrojar un error en Chrome:

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

EDITAR: Mucha gente respondió con "¡No hagas esto! ¡Cambia la estructura del código!" Esto me recuerda una publicación donde alguien preguntó cómo olfatear el navegador. Recibió un aluvión de publicaciones que decían: "¡NO HAGAS ESO! ES MALO", pero nadie le dijo cómo olfatear el navegador . Esta no es una revisión de código. Imagine que está tratando con un código heredado que no se puede cambiar, y sin alguna función pasada, arrojará un error. O, simplemente, así lo quiere el cliente y me está pagando . Entonces, respetuosamente, responda la pregunta : ¿Cuál es la mejor manera de especificar una función de "no operación" en JavaScript?

EDIT2: ¿Qué tal uno de estos?

true;
false;
0;
1;
null;
kmiklas
fuente
7
¿Por qué no una simple declaración if?
epascarello
11
Ninguna de esas alternativas 0es efectivamente mejor (o peor). Yo diría que lo correcto es if (a === 1) doSomething();no usarlo ? :cuando no tiene sentido.
Puntiagudo
6
Estás abusando del operador ternario con un efecto secundario. Si debe hacerlo, hágaloif (statement) action; else ;
mplungjan
falseo 0funcionará; nulles una buena forma de expresar no-op.
Matt
3
Espero que el ternario tenga alguna necesidad que no podemos ver por alguna razón ... Parece que estás complicando tu código para sentirte bien (¡todos lo hemos hecho!)
Stachu

Respuestas:

95

Para responder a la pregunta original, la implementación más elegante y ordenada de una función noop en Javascript puro (como también se discute aquí ) es Function.prototype . Esto es porque:

  1. Function.prototype es una función:

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

  1. Se puede invocar como una función y esencialmente no hace nada como se muestra aquí:
setTimeout(function() {
      console.log('Start: ', Date.now());
      Function.prototype();
      console.log('End  : ', Date.now());
    }, 1000);

Aunque este es un "verdadero noop" ya que la mayoría de los navegadores parecen no hacer nada para ejecutar el noop definido de esta manera (y por lo tanto, ahorrar ciclos de CPU), puede haber algunos problemas de rendimiento asociados con esto (como también lo mencionan otros en los comentarios o en Otras respuestas).

Sin embargo, dicho esto, puede definir fácilmente su propia función noop y, de hecho, muchas bibliotecas y marcos también proporcionan funciones noop. A continuación se muestran algunos ejemplos:

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

Aquí hay una lista alfabética de varias implementaciones de funciones noop (o discusiones relacionadas o búsquedas en Google):

AngularJS 1.x , Angular 2+ (No parece tener una implementación nativa; use la suya propia como se muestra arriba), Ember , jQuery , Lodash , NodeJS , Ramda , React (No parece tener una implementación nativa; use la suya propia como se muestra arriba), RxJS , Underscore

LÍNEA INFERIOR : Aunque Function.prototype es una forma elegante de expresar un noop en Javascript, sin embargo, puede haber algunos problemas de rendimiento relacionados con su uso. Por lo tanto, puede definir y usar el suyo (como se muestra arriba) o usar uno definido por la biblioteca / marco que podría estar usando en su código.

Alan CS
fuente
5
Esta debería ser la respuesta. Incluso se puede usar Function.prototype()si no necesita el tiempo de espera y desea que se ejecute de inmediato.
cargado por resorte
1
setTimeout(Function(), 10000);
iegik
@iegik: +1. Sí, estás en lo correcto. Todos los siguientes son equivalentes: setTimeout (function () {}, 10000); O setTimeout (nueva función (), 10000); O setTimeout (función (), 10000); O setTimeout (Función, 10000); ya que la gramática de Javascript y la implementación del constructor de funciones permiten estas construcciones.
Alan CS
5
En ES6 o usando babel u otro transpiler (( )=>{};)técnicamente Pure JS y mucho más corto function () {}pero exactamente equivalente.
Ray Foss
2
Estaba interesado en encontrar una construcción similar a no-op para desactivar eficientemente el código de depuración en mi programa. En C / C ++, habría usado macros. Probé asignando mi fn a Function.prototype y lo cronometré, comparando los resultados con simplemente tener una declaración if dentro de la función para regresar inmediatamente. También los comparé con los resultados de simplemente eliminar la función por completo del bucle. Desafortunadamente, Function.prototype no funcionó bien. llamar a una función vacía fue muy superior en rendimiento, incluso más rápido que llamar a la función con la simple prueba 'if' dentro para regresar.
bearvarine
72

La más concisa y noop performant es una función de flecha vacía : ()=>{}.

Las funciones de flecha funcionan de forma nativa en todos los navegadores excepto IE (hay una transformación de babel si es necesario): MDN


()=>{} vs. Function.Prototype

  • ()=>{}es un 87% más rápido que Function.prototypeen Chrome 67.
  • ()=>{}es un 25% más rápido que Function.prototypeen Firefox 60.
  • ()=>{}es un 85% más rápido que Function.prototypeen Edge (15/6/2018) .
  • ()=>{}es 65% menos código que Function.prototype.

La prueba a continuación se calienta usando la función de flecha para dar sesgo Function.prototype, pero la función de flecha es la clara ganadora:

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!`)

cchamberlain
fuente
1
Creé una prueba jsPerf para comparar algunas opciones: noop-function . Parece que en Firefox 54, todas las alternativas parecen ser equivalentes; en Chromium 58, Function.prototypees drásticamente más lento, pero ninguna de las otras opciones tiene una ventaja definida.
Lucas Werkmeister
_=>{}es un personaje menos y hace lo mismo.
Ross Attrill
@RossAttrill * algo similar: _=>{}tiene un solo parámetro. Si alguien está usando TypeScript con una configuración razonablemente estricta, eso no se compilaría. Si lo llama, su IDE probablemente le dirá que acepta un único parámetro que puede ser de cualquier tipo (tanto JavaScript como especialmente TypeScript en vscode).
cchamberlain
15

todo lo que tiendes a lograr aquí está mal. Las expresiones ternarias no se deben usar como una declaración completa, solo en expresión, por lo que la respuesta a su pregunta es:

ninguna de tus sugerencias, en su lugar haz lo siguiente:

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

entonces el código es fácilmente comprensible, legible y lo más eficiente posible.

¿Por qué hacerlo más difícil cuando puede ser simple?

editar:

Entonces, ¿un comando de "no operación" indica básicamente una estructura de código inferior?

Estás perdiendo mi punto. Todo lo anterior se trata de la expresión ternaria x ? y : z.

Pero, un comando sin operación no tiene sentido en lenguajes de nivel superior como Javascript.

Por lo general, se usa, en lenguajes de nivel inferior, como ensamblador o C, como una forma de hacer que el procesador no haga nada por una instrucción con fines de temporización.

En JS, si lo hace 0;, null;, function () {};o una declaración vacía, hay grandes posibilidades de que será ignorado por el intérprete cuando se está leyendo, pero antes de que se interpretó, así que al final, vas a ser su programa hacen cargado más lentamente por una cantidad de tiempo muy pequeña. Nota Bene : Asumo esto, ya que no estoy involucrado en ningún intérprete JS ampliamente utilizado, y hay posibilidades de que cada intérprete tenga su propia estrategia.

En caso de que use algo un poco más complicado, como $.noop()o var foo = function () {}; foo(), entonces el intérprete puede hacer una llamada de función inútil que terminará estropeando algunos bytes de su pila de funciones y algunos ciclos.

La única razón por la que veo una función como la $.noop()que existiría sería para poder seguir dando una función de devolución de llamada a alguna función de evento que arrojaría una excepción si no puede llamar a esa devolución de llamada. Pero entonces, es necesariamente una función que debe asignar, y darle el noopnombre es una buena idea, por lo que le está diciendo a sus lectores (y ese puede ser usted en 6 meses) que intencionalmente da una función vacía.

Al final, no existe una estructura de código "inferior" o "superior". Tiene razón o no en la forma en que usa sus herramientas. Usar un ternario como ejemplo es como usar un martillo cuando quiere atornillar. Funcionará, pero no estás seguro de poder colgar algo en ese tornillo.

Lo que podría considerarse "inferior" o "superior" es el algoritmo y las ideas que pone en su código. Pero eso es otra cosa.

zmo
fuente
1
Entonces, ¿un comando de "no operación" indica básicamente una estructura de código inferior?
kmiklas
3
@kmiklas: No puedo decir que alguna vez haya encontrado una necesidad legítima de un NOOP, fuera del ensamblador.
Matt Burland
@zmo: Entiendo su punto con respecto a cómo se relacionan sus comentarios con la expresión ternaria; todo el mundo parece haber pasado por alto mi punto de que esto era solo un ejemplo ilustrativo de una llamada a la función "No Operation".
kmiklas
1
Por supuesto, hay razones para querer / necesitar una función sin operación. No hagas generalizaciones amplias basadas en tus 12 segundos de pensamiento profundo en algo. JavaScript es un lenguaje que hace que las funciones sean totalmente controlables como variables. Suponga que necesita deshabilitar una función sin una prueba explícita. Esto sería perfecto para eso. Si tiene una función que aparece con frecuencia, como una declaración de depuración, sería bueno poder desactivarla reasignando la función a no-op en lugar de tener que agregar código adicional para probar alguna variable cada vez que aparece la función. .
bearvarine
2
@bearvarine gracias por la alta opinión que tienes de mí :-D. Tienes toda la razón en tu comentario, pero en realidad no es para el diseño de código, solo el diseño de maquetas usando un truco. Lo que describe también se llama parche de mono, y hay una razón para eso ;-)
zmo
7

Creo que jQuery noop()está destinado principalmente a evitar que el código se bloquee al proporcionar una función predeterminada cuando la solicitada no está disponible. Por ejemplo, considerando el siguiente ejemplo de código, $.noopse elige si fakeFunctionno está definido, evitando que se fnbloquee la siguiente llamada a :

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

Luego, noop()permite ahorrar memoria al evitar escribir la misma función vacía varias veces en cualquier lugar de su código. Por cierto, $.noopes un poco más corto que function(){}(6 bytes guardados por token). Entonces, no hay relación entre su código y el patrón de función vacío. Use null, falseo 0si lo desea, en su caso no habrá efectos secundarios. Además, vale la pena señalar que este código ...

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

... es completamente inútil ya que nunca llamarás a la función, y esta ...

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

... es aún más inútil ya que llamas a una función vacía, que es exactamente lo mismo que ...

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

Reemplacemos la expresión ternaria con una ifdeclaración para ver cuánto es inútil:

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

Simplemente podría escribir:

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

O incluso más corto:

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

Para finalmente responder a su pregunta, supongo que una "no operación convencional" es la que nunca se escribe.

hoja
fuente
1
Sin embargo, a veces tienes que proporcionar una función. Por ejemplo, con promesas, entonces (fn, fn), a veces desea proporcionar la segunda función pero no la primera. por lo tanto, necesita algo para un marcador de posición ...mypromise.then($.noop, myErrorhandler);
John Henckel
3

Yo suelo:

 (0); // nop

Para probar el tiempo de ejecución de esta ejecución como:

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

resultado: marca: 0.000ms

El uso Boolean( 10 > 9)se puede reducir a simplemente lo ( 10 > 9)que devuelve true. Se me ocurrió la idea de usar un solo operando que esperaba (0);que regresara false, pero simplemente devuelve el argumento, ya que se puede revisar realizando esta prueba en la consola.

> var a = (0);
< undefined
> a
< 0
Huevos
fuente
1
muchos que intenten esto obtendránargument 0 is not a function
Code Whisperer
4
Simplemente está asignando 0. IMO noopdebería ser una función que evalúe con undefinedresultado.
cchamberlain
1
¿Cómo es esta declaración (0); asignando un 0 ?, donde esta var y el signo igual? No hay un nombre de función delante del (0), así que no veo cómo esto podría ser un argumento. si prueba typeof ((0)), regresa como un número. Cuando conteste la pregunta, estaba proporcionando una declaración que uso para imitar un NOP, pero estoy dispuesto a cambiar mi respuesta para usar simplemente; para representar una declaración vacía
Huevos
1
Pienso (0); es una solución aceptable a la pregunta de OP. Creo que las personas se están confundiendo porque encontraron esta página mientras buscaban una función que no hace nada, en lugar de una declaración que no hace nada. El OP no ayuda al sugerir inicialmente function(){}como una solución. Llamar (0)()causaráTypeError: 0 is not a function
Benjamín
Me estaba refiriendo var a = (0);. Una operación no operativa no debería cambiar la memoria y su ejemplo de asignación hace precisamente eso. El (0)por sí solo puede considerarse un no-op inútil (hay un número infinito de estos en JavaScript, por lo que no hay nada que haga que su ejemplo aquí sea especial).
cchamberlain
2

No hay absolutamente ningún problema o penalización de rendimiento por usar Function.prototypeover () => {}.

El principal beneficio Function.prototypees tener una función singleton en lugar de redefinir una nueva función anónima cada vez. Es especialmente importante usar un tipo no Function.prototypeoperativo al definir valores predeterminados y memorizar, ya que le brinda un puntero de objeto consistente que nunca cambia.

La razón por la que recomiendo en Function.prototypelugar deFunction es porque no son lo mismo:

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

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

También, puntos de referencia de otras respuestas son engañosos . De hecho, Function.prototypefunciona más rápido que () => {}dependiendo de cómo escriba y ejecute el punto de referencia:

No puede confiar en los puntos de referencia de JS << Específicamente llamando a los puntos de referencia en esta pregunta.

No diseñe su código a partir de puntos de referencia; haga todo lo que se pueda mantener y deje que el intérprete descubra cómo optimizar a largo plazo.

Sawtaytoes
fuente