Сегодня мне сказали, что можно вызывать функцию без скобок. Я мог придумать только один способ - использовать такие функции, как apply или call.

.
f.apply(this);
f.call(this);

Но для этого требуются круглые скобки для apply и call, оставляя нас на первом месте. Я также рассматривал идею передачи функции в какой-то обработчик событий, например setTimeout:

setTimeout(f, 500);

Но тогда возникает вопрос: «Как вы вызываете setTimeout без скобок?»

Так как же разгадать эту загадку? Как можно вызвать функцию в Javascript без скобок?

Mike Cluck

Ответов: 6

Ответы (6)

Есть несколько разных способов вызвать функцию без скобок.

Предположим, у вас определена эта функция:

function greet() {
    console.log('hello');
}

Тогда вот несколько способов вызвать greet без скобок:

1. Как конструктор

С new вы можете вызывать функцию без скобок:

new greet; // parentheses are optional in this construct.

From MDN на new oprator:

Синтаксис

новый конструктор [([аргументы])]

2. Как toString или valueOf Реализация

toString и valueOf - специальные методы: они вызываются неявно, когда требуется преобразование:

var obj = {
    toString: function() {
         return 'hello';
    }
}

'' + obj; // concatenation forces cast to string and call to toString.

Вы можете (ab) использовать этот шаблон для вызова greet без скобок:

'' + { toString: greet };

Или с valueOf:

+{ valueOf: greet };

valueOf и toString на самом деле вызываются из метода @@ toPrimitive (начиная с ES6), поэтому вы также можете реализовать , что метод:

+{ [Symbol.toPrimitive]: greet }
"" + { [Symbol.toPrimitive]: greet }

2.b Переопределение valueOf в прототипе функции

Вы можете воспользоваться предыдущей идеей, чтобы переопределить метод valueOf в Function prototype:

Function.prototype.valueOf = function() {
    this.call(this);
    // Optional improvement: avoid `NaN` issues when used in expressions.
    return 0; 
};

Как только вы это сделаете, вы можете написать:

+greet;

И хотя в строке присутствуют круглые скобки, фактический запускающий вызов не имеет скобок. Подробнее об этом читайте в блоге «Вызов методов в JavaScript, без их реального вызова»

3. Как генератор

Вы можете определить функцию генератора *), которая возвращает итератор. Вы можете вызвать его, используя синтаксис spread или синтаксис for ... of.

Для начала нам понадобится генератор вариант оригинала greet function:

function* greet_gen() {
    console.log('hello');
}

И затем мы вызываем его без скобок, определяя метод @@ iterator:

[...{ [Symbol.iterator]: greet_gen }];

Обычно генераторы содержат где-нибудь ключевое слово yield, но это не требуется для вызова функции.

The last statement invokes the function, but that could also be done with destructuring:

[,] = { [Symbol.iterator]: greet_gen };

или для ... конструкции, но у нее есть собственные скобки:

for ({} of { [Symbol.iterator]: greet_gen });

Обратите внимание, что вы можете сделать то же самое с исходной функцией greet, но это вызовет исключение в процессе, после greet был выполнен (проверено на FF и Chrome). Вы можете управлять исключением с помощью блока try ... catch.

4. Как получатель

@ jehna1 имеет полный ответ на этот вопрос, так что отдайте ему должное. Вот способ вызвать функцию без скобок в глобальной области, избегая устаревшего __ defineGetter __ метода. Вместо этого он использует Object.defineProperty.

Для этого нам нужно создать вариант исходной функции greet:

Object.defineProperty(window, 'greet_get', { get: greet });

А потом:

greet_get;

Замените window любым вашим глобальным объектом.

Вы можете вызвать исходную функцию greet, не оставляя следов на глобальном объекте, например:

Object.defineProperty({}, 'greet', { get: greet }).greet;

Но можно утверждать, что здесь есть круглые скобки (хотя они не участвуют в самом вызове).

5. Как функция тега

Начиная с ES6, вы можете вызывать функцию, передавая ей литерал шаблона с таким синтаксисом:

greet``;

См. «Тегированные шаблонные литералы».

6. Как обработчик прокси

Начиная с ES6, вы можете определить прокси:

var proxy = new Proxy({}, { get: greet } );

И затем чтение любого значения свойства вызовет greet:

proxy._; // even if property not defined, it still triggers greet

Есть много вариантов этого. Еще один пример:

var proxy = new Proxy({}, { has: greet } );

1 in proxy; // triggers greet

7. Как средство проверки экземпляра

Оператор instanceof выполняет метод @@ hasInstance для второго операнда, если он определен:

1 instanceof { [Symbol.hasInstance]: greet } // triggers greet

Если мы принимаем подход нестандартного мышления, в браузере есть несколько API, которыми мы можем злоупотреблять для выполнения произвольного JavaScript, включая вызов функции, без каких-либо фактических скобок.

1.расположение и javascript: протокол:

Одним из таких приемов является злоупотребление протоколом javascript: при назначении location.

Рабочий пример:

location = 'javascript: alert \ x281 \ x29'

Хотя технически \ x28 и \ x29 все еще остаются скобками после оценки кода, фактические ( и ) символ не появляется. Скобки экранируются в строке JavaScript, которая оценивается при присвоении.


2.ошибка и eval:

Точно так же, в зависимости от браузера, мы можем злоупотребить глобальной ошибкой onerror, установив для него значение evalи выбрасывая что-то, что будет преобразовано в допустимый JavaScript. Это сложнее, потому что браузеры несовместимы в этом поведении, но вот пример для Chrome.

Рабочий пример для Chrome (не Firefox, другие непроверенные):

window.onerror = eval; Uncaught = 0; throw '; alert \ x281 \ x29';

Это работает в Chrome, потому что throw'test ' передаст ' Uncaught test ' в качестве первого аргумента для onerror, что является почти правильным JavaScript. Если мы вместо этого выполним throw '; test', будет пройдено 'Uncaught; test'. Теперь у нас есть действующий JavaScript! Просто определите Uncaughtи замените test полезной нагрузкой.


В заключение:

Такой код действительно ужасен, а никогда не следует использовать, но иногда используется в XSS-атаках, поэтому мораль этой истории заключается в том, что не полагайтесь на фильтрацию скобок для предотвратить XSS. Также было бы неплохо использовать CSP для предотвращения такого кода.

Проще всего это сделать с помощью оператора new:

функция f () {
  предупреждение ('привет');
}

новый f;

Хотя это неортодоксально и неестественно, но работает и совершенно законно.

Оператор new не требует скобок, если не используются параметры.

Вы можете использовать геттеры и сеттеры.

var h = {
  get ello () {
    alert("World");
  }
}

Запустите этот сценарий только с:

h.ello  // Fires up alert "world"

Изменить:

Мы даже можем аргументировать!

var h = {
  set ello (what) {
    alert("Hello " + what);
  }
}

h.ello = "world" // Fires up alert "Hello world"

Редактировать 2:

Вы также можете определить глобальные функции, которые можно запускать без скобок:

window.__defineGetter__("hello", function() { alert("world"); });
hello;  // Fires up alert "world"

И с аргументами:

window.__defineSetter__("hello", function(what) { alert("Hello " + what); });
hello = "world";  // Fires up alert "Hello world"

Заявление об ограничении ответственности:

Как заявил @MonkeyZeus: Никогда вы никогда не будете использовать этот фрагмент кода в производстве, независимо от того, насколько хороши ваши намерения.

Вот пример для конкретной ситуации:

window.onload = funcRef;

Хотя этот оператор на самом деле не вызывает , но приведет к будущему вызову.

Но, я полагаю, серые области могут быть подходящими для таких загадок :)

В ES6 у вас есть так называемые Тегированные шаблонные литералы.

Например:

function foo (val) {
    console.log (значение);
}

foo`Tagged Template Literals`;

2022 WebDevInsider