Пять способов вызвать функцию

http://devlicio.us/blogs/sergio_pereira/archive/2009/02/09/javascript-5-ways-to-call-a-function.aspx
  • Перевод
Мне часто приходится сталкиваться с JavaScript-кодом, ошибки в котором вызваны неправильным понимаем того, как работают функции в JavaScript (кстати, значительная часть такого кода была написана мной самим). JavaScript — язык мультипарадигменный, и в нем имеются механизмы функционального программирования. Пора изучить эти возможности. В этой статье я расскажу вам о пяти способах вызова функций в JavaScript.

На первых этапах изучения JavaScript новички обычно думают, что функции в нем работают примерно так же, как, скажем, в C#. Но механизмы вызова функций в JavaScript имеют ряд важных отличий, и незнание их может вылиться в ошибки, которые будет непросто найти.

Давайте напишем простую функцию, которая возвращает массив из трех элементов — текущего значения this и двух аргументов, переданных в функцию.
function makeArray(arg1, arg2){
  return [ this, arg1, arg2 ];
}

Самый распространенный способ: глобальный вызов


Новички часто объявляют функции так, как показано в примере выше. Вызвать эту функцию не составляет труда:
makeArray('one', 'two'); // => [ window, 'one', 'two' ]

Погодите. Откуда взялся объект window? Почему это у нас this равен window?

В JavaScript, неважно, выполняется ли скрипт в браузере или в ином окружении, всегда определен глобальный объект. Любой код в нашем скрипте, не «привязанный» к чему-либо (т.е. находящийся вне объявления объекта) на самом деле находится в контексте глобального объекта. В нашем случае, makeArray — не просто функция, «гуляющая» сама по себе. На самом деле, makeArray — метод глобального объекта (в случае исполнения кода в браузере) window. Доказать это легко:
alert( typeof window.methodThatDoesntExist ); // => undefined
alert( typeof window.makeArray ); // => function

То есть вызов makeArray('one', 'two'); равносилен вызову window.makeArray('one', 'two');.

Меня печалит тот факт, что этот способ вызова функций наиболее распространен, ведь он подразумевает наличие глобальной функции. А мы все знаем, что глобальные функции и переменные — не самый хороший тон в программировании. Особенно это справедливо для JavaScript. Избегайте глобальных определений, и не пожалеете.

Правило вызова функций №1: Если функция вызывается напрямую, без указания объекта (например, myFunction()), значением this будет глобальный объект (window в случае исполнения кода в браузере).

Вызов метода


Давайте создадим простой объект и сделаем makeArray его методом. Объект объявим с помощью литеральной нотации, а после вызовем наш метод:
// создаем объект
var arrayMaker = {
  someProperty: 'какое-то значение',
  make: makeArray
};

// вызываем метод make()
arrayMaker.make('one', 'two'); // => [ arrayMaker, 'one', 'two' ]
// альтернативный синтаксис, используем квадратные скобки
arrayMaker['make']('one', 'two'); // => [ arrayMaker, 'one', 'two' ]

Видите разницу? Значение this в этом случае — сам объект. Почему не window, как в предыдущем случае, ведь объявление функции не изменилось? Весь секрет в том, как передаются функции в JavaScript. Function — это стандартный тип JavaScript, являющийся на самом деле объектом, и как и любой другой объект, функции можно передавать и копировать. В данном случае, мы как бы скопировали всю функцию, включая список аргументов и тело, и присвоили получившийся объект свойству make объекта arrayMaker. Это равносильно такому объявлению:
var arrayMaker = {
  someProperty: 'Какое-то значение';
  make: function (arg1, arg2) {
    return [ this, arg1, arg2];
  }
};

Правило вызова функций №2: В функции, вызванной с использованием синтаксиса вызова метода, например, obj.myFunction() или obj['myFunction'](), this будет иметь значение obj.

Непонимание этого простого, в общем-то, принципа часто приводит к ошибкам при обработке событий:
<input type="button" value="Button 1" id="btn1" />
<input type="button" value="Button 2" id="btn2" />
<input type="button" value="Button 3" id="btn3" onclick="buttonClicked();" />

