usar funciones flecha example ecmascript cuando javascript function ecmascript-6 ecmascript-harmony arrow-functions

javascript - funciones - ecmascript 6



JavaScript ES6: prueba de función de flecha, función incorporada, función regular? (7)

¿Hay una manera elegante de distinguir las funciones de flecha delgada de Harmony aparte de las funciones regulares y las funciones integradas?

La wiki de Harmony dice que:

Las funciones de flecha son como funciones integradas en que ambas carecen de .prototype y de cualquier método interno [[Construct]]. Entonces new (() => {}) arroja un TypeError pero de lo contrario las flechas son como funciones

Lo que significa que puede probar funciones de flecha como:

!(()=>{}).hasOwnProperty("prototype") // true !(function(){}).hasOwnProperty("prototype") // false

Pero la prueba también devolverá true para cualquier función incorporada, por ejemplo, setTimeout o Math.min .

De alguna manera funciona en Firefox si obtienes el código fuente y compruebas si es "native code" , pero no parece muy confiable ni portátil (otras implementaciones de navegador, NodeJS / iojs):

setTimeout.toSource().indexOf("[native code]") > -1

El pequeño proyecto node-is-arrow-function GitHub node-is-arrow-function basa en las comprobaciones RegExp contra el código fuente de la función, que no es tan bueno.

editar: probé la acorn analizador de JavaScript y parece que funciona bastante bien, aunque es demasiado exagerado.

acorn = require("./acorn"); function fn_sample(a,b){ c = (d,e) => d-e; f = c(--a, b) * (b, a); return f; } function test(fn){ fn = fn || fn_sample; try { acorn.parse("(" + fn.toString() + ")", { ecmaVersion: 6, onToken: function(token){ if(typeof token.type == "object" && token.type.type == "=>"){ console.log("ArrowFunction found", token); } } }); } catch(e) { console.log("Error, possibly caused by [native code]"); console.log(e.message); } } exports.test = test;


ECMAScript renuncia a muchas de sus garantías para objetos host y, por lo tanto, por extensión, funciones host. Eso hace que las propiedades sean accesibles a través de la reflexión en su mayoría dependientes de la implementación con pocas garantías de consistencia, al menos en lo que respecta a la especificación ecmascript, las especificaciones W3C pueden ser más específicas para los objetos host del navegador.

Por ejemplo, ver

8.6.2 Propiedades y métodos internos del objeto

La Tabla 9 resume las propiedades internas utilizadas por esta especificación que solo son aplicables a algunos objetos ECMAScript. [...] Los objetos host pueden admitir estas propiedades internas con cualquier comportamiento dependiente de la implementación, siempre que sea coherente con las restricciones específicas de objetos host establecidas en este documento.

Por lo tanto, las funciones integradas pueden ser invocables pero no tienen prototipo (es decir, no se heredan de la función). O podrían tener uno.

La especificación dice que pueden comportarse de manera diferente. Pero también pueden implementar todo el comportamiento estándar, haciéndolos indistinguibles de las funciones normales.

Tenga en cuenta que estoy citando la especificación ES5. ES6 aún está en proceso de revisión, los objetos nativos y de host ahora se denominan objetos exóticos. Pero la especificación dice más o menos lo mismo. Proporciona algunos invariantes que incluso deben cumplir, pero de lo contrario solo dice que pueden cumplir o no todos los comportamientos opcionales.


Escribí esto para Node, también debería funcionar en Chrome.

El "límite" se detecta ( aparentemente, solo en ES6 ) y se informa como native && bound . Esto puede o no ser un problema, dependiendo de para qué esté usando esa información.

const flags = { function: f instanceof Function, name: undefined, native: false, bound: false, plain: false, arrow: false }; if (flags.function) { flags.name = f.name || ''(anonymous)''; flags.native = f.toString().trim().endsWith(''() { [native code] }''); flags.bound = flags.native && flags.name.startsWith(''bound ''); flags.plain = !flags.native && f.hasOwnProperty(''prototype''); flags.arrow = !(flags.native || flags.plain); } return flags;


Kangax ha hecho un gran punto. Si quisieras la perfección, usarías una biblioteca de analizador AST para determinar el tipo de función, pero es probable que eso sea excesivo para muchos de ustedes.

Para ese fin, aquí hay un método para implementar usando Expresiones regulares:

