Пользователь
0,0
рейтинг
24 мая 2011 в 01:13

Разработка → Пять способов вызвать функцию перевод

Мне часто приходится сталкиваться с 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, поэтому имеет смысл предупреждать их возникновение заранее.
Перевод: Sergio Pereira
Павел Носов @kaluzhanin
карма
57,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (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
    });
    


    Конечно, в простых приложениях такая магия нужна редко. Но в фреймворках она используется очень часто и без неё было бы очень тяжело построить изящный интерфейс.
    • +6
      var array = [].slice.call(arguments);

      Имхо здесь лучше использовать Array.prototype.slice.call, он хоть и выглядит не так коротко и изящно, но зато позволяет не создавать лишний объект.
    • 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
                      


                      Но даже это не прикольно. Про использование конструктора для преобразования я знаю.
                      • +4
                        Самый короткий это >0
                        console.log(new Boolean(false) > 0);
                        • 0
                          great)
                  • 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 объекты (или преобразуются в объекты) — это всё равно что говорить что всё числа (или преобразуются в числа).

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

                    Были времена, когда всё было числами выражено. Всё выражалось двоичном кодом, что в памяти хранилось, поэтому и имело место говорить, что всё числа. Тогда же и обрабатывали мы их таким образом. Сейчас же другая ситуация: в 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
      В последнем предложении вы что-то намудрили…
      • 0
        Где именно?
        • 0
          Извиняюсь, неправильно понял.
  • 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 — не заработало).

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