<script type="text/javascript">
function buttonClicked(){
  var text = (this === window) ? 'window' : this.id;
  alert( text );
}
var button1 = document.getElementById('btn1');
var button2 = document.getElementById('btn2');

button1.onclick = buttonClicked;
button2.onclick = function(){ buttonClicked(); };
</script>

Щелчок по первой кнопке покажет сообщение «btn1», потому что в данном случае мы вызываем функцию как метод, и this внутри функции получит значение объекта, которому этот метод принадлежит. Щелчок по второй кнопке выдаст «window», потому что в этом случае мы вызываем buttonClicked напрямую (т.е. не как obj.buttonClicked()). То же самое происходит, когда мы назначаем обработчик события в тэге элемента, как в случае третьей кнопки. Щелчок по третьей кнопке покажет то же самое сообщение, что и для второй.

При использовании библиотек вроде jQuery думать об этом не надо. jQuery позаботится о том, чтобы переписать значение this в обработчике события так, чтобы значением this был элемент, вызвавший событие:
// используем jQuery
$('#btn1').click( function() {
  alert( this.id ); // jQuery позаботится о том, чтобы 'this' являлась кнопкой
});

Каким образом jQuery удается изменить значение this? Читайте ниже.

Еще два способа: apply() и call()


Логично, что чем чаще вы используете функции, тем чаще вам приходится передавать их и вызывать в разных контекстах. Зачастую возникает необходимость переопределить значение this. Если вы помните, функции в JavaScript являются объектами. На практике это означает, что у функций есть предопределенные методы. apply() и call() — два из них. Они позволяют переопределять значение this:
var car = { year: 2008, model: 'Dodge Bailout' };
makeArray.apply( car, [ 'one', 'two' ] ); // => [ car, 'one', 'two' ]
makeArray.call( car, 'one', 'two' ); // => [ car, 'one', 'two' ]

Эти два метода очень похожи. Первый параметр переопределяет this. Различия между ними заключаются в последющих аргументах: Function.apply() принимает массив значений, которые будут переданы функции, а Function.call() принимает аргументы раздельно. На практике, по моему мнению, удобнее применять apply().

Правило вызова функций №3: Если требуется переопределить значение this, не копируя функцию в другой объект, можно использовать myFunction.apply( obj ) или myFunction.call( obj ).

Конструкторы


Я не буду подробно останавливаться на объявлении собственных типов в JavaScript, но считаю необходимым напомнить, что в JavaScript нет классов, а любой пользовательский тип нуждается в конструкторе. Кроме того, методы пользовательского типа лучше объявлять через prototype, который является свойством фукции-конструктора. Давайте создадим свой тип:
// объявляем конструктор
function ArrayMaker(arg1, arg2) {
  this.someProperty = 'неважно';
  this.theArray = [ this, arg1, arg2 ];
}
// объявляем методы
ArrayMaker.prototype = {
  someMethod: function () {
    alert('Вызван someMethod');
  },
  getArray: function () {
    return this.theArray;
  }
};

var am = new ArrayMaker( 'one', 'two' );
var other = new ArrayMaker( 'first', 'second' );

am.getArray(); // => [ am, 'one', 'two' ]

Важным в этом примере является наличие оператора new перед вызовом функции. Если бы не он, это был бы глобальный вызов, и создаваемые в конструкторе свойства относились бы к глобальному объекту. Нам такого не надо. Кроме того, в конструкторах обычно не возвращают значения явно. Без оператора new конструктор вернул бы undefined, с ним он возвращает this. Хорошим стилем считается наименование конструкторов с заглавной буквы; это позволит вспомнить о необходимости оператора new.

В остальном, код внутри конструктора, скорее всего, будет похож на код, который вы написали бы на другом языке. Значение this в данном случае — это новый объект, который вы создаете.

Правило вызова функций №4: При вызове функции с оператором new, значением this будет новый объект, созданный средой исполнения JavaScript. Если эта функция не возвращает какой-либо объект явно, будет неявно возвращен this.

Заключение


