Disseram-me hoje que é possível invocar uma função sem parênteses. As únicas maneiras que eu conseguia pensar era usando funções como apply
ou call
.
f.apply(this);
f.call(this);
Mas isso exige parênteses apply
e call
nos deixa na estaca zero. Também considerei a ideia de passar a função para algum tipo de manipulador de eventos, como setTimeout
:
setTimeout(f, 500);
Mas então a pergunta se torna "como você invoca setTimeout
sem parênteses?"
Então, qual é a solução para esse enigma? Como você pode chamar uma função em Javascript sem usar parênteses?
javascript
Mike Cluck
fonte
fonte
<insert any solution>
seja objetivamente melhor ou mais útil quef()
.Respostas:
Existem várias maneiras diferentes de chamar uma função sem parênteses.
Vamos supor que você tenha essa função definida:
A seguir, siga algumas maneiras de ligar
greet
sem parênteses:1. Como Construtor
Com
new
você pode invocar uma função sem parênteses:Do MDN no
new
oprator :2. Como
toString
ouvalueOf
ImplementaçãotoString
evalueOf
são métodos especiais: eles são chamados implicitamente quando uma conversão é necessária:Você poderia (ab) usar esse padrão para chamar
greet
sem parênteses:Ou com
valueOf
:valueOf
etoString
são chamados de fato do método @@ toPrimitive (desde ES6) e, portanto, você também pode implementar esse método:2.b Substituição
valueOf
no protótipo de funçãoVocê pode seguir a ideia anterior para substituir o
valueOf
método noFunction
protótipo :Depois de fazer isso, você pode escrever:
E embora haja parênteses envolvidos na linha, a chamada de acionamento real não tem parênteses. Veja mais sobre isso no blog "Chamando métodos em JavaScript, sem realmente chamá-los"
3. Como gerador
Você pode definir uma função de gerador (com
*
), que retorna um iterador . Você pode chamá-lo usando a sintaxe de propagação ou com afor...of
sintaxe.Primeiro, precisamos de uma variante geradora da
greet
função original :E então a chamamos sem parênteses, definindo o método @@ iterator :
Normalmente, os geradores teriam uma
yield
palavra - chave em algum lugar, mas não é necessário para a função ser chamada.A última instrução chama a função, mas isso também pode ser feito com a desestruturação :
ou uma
for ... of
construção, mas possui parênteses próprios:Observe que você também pode fazer o acima com a
greet
função original , mas ela acionará uma exceção no processo, após agreet
execução (testada no FF e no Chrome). Você pode gerenciar a exceção com umtry...catch
bloco.4. Como Getter
@ jehna1 tem uma resposta completa sobre isso, então dê-lhe crédito. Aqui está uma maneira de chamar uma função sem parênteses no escopo global, evitando o método descontinuado
__defineGetter__
. Ele usa em seuObject.defineProperty
lugar.Precisamos criar uma variante da
greet
função original para isso:E depois:
Substitua
window
por qualquer que seja seu objeto global.Você poderia chamar a
greet
função original sem deixar rastros no objeto global como este:Mas alguém poderia argumentar que temos parênteses aqui (embora eles não estejam envolvidos na invocação real).
5. Como função de tag
Desde o ES6, você pode chamar uma função passando um literal de modelo com esta sintaxe:
Consulte "Literais de modelo marcados" .
6. Como manipulador de proxy
Desde o ES6, você pode definir um proxy :
E a leitura de qualquer valor da propriedade chamará
greet
:Existem muitas variações disso. Mais um exemplo:
7. Como verificador de instância
O
instanceof
operador executa o@@hasInstance
método no segundo operando, quando definido:fonte
valueOf
.func``;
'1' |> alert
A maneira mais fácil de fazer isso é com o
new
operador:Embora isso não seja ortodoxo nem natural, ele funciona e é perfeitamente legal.
O
new
operador não exige parênteses se nenhum parâmetro for usado.fonte
!f
resulta na mesma coisa, sem instanciar uma instância da função construtora (que requer a criação de um novo contexto). É muito mais eficiente e é usado em muitas bibliotecas populares (como o Bootstrap).+
,-
,~
) em tais bibliotecas, é para salvar um byte na versão minimizada da biblioteca onde o valor de retorno não é necessário. (ou seja!function(){}()
) A sintaxe usual de auto-chamada é(function(){})()
(infelizmente a sintaxefunction(){}()
não é entre navegadores) Como a função de auto-chamada mais usada geralmente não tem nada a retornar, geralmente é o objetivo dessa técnica de economia de bytesfunction foo() { alert("Foo!") };
Por exemplo:!foo
retornafalse
Você pode usar getters e setters.
Execute este script apenas com:
Editar:
Podemos até argumentar!
Edição 2:
Você também pode definir funções globais que podem ser executadas sem parênteses:
E com argumentos:
Aviso Legal:
Como o @MonkeyZeus declarou: Nunca, você nunca deve usar esse código na produção, por melhores que sejam suas intenções.
fonte
__defineGetter__
está obsoleto. Use emObject.defineProperty
vez disso.Aqui está um exemplo para uma situação específica:
Embora essa declaração não seja realmente invocadora, ela levará a uma invocação futura .
Mas acho que as áreas cinzentas podem ser boas para enigmas como este :)
fonte
Se aceitarmos uma abordagem de pensamento lateral, em um navegador, existem várias APIs que podemos abusar para executar JavaScript arbitrário, incluindo a chamada de uma função, sem caracteres reais entre parênteses.
1.
location
ejavascript:
protocolo:Uma dessas técnicas é abusar do
javascript:
protocolo nalocation
atribuição.Exemplo de trabalho:
Embora tecnicamente
\x28
e\x29
ainda estejam entre parênteses após a avaliação do código, o caractere(
e o real)
não aparecem. Os parênteses são escapados em uma sequência de JavaScript que é avaliada na atribuição.2.
onerror
eeval
:Da mesma forma, dependendo do navegador, podemos abusar do global
onerror
, definindo-o comoeval
e lançando algo que será restringido ao JavaScript válido. Este é mais complicado, porque os navegadores são inconsistentes nesse comportamento, mas aqui está um exemplo para o Chrome.Exemplo de trabalho para o Chrome (não Firefox, outros não testados):
Isso funciona no Chrome porque
throw'test'
passará'Uncaught test'
como o primeiro argumento paraonerror
, que é o JavaScript quase válido. Se fizermos isso,throw';test'
isso passará'Uncaught ;test'
. Agora temos JavaScript válido! Basta definirUncaught
e substituir o teste pela carga útil.Em conclusão:
Esse código é realmente horrível e nunca deve ser usado , mas às vezes é usado em ataques de XSS; portanto, a moral da história é não confiar na filtragem de parênteses para impedir o XSS. Usar um CSP para impedir esse código também seria uma boa ideia.
fonte
eval
e escrever a sequência sem realmente usar os caracteres entre parênteses.eval
normalmente requer parênteses, portanto, este truque de evasão de filtro.\x28
e\x29
ainda são parênteses.'\x28' === '('
.No ES6, você tem o que chamamos literais de modelo marcado .
Por exemplo:
fonte