/** Check if function is Arrow Function */ const isArrowFn = (fn) => (typeof fn === ''function'') && /^[^{]+?=>/.test(fn.toString()); /* Demo */ const fn = () => {}; const fn2 = function () { return () => 4 } isArrowFn(fn) // True isArrowFn(fn2) // False

Lógica

Esto supone que todos los bloques de funciones que no sean flechas deben estar rodeados por {}. No creo que haya un entorno que de otra manera representaría, pero avíseme si me equivoco.

Nota: La expresión regular también se escribe asumiendo que la función => siempre estará en la primera línea. Esto se puede cambiar fácilmente, pero una vez más, no puedo imaginar nada que represente una nueva línea antes.

¿Como funciona?

  • ^ - comienza al principio de la línea
  • [ ^ { ] +? - Revisa todo lo que sigue hasta el siguiente patrón (=>), pero si encuentras un { primero, no es una coincidencia
  • => - Encontrado => antes de { , por lo que es una coincidencia

¿Problema?

Deja un comentario si encuentras un caso en el que no funciona, y veré si podemos acomodarlo.


La solución de Ron S funciona muy bien pero puede detectar falsos positivos:

F.toString().replace(//s+/g, '''').indexOf('')=>'')>=1


Por extraño que parezca...

Probablemente la forma más confiable (pero no 100%) es probar la presencia de "=>" en la representación de cadena de una función.

Obviamente, no podemos probar ninguna de las dos condiciones que mencionó: la falta de propiedad del prototipo y la falta de [[Construct]] ya que eso podría dar falsos positivos con objetos host o incorporados que carecen de [[Construct]] ( Math.floor , JSON.parse , etc.)

Sin embargo, podríamos usar el viejo Function.prototype.toString para verificar si la representación de la función contiene "=>".

Ahora, siempre he recomendado no usar Function.prototype.toString (la llamada descompilación de funciones ) debido a su naturaleza dependiente de la implementación e históricamente poco confiable (más detalles en el estado de descompilación de funciones en Javascript ).

Pero ES6 en realidad trata de imponer reglas sobre la forma en que (al menos) las funciones integradas y "creadas por el usuario" (por falta de un mejor término) están representadas.

  1. Si Type (func) es Object y es un objeto de función incorporado o tiene una ranura interna [[ECMAScriptCode]] , entonces

    a. Devuelve una representación de código fuente String dependiente de la implementación de func. La representación debe cumplir con las siguientes reglas .

...

Requisitos de representación de toString:

  • La representación de cadena debe tener la sintaxis de una FunctionDeclaration FunctionExpression, GeneratorDeclaration, GeneratorExpession, ClassDeclaration, ClassExpression, ArrowFunction , MethodDefinition o GeneratorMethod, según las características reales del objeto.

  • El uso y la colocación de espacios en blanco, terminadores de línea y punto y coma dentro de la cadena de representación dependen de la implementación.

  • Si el objeto se definió utilizando el código ECMAScript y la representación de cadena devuelta no tiene la forma de MethodDefinition o GeneratorMethod, entonces la representación debe ser tal que si se evalúa la cadena, utilizando eval en un contexto léxico que sea equivalente al contexto léxico utilizado para crear el objeto original, dará como resultado un nuevo objeto funcionalmente equivalente. En ese caso, el código fuente devuelto no debe mencionar libremente ninguna variable que no haya sido mencionada libremente por el código fuente de la función original, incluso si estos nombres "adicionales" estaban originalmente en el alcance.

  • Si la implementación no puede producir una cadena de código fuente que cumpla con estos criterios, debe devolver una cadena para la cual eval generará una excepción SyntaxError.

Destaqué los fragmentos relevantes.

Las funciones de flecha tienen [[ECMAScriptCode]] interno (que puede rastrear desde 14.2.17 - evaluación de la función de flecha - a FunctionCreate a FunctionInitialize ).

Esto significa que deben cumplir con la sintaxis de ArrowFunction :

ArrowFunction[In, Yield] : ArrowParameters[?Yield] [no LineTerminator here] => ConciseBody[?In]

..lo que significa que deben tener => en la salida de Function.prototype.toString .

Obviamente, deberá asegurarse de que "=>" siga a ArrowParameters y no sea solo algo presente en FunctionBody:

function f() { return "=>" }

En cuanto a la fiabilidad, recuerde que este comportamiento no es compatible con ninguno / todos los motores en este momento y que la representación de los objetos del host puede estar (a pesar de los esfuerzos de las especificaciones) por cualquier motivo.


Por lo que puedo decir, esto debería funcionar:

Todas las funciones que no son flechas cuando se convierten en String START con ''function''. Las funciones de flecha no.

Intentar probar la presencia de ''=>'' no es una forma confiable de probar si la función es una flecha o no porque cualquier función que no sea de flecha puede contener funciones de flecha dentro de ellas y, por lo tanto, ''=>'' puede estar presente en su código fuente.


  • Convertir función a cadena a cadena
  • eliminando todos los espacios de esta cadena.
  • si ")=>" existe con un índice mayor o igual a 1 => 99.99% es una función de flecha.

    var fn1=function(e){ e.target.value=new Date(); }; var fn2=(a,b)=> a+b; function isArrow(name,F){ if(F.toString().replace(//s+/g, '''').indexOf('')=>'')>=1){ console.log(`${name} is arrow-function`); }else{ console.log(`${name} is classic-function`); } } isArrow(''fn1'',fn1); isArrow(''fn2'',fn2);

DEMO:

var fn1=function(e){ e.target.value=new Date(); }; var fn2=(a,b)=> a+b; function isArrow(name,F){ if(F.toString().replace(//s+/g, '''').indexOf('')=>'')>=1){ console.log(`${name} is arrow-function`); }else{ console.log(`${name} is classic-function`); } } isArrow(''fn1'',fn1); isArrow(''fn2'',fn2);