Надеюсь, понимание разницы между разными способами вызова функций возволит вам улучшить ваш JavaScript-код. Иногда непросто отловить ошибки, связанные со значением this, поэтому имеет смысл предупреждать их возникновение заранее.
Поделиться публикацией
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама
Комментарии 84
  • +5
    Следующим шагом к пониманию функциональной стороны JavaScript является изучение замыканий, о чем уже была подробная статья.
    • +4
      Я бы все таки посоветовал книгу, одна хорошая книга будет полезнее пачки статей/переводов. А в статьях и блогах было бы интересно видеть что-то не стандартное, не из разряда справочной информации.
      • 0
        В книгах информация бывает сгруппирована в другом порядке, по другой логике.
        Или Вы имеете ввиду какую-то конкретную книгу, где информация по this в функциях сгруппирована именно так?
        • 0
          А группировка «именно так» претендует на единственную истину? Бывают книги, именно с подборками приемов и конструкций, для гиков, и от гиков ) книги Stefanov, Zakas, Resig и тп. Где не рассматриваются азы JS, а именно приемы, паттерны и тд. Прочитайте парочку, и на многие статьи не придется тратить время.
          • 0
            Группировка «именно так» претендует на понятность именно этой взаимосвязи группируемого. Вовсе не обязательно ей претендовать на «единственность». Народ плюсует, соответственно части народа подобная группировка пришлась по душе. Мне тоже.
    • +1
      У меня в аське сегодня как раз был вопрос на эту тему. Удивительно, что многим людям таак тяжело даётся этот участок.

      Ещё топику не хватает описания bind — возможности привязать функцию к определённому контексту.

      Самое главное — помнить, что у функции контекст (this) — это то, от какого объекта она вызвана, а не то, возле какого она создана.

      Ну и не забываем о 'use strict'
      • +1
        И ещё хотел бы добавить. Прочитал в твиттере вашего товарища, что он не понимает, для чего нужны call/apply. На самом деле они очень часто используются для создания изящного интерфейса, многие трюки базируются именно на этих методах. К примеру, можно очень легко сделать из объекта arguments — массив:
        var array = [].slice.call(arguments);
        


        Магия jQuery-each применяется именно при помощи apply:
        $('div').each(function () {
            $(this); // <== here
        });
        


        Конечно, в простых приложениях такая магия нужна редко. Но в фреймворках она используется очень часто и без неё было бы очень тяжело построить изящный интерфейс.
        • НЛО прилетело и опубликовало эту надпись здесь
          • 0
            В древнем приложении, где все было построено на фреймах, приходилось использовать apply. А вообще — да, редко используется. Только на собеседованиях :)
            • 0
              а почему по стрелочке <== here я не вижу слова apply?
          • 0
            Пункт 2 сформулирован неверно. Ничто не мешает вызвать функцию так: { func: function(){} }.func()

            Ну сколько уже можно переводить статьи из серии «JS/PHP/Java/<любая другая технология> для чайников»?
            • 0
              И что? Да, переменной нету, но ссылка будет на объект:
              </source.
              • 0
                !{ x: 42,
                   func: function(){
                     console.log(this.x);
                   }
                }.func() // 42
                
                • 0
                  Ну такой способ порой бывает очень даже наруку.
                  var foo = {
                      show: function() {
                          $('body').css('color', '#F');
                          return this;
                      }
                      /**  ...  */
                  }.show();

                  — и объект создали, и он успел уже что-то сделать;), и код чище. Почему бы не использовать?

              • +5
                пусть народ читает, а то развелось кодеров на «jquery» глаза б мои их кода не видели!!!

                Вообще хорошо бы юзерам понимать что в js все есть объекты, каждая мелочь даже
                function(){
                var t,y,u;

                }
                t,y,u — это свойства объекта, а в качестве объекта, объект перменных данной функции.

                Может написать статью по объектам в js? надо погуглить хабр есть ли тут такое
                • +1
                  Ммм? Но есть нюансы.
                  var num = 123;
                  num.test = 42;
                  console.log( num.test ); // undefined
                  
                  • +1
                    Единственное применение конструктора Number:)
                    • +1
                      Но не совсем rulez =(
                      var foo = 0, bar = new Number(0);
                      
                      console.log( !foo, !bar ); // true, false
                      
                      • 0
                        :(
                        Только так:
                        var bar = new Number(0);
                        console.log(!(bar + 0))
                        • 0
                          Вообще можно так:
                          var bar = new Number(0);
                          console.log(!+bar);
                          

                          Но всё-равно не айс. По этой причине в Modernizr костыльно делается проверка на video и audio. Если они присутствуют, то хранится new Object(true), иначе — false. Плохо, если в таком случае необходимо и в отрицательном варианте хранить дополнительные свойства.
                          • 0
                            Я иногда жалею, что нет методов типа toBoolean
                            • 0
                              Категорически поддерживаю. Этим страдает что php, что js. Реализовано только String =(

                              ps. php __cast
                              • +1
                                Преобразование типа можно явным образом сделать через его название:
                                var test = Boolean(«something») // true
                                • 0
                                  Ну, самый короткий вариант это !!+
                                  console.log( !!+ new Boolean(false) ) // false
                                  


                                  Но даже это не прикольно. Про использование конструктора для преобразования я знаю.
                              • 0
                                Теоретически, можно «по соглашению» делать — функция toBoolean, которая вызывает метод .toBoolean у аргумента. Но это не так изящно, как если бы поддерживалось языком.
                                • 0
                                  да можно Object.prototype.toBoolean = function а так каст в bool вшит в язык
                                • +2
                                  Чем не угодил valueOf()? Проблема в том что object → boolean всегда true.
                                  • 0
                                    > Проблема в том что object → boolean всегда true
                                    вот этим самым и не угодил
                        • 0
                          foo — элементарный bar — объект естественно что он не интерпретируется в false при отрицании
                          • 0
                            Но Вы ведь выше говорили, что
                            в js все есть объекты

                            ;)
                            • 0
                              ну примитивы нужны, но они преобразуются автоматически в объект «Хабр».length при вызове методов
                              • 0
                                Примитивы преобразуются в объекты с помощью дот-оператора, а объекты преобразуются в примитивы при помощи операторов +, -, <, >, !, etc.

                                Говорить, что всё в JS объекты (или преобразуются в объекты) — это всё равно что говорить что всё числа (или преобразуются в числа).

                                Не смущайте новичков.
                                • Новичков, может, это и смущает, но знать стоит.

                                  Были времена, когда всё было числами выражено. Всё выражалось двоичном кодом, что в памяти хранилось, поэтому и имело место говорить, что всё числа. Тогда же и обрабатывали мы их таким образом. Сейчас же другая ситуация: в JS нет (на соотв. уровне абстракции) понятия памяти, ея управления и др.; вместо этого нам предоставляется мощный инструмент для работы к объектами. Обратите внимание: только null и undefined не могут быть сведены к Object (что, кстати, было бы странно). Это дает нам полное право утверждать, что всякая структура данных выражается объектом. На практике, собственно, так и бывает: со всеми структурами можно проводить характерные «объектные» манипуляции.

                                  Я, правда, соглашусь, что примитивы нужны. Сложно было бы осваивать язык, если б числа, строки и булевы величины вели себя «более объектно». А так на данный момент на них можно смотреть как на примитивы, так и как на объекты.
                                  • 0
                                    Примитивы может и не нужны, во многих языках их нет и всё в порядке. Стоит понимать разницу между примитивами и объектами и не валить всё в одну кучу.
                                    Обратите внимание, что даже null и undefined могут быть сведены к Number. Это дает нам полное право утверждать, что всякая структура данных выражается числом. То же верно и про строку и про boolean.

                                    {valueOf: function () { return 6; }} / 2 === 3

                                    это то же самое что

                                    «habr».length === 4

                                    оператор преобразовал значение в нужный нам тип чтобы разделить его на два или взять свойство length.
                                  • 0
                                    не с помощью дот оператор а с помощью вызова метода. Возь пример из комментария выше
                                    var num = 123;
                                    num.test = 42;
                                    console.log( num.test ); // undefined

                                    Говорить, что все объекты можно, это сделано для удобства ибо в любой момент можно сделать «ааа».length и примитив будет вести себя как объект. Вот из-за этого и возникает термин «все объекты», так как разница между объектом и примитивом, а скажем в java заметна, хотя там тоже главная идеология объекты.

                                    я бы мог только undefined назвать костылем, так как null в данном случае является нормальным значением в мире объектов, указывая на отсутствие оного
                                    • +2
                                      Какого метода?
                                      Говорить, что всё объекты — это то же самое что говорить, что киты и дельфины это рыбы. Так оно может и проще рассуждать, только это неправда и в приведённом вами коде это явно видно.
                        • 0
                          • 0
                            Разве это подводный камень? Это общеизвестное, задокументированное поведение.
                        • 0
                          Может написать статью по объектам в js? надо погуглить хабр есть ли тут такое

                          В общем, если есть что писать — пишите.
                          • 0
                            никогда не писал, а фигню писать не хочу

                            Что касается примитивов при необходимости он преобразуются в объект.
                        • +1
                          В JavaScript, неважно, выполняется ли скрипт в браузере или в ином окружении, всегда определен глобальный объект. Любой код в нашем скрипте, не «привязанный» к чему-либо (т.е. находящийся вне объявления объекта) на самом деле находится в контексте глобального объекта.

                          Это не справедливо для strict mode, this там будет undefined

                          6 способ вызова функции — косвенный вызов функции. Это когда мы вызываем функцию не напрямую someFunc(args), а через результат выполнения какой-нибудь операции, например используя запятую: (1, someFunc)(args). Такой вызов — единственный способ получить глобальный объект в strict mode, с помощью косвенного вызова eval
                        • 0
                          А кто нибудь знает, как вызвать функцию одновременно с new и call/apply?
                          Т.е. у меня есть параметры в массиве, я не знаю что там в этом массиве, и нужно вызвать функцию как конструктор с этими параметрами.
                          • 0
                            Никак :(
                            • 0
                              Есть хитрость на самом деле.
                            • 0
                              Можно вынести код из конструктора в прототип (например метод init) и после конструирования через new выполнить вынесенный метод с помощью apply. Конечно всё это не ручками хорошо бы делать, а написать обёртку какую-нибудь
                              • –1
                                eval?
                                • 0
                                  Если вы про создание объекта по массиву аргументов конструктора, то, например (но не работает для системных конструкторов, только для пользовательских):

                                  // create objects with arbitrary-length args to constructor
                                  var constructor_apply = function ( constructor, args )
                                  {
                                  function new_const( constructor, args )
                                  {
                                  return constructor.apply( this, args );
                                  };
                                  new_const.prototype = constructor.prototype;
                                  return new new_const( constructor, args );
                                  };
                                  //…
                                  var o = constructor_apply( MyClass, [ 42 ] );

                                  Ждём штатный способ в новых версиях JS…
                                  • 0
                                    В AtomJS я реализовал метод Class.factory так:
                                    Constructor.prototype.factory = new function() {
                                    	function Factory(args) { return Constructor.apply(this, args); }
                                    	Factory[prototype] = Constructor[prototype];
                                    	return function(args) { return new Factory(args || []); }
                                    }
                                    


                                    Потом можно вызывать метод factory:
                                    Constructor.factory([1, 2, 3]) == new Constructor(1, 2, 3)
                                    
                                    • 0
                                      Ой, prototype там лишний. Там просто
                                      Constructor.factory = new function() {
                                      
                                      • 0
                                        Тем не менее, это всё равно hack который не сработает для «нативных» конструкторов.
                                        • 0
                                          Array.factory = new function() {
                                          	function Factory(args) { return Array.apply(this, args); }
                                          	Factory.prototype = Array.prototype;
                                          	return function(args) { return new Factory(args || []); }
                                          }
                                          console.log( Array.factory([5]).length ); // 5
                                          
                                          • 0
                                            String? Number? Boolean? :)
                                            • 0
                                              Мне просто интересно, зачем оно может понадобиться для String, Number или Boolean =))
                                              • 0
                                                Ну я могу написать функцию которая будет по разному себя вести в зависимости от того как она вызывается, как конструктор или как функция.

                                                Я к тому, что решение хорошее, но небезупречное. Это как if (a instanceof Array), в большинстве случаев сработает…
                                                • 0
                                                  Ну я могу написать функцию которая будет по разному себя вести в зависимости от того как она вызывается, как конструктор или как функция.

                                                  Угу, у меня в atom.Class такое реализовано. Был вдохновлён Number

                                                  Кстати, почему оно не работает с примитивными объектами?
                                                  • 0
                                                    Что оно? И что такое «примитивные объекты»?
                                                    • 0
                                                      Понял. По ночи не до конца сообразил))
                                                    • 0
                                                      Всё, дошло.
                                                    • 0
                                                      Кстати, как обмануть такую фабрику? Обычный подход с this instanceof Class — не подходит:
                                                      var Class = function () {
                                                          if ( this instanceof Class ) {
                                                              console.log('first');
                                                          } else {
                                                              console.log('second');
                                                          }
                                                      };
                                                      Class.factory = new function() {
                                                      	function Factory(args) { return Class.apply(this, args); }
                                                      	Factory.prototype = Class.prototype;
                                                      	return function(args) { return new Factory(args || []); }
                                                      }
                                                      Class.factory();
                                                      
                                                      • 0
                                                        Действительно, был неправ. Подмена прототипа закрывает все дыры. Остаются только нативы типа Number, но ими можно пренебречь. :)
                                        • +1
                                          Поправьте плиз
                                          с
                                          return [ this, arg1, arg 2];
                                          на
                                          return [ this, arg1, arg2 ];
                                          • +2
                                            Как раз на днях пришлось разбираться с поведением this. К перечисленным четырём вариантам я бы, пожалуй, добавил ещё и пятый, который не то, чтобы совсем особенный, но всё же неочевидный.

                                            Возьмём такой код:
                                            setTimeout(someObj.someFunc, 1000);

                                            Даже когда я прочёл описания всех четырёх механизмов (глобальный вызов, конструктор, метод объекта, call/apply), я был уверен, что при выполнении someFunc() значением this будет someObj. На самом деле — ничего подобного, он будет равен window. Чёткого и внятного объяснения в интернетах я найти не смог, но по обрывкам рассуждений понял, что в данном случае движок JS сохраняет для отложенного вызова лишь ссылку на функцию, после чего вызывает её из глобального контекста, а информация, что хотели вызвать не просто функцию, а именно метод объекта someObj, к этому моменту уже потеряна.
                                            • +5
                                              Объяснение этому следующее: функции в JS являются объектами первого класса и не имеют неявной привязки к объекту. Если вы передаёте куда-то в качестве аргумента функцию — вы передаёте только функцию и ничего больше.
                                              • 0
                                                Все верно, представьте себе это так:

                                                var someFunc = someObj.someFunc;
                                                someFunc();

                                                т.е. someFunc ссылается на ту же самую функцию, но вызывается уже в другом контексте.

                                                Лечится например так:

                                                setTimeout( function(){ someObj.someFunc() }, 1000 );

                                                или так:

                                                setTimeout( "someObj.someFunc()", 1000 );
                                                • 0
                                                  // или так:
                                                  setTimeout( someObj.someFunc.bind(someObj) , 1000 );
                                                  // или при помощи mootools:
                                                  someObj.someFunc.delay(1000, someObj);
                                                  
                                                  • 0
                                                    Это не подойдёт, если используется только чистый JS, без сторонних фреймворков. (Первый вариант — это, я так понял, для jQuery?)
                                                    • 0
                                                      Первый вариант, как раз — это чистый Javascript.
                                                      Необходим маленький скрипт для того, чтобы заработало в старых браузерах.
                                                      • 0
                                                        Понятно. Попытка гугления вывалила лишь кучу ссылок на Prototype и jQuery, до сайта Мозиллы я не добрался. Буду знать.

                                                        PS: Наверное, всё же, не «в старых браузерах», а в неподдерживающих. Потому что даже в последнем снэпшоте Оперы такой функции нет.
                                                  • 0
                                                    Лечится например так:
                                                    Именно так в конце концов и поступил (по первому способу, второй не прокатил бы, т.к. в качестве someObj выступал локальный объект, а не глобальный). Но с точки зрения незамутнённого сознания всё-таки не совсем логично создавать новую «пустую» функцию исключительно для того, чтобы сохранить привязку к объекту…
                                                    • 0
                                                      Можно сделать функцию с параметром, в котором передаётся объект :)
                                                • +1
                                                  В данном случае, мы как бы скопировали всю функцию, включая список аргументов и тело, и присвоили получившийся объект свойству make объекта arrayMaker. Это равносильно такому объявлению:
                                                  var arrayMaker = {
                                                  someProperty: 'Какое-то значение';
                                                  make: function (arg1, arg2) {
                                                  return [ this, arg1, arg 2];
                                                  }
                                                  };

                                                  Не скопировали, а присвоили ссылку на созданную функцию новой переменной. И это не равносильно такому объявлению, так как в нем как раз создается новая аналогичная функция.

                                                  Пы.Сы. Заголовок звучит как «Пять способов вызвать функцию», хотя описано только четыре (если конечно apply и call не считаются двумя принципиально разными способами).
                                                  • +5
                                                    Не описан способ вызова анонимных функций.
                                                    (function (x){ console.log(x);})('Hello world');
                                                    

                                                    или
                                                    !function (x){ console.log(x);}('Hello world');
                                                    


                                                    или именованных функций

                                                    (function f(x){ console.log(x); return f; })('Hello')('world');
                                                    // при этом прошу заметить
                                                    f('foo'); // exception
                                                    


                                                    или для рекурсии

                                                    (function f(x){ console.log(x); if(x>0) f(x-1);})(10);
                                                    


                                                    • +1
                                                      Есть ещё несколько методов из будущего (неявный вызов функции):
                                                      // 1. через ключевое слово get
                                                      var o = {
                                                        get pewpew () {
                                                            console.log('pass');   
                                                        }
                                                      }
                                                      // неявно вызываем функцию pewpew
                                                      o.pewpew;
                                                      
                                                      // 2. через __defineGetter__
                                                      var o = {};
                                                      o.__defineGetter__("pewpew", function blabla(){
                                                          console.log('pass');
                                                      });
                                                      
                                                      // неявно вызываем функцию blabla
                                                      o.pewpew;
                                                      
                                                      // 3. через __defineSetter__
                                                      Object.prototype.__defineSetter__("pew", function blabla(a){
                                                          console.log('pass');
                                                      });
                                                      
                                                      // неявно вызываем функцию blabla
                                                      ({}).pew = 152;
                                                      
                                                      // 4. через Proxy (существует куча ловушек, обработчик каждой можно неявно вызвать)
                                                      var o = Proxy.create({
                                                          get: function () {
                                                              console.log('pass');
                                                          }
                                                      }, {});
                                                      
                                                      // неявно вызываем функцию-ловушку get
                                                      o.pewpew1;
                                                      o.pewpew2;
                                                      o.pewpew3;
                                                      

                                                      С помощью __defineSetter__, кстати, можно провернуть один трюк с JSONP. Вместо функции обработчика можно передать ({}).pew= и настроить обработчик на «pew»:
                                                      Object.prototype.__defineSetter__("pew", function pewHook(a){
                                                          console.log(a.aaa === 123);
                                                          // снимаем обработчик "pew" ...
                                                      });
                                                      
                                                      // Сервер подчинится и отправит нам код в таком формате, как будто это вызывается какая-то функция:
                                                      ({}).pew=({'aaa':123});
                                                      
                                                      // Дальше отработает pewHook
                                                      
                                                      Ничего полезного в этом нет (просто «финт ушами»), разве что не засоряются глобалы на время ожидания JSONP. Не все сервера принимают обработчик такого формата (проверял на Flickr — не заработало).

                                                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.