Есть несколько разных способов вызова функции без скобок.
Давайте предположим, что вы определили эту функцию:
function greet() {
console.log('hello');
}
Далее следуйте нескольким способам звонка greetбез скобок:
1. Как конструктор
С помощью newвы можете вызвать функцию без скобок:
new greet; // parentheses are optional in this construct.
Из МДН на newоператора :
Синтаксис
new constructor[([arguments])]
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прототипе :
Function.prototype.valueOf = function() {
this.call(this);
// Optional improvement: avoid `NaN` issues when used in expressions.
return 0;
};
Как только вы это сделали, вы можете написать:
+greet;
И хотя в этой строке присутствуют круглые скобки, фактический вызывающий вызов не имеет круглых скобок. Подробнее об этом читайте в блоге «Вызов методов в JavaScript, без их реального вызова»
3. Как генератор
Вы можете определить функцию генератора (с *), которая возвращает итератор . Вы можете вызвать это, используя синтаксис распространения или с for...ofсинтаксисом.
Сначала нам нужен генераторный вариант исходной greetфункции:
function* greet_gen() {
console.log('hello');
}
И затем мы вызываем его без скобок, определяя метод @@ iterator :
[...{ [Symbol.iterator]: greet_gen }];
Обычно у генераторов где-то есть yieldключевое слово, но оно не требуется для вызова функции.
Последний оператор вызывает функцию, но это также можно сделать с помощью деструктуризации :
[,] = { [Symbol.iterator]: greet_gen };
или for ... ofконструкция, но она имеет свои собственные круглые скобки:
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