Подтвердить что ты не робот

Какие шаблоны дизайна используют преимущества JavaScript-манипуляции?

Отличная статья Ben Cherry объясняет подъем в JavaScript. Моя проблема, однако, в том, что я не могу представить себе прецедент для этого пресловутого исполнителя беспорядка. Пожалуйста, объясните, есть ли шаблон дизайна, который действительно использует преимущества этой языковой функции.

Во-вторых, является ли область видимости уникальной для JavaScript?

UPDATE --- Я добавляю щедрость для ответа, который удовлетворяет моему любопытству: Какие шаблоны дизайна действительно используют поведение JavaScript-hoisting? я понять, почему JavaScript поддерживает подъем, но я хочу знать, как я могу воспользоваться этой функцией.

4b9b3361

Ответ 1

Переменный подъем

Одним из самых простых применений подъема является перемещение переменной. Если бы у нас не было переменной подъема, это заставило бы ReferenceError:

var bar = foo; 
var foo;

Это не кажется сразу полезным, но это позволяет нам делать такие вещи:

var myCoolJS = myCoolJS || {};

В основном это означает, что это выглядит так: myCoolJS есть myCoolJS, если он существует, или новый объект, если он этого не делает. Второй myCoolJS не бросает ReferenceError, если myCoolJS еще не существует, потому что это объявление переменной поднято.

Это избавит нас от выполнения неудобной проверки typeof myCoolJS != 'undefined'.

Подъем функции

Функция подъема может быть особенно полезна при объединении нескольких сценариев в один. Например, я создал легкую реализацию времени сборки модулей CommonJS. Это обеспечивает те же функции module, require и exports, которые находятся в node.js. Я построил инструмент, чтобы необходимые модули состояли из нескольких файлов. Например, require('/foo') может привести к созданию модуля, состоящего из двух файлов, foo.js ( "body file" ) и foo.h.js ( "файл заголовка" ).

Это позволяет "файлу тела" не знать о свободных переменных, предоставляемых средой модулей CommonJS; все это обрабатывается в заголовке. Это делает код многоразовым и легко тестируется без сборки. Однако, поскольку заголовки добавляются к телу, мы используем функцию hoisting в файле body, чтобы разрешить экспорт в заголовках. Например:

// dom.h.js

var util = require('util').util;

exports.css = css; // we can do this because "css" is hoisted from below

// ... other exports ...

...

// dom.js

function css(){}; // this would normally just be an object.

css.hasClass = function(element) { ... };
css.addClass = function(element) { ... };

// ...other code...

Ответ 2

Здесь используется для подъема:

(function() {
    var factorial = function(n) {
        if(n == 0)
            return 1;
        return n * factorial(n - 1);
    };
})();

Без подъема это не будет компилироваться, потому что factorial еще не существует внутри функционального литерала. Вам придется объявить переменную отдельно или использовать именованную функцию.

JavaScript также позволяет использовать следующий код:

var test = function(b) {
    if(b) {
        var value = 2;
    } else {
        var value = 5;
    }
    console.log(value);
};

С блочным просмотром вы должны добавить еще одну строку, чтобы объявить value до if.

Чтобы быть справедливым, этот код работает из-за возможности функции, а не для подъема. И JavaScript мог иметь область действия без подъема. Ruby справляется с этим лучше: Ruby имеет область методов для переменных, но переменные не существуют, пока вы их не установили:

def test(b)
    # unlike JavaScript, value is not accessible here
    if b
        value = 2
    else
        value = 5
    end
    puts value
end

Ответ 3

JavaScript не имеет области блока (пусть забудьте о let), и поэтому объявление любой переменной объявляется для всей функции, из которой у JavaScript есть область видимости.

Если вы подумаете об этом, JavaScript-подъем может иметь больше смысла.

Если вы помните о подъеме, это не должно быть источником ошибок и путаницы. Это просто один из тех причуд, которые вы должны понимать и помнить.

Я не уверен, что листинг ограничен JavaScript. Я никогда не слышал об этом в другом месте, но это не обязательно означает, что он не существует на других языках.

Ответ 4

Первые два примера в этой статье просто плохо написаны. Плохой код явно приводит к ошибкам и путанице. Позвольте мне дать вам рефакторинговые версии этих примеров. Вы увидите, что здесь нет путаницы...

Пример 1 - Исходный код

var foo = 1;
function bar() {
    if (!foo) {
        var foo = 10;
    }
    alert(foo);
}
bar();

Пример 1 - Рефакторированный код (удаленная путаница)

var foo = 1;

function bar() {
    var foo;

    if ( !foo ) {
        foo = 10;
    }

    alert( foo );
}

bar();

В предупреждении отображается "10", и понятно, почему. Здесь нет путаницы.

Пример 2 - Исходный код

var a = 1;
function b() {
    a = 10;
    return;
    function a() {}
}
b();
alert(a);

Пример 2 - Рефакторированный код (удаленная путаница)

var a = 1;

function b() {
    var a = function () {}; 
    a = 10;
    return; 
}

b();

alert( a );

В предупреждении отображается "1". Очевидно. Здесь нет путаницы.

Ответ 5

"hoisting" не является частью стандарта ECMAScript, но он говорит, что переменная внутри функции объявляется в начале функции независимо от того, где в функции она помещается в коде.

Пример

(function() {
  alert(myvar); // undefined
  var myvar = 'local value';
})();

Внутренне Javascript объявит myvar перед предупреждением, покажет предупреждение, затем он назначит myvar "локальному значению".

Итак, Javascript будет интерпретировать этот код как:

(function() {
  var myvar;
  alert(myvar); // undefined
  myvar = 'local value';
})();

Вот почему "Java the Good parts" имеет руководство, в котором говорится, что вы должны объявлять переменную в верхней части своей функции.

Источник: http://net.tutsplus.com/tutorials/javascript-ajax/quick-tip-javascript-hoisting-explained/

"Пожалуйста, объясните, есть ли шаблон дизайна, который действительно использует эту особенность языка". "подъем" - это не функция, а следствие того, как интерпретатор Javascript структурирует код, поскольку язык использует обзор функций.

"Какие шаблоны проектирования действительно используют поведение JavaScript для подъема?" Ответ: Нет.

Ответ 6

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

function foo()
{
   function a()
   {
      //...
   }

   function b()
   {
      //...
   }
}

также может быть записана как:

function foo()
{
   var a = function ()
   {
      //...
   }

   var b = function ()
   {
      //...
   }
}

Без подъема следующее приведет к ошибке:

function foo()
{
   var a = function ()
   {
      b();
   }
   a(); //Error in function since b is not yet defined

   var b = function ()
   {
      //...
   }
}

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

Ответ 7

Здесь реальный прецедент (хотя и приведенный к псевдокоду) для вас, от кого-то, кто действительно хотел бы использовать преимущества подъема в дикой природе.

Недавно я написал этот script для обработки простой проверки и подачи формы. Насколько возможно, каждое объявление функции вызывает следующее. Это имеет 2 главных преимущества для удобочитаемости:

  • Логическая последовательность: в коде есть последовательный поток, что означает, что функции всегда будут вызываться до их объявления. Это преимущество в том, что при использовании с функциями с низкой степенью сложности он сохраняет отношения относительно плоскими и дает вам представление о вызывающем контексте функции незадолго до ее источника. Вам просто придется прокручивать вниз (никогда не вверх), чтобы следовать коду, и - насколько это возможно - прокручивать не совсем или очень мало.
  • Низкие ссылочные накладные расходы. Мне нравится сохранять все объявления переменных в верхней части каждой области, чтобы читатели знали обо всех движущихся частях, которые требуется функции перед чтением через свое тело, но никто не хочет прочитайте каждый инициированный исходный код функции, чтобы получить представление о том, что делает текущая область действия. Используя этот метод, вы никогда не столкнетесь с ссылкой на функцию перед ее объявлением. Сначала это звучит глупо, но на самом деле это уменьшает когнитивные издержки: вам никогда не дают источник функции с неявным помните об этом - мы будем использовать его позже - вместо этого вы только когда-либо читаете источник функции, как только знаете контекст, который он назвал в.
$( function emailHandler(){
  var $form      = …
  var $email     = …
  var $feedback  = …
  var value      = …
  var validation = …

  // All initialisation is right here. Executes immediately.
  // Therefore, all future code will only ever be invoked
  // by call stacks passing through here.
  void function bindEvents(){
    $email.on( 'input', filterInput );

    $form.on( 'submit', preSubmit );
  }();

  function filterInput( inputEvent ){
    if( inputEvent && inputEvent.which === '13' ){
      return presubmit( inputEvent );
    }

    return validate();
  }

  function validate(){
    var presentValue = $email.val();

    if( validation.done || presentValue === value ){
        return;
    }
    else if( presentValue === placeholder || presentValue === '' ){
        validation.message = 'You must enter an email address to sign up';
        validation.valid   = false;
    }
    else if( !validation.pattern.test( presentValue ) ){
        validation.message = 'Please enter a valid email address';
        validation.valid   = false;
    }
    else {
        validation.message = '';
        validation.valid   = true;
    }

    validation.ever = true;

    clearFeedback();
  }

  function preSubmit( inputEvent ){
    if( inputEvent instanceof $.Event ){
      inputEvent.preventDefault();
    }

    if( !validation.ever ){
      validate();
    }
    if( validation.pending || validation.done ){
      return;
    }
    else if( validation.valid ){
      return submit();
    }
    else {
      return feedback();
    }
  }

  function submit(){
    $form.addClass( 'pending' );

    // Create an XHR base on form attributes
    $.ajax( {
      cache : false,
      url   : $form.attr( 'action' ),
      data  : $form.serialize(),
      type  : $form.attr( 'method' ).toUpperCase()
    } )
      .done( success )
      .fail( failure )
      .always( feedback );
  }

  function success( response ){
    validation.message = response.message;
    validation.valid   = response.valid;
  }

  function failure(){
    validation.message = 'Our server couldn\'t sign you up. Please try again later.';
    validation.error   = true;
  }

  function feedback(){
    clearFeedback();

    if( validation.message ){
      $feedback
        .html( validation.message )
        .appendTo( $placeholder );
    }

    if( !validation.valid || validation.error ){
      $form.addClass( 'invalid' );

      $email.trigger( 'focus' );
    }
    else {
      $form.addClass( 'done' );

      validation.done = true;
    }

    validation.pending = false;
  }

  function clearFeedback(){
    $form.removeClass( 'invalid pending done' );
  }
} );

Ответ 8

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

Я могу только представить себе несколько тривиальных случаев. Основное свойство для использования заключается в том, что переменная может быть объявлена ​​(но undefined), а затем назначена только в одной строке кода, но с событиями, интерпретируемыми в двух разных точках.

С объявлением в конце цикла (а не .forEach, поскольку это, конечно, задает область), вы можете использовать это для обнаружения первой итерации.

var notfirst = true;  // this is actually never referenced.

(function () {  
  var number, stack = [1, 2, 3, 4, 5];

  while (number = stack.pop()) {
    if (notfirst) console.log(number);
    var notfirst = true;
  }
})();

Выход из опорожнения стека равен 4, 3, 2, 1. 5 отклоняется.

Опять же. Не делайте этого!

Ответ 9

Если вы рассматриваете способ написания других языков (С++/Java) и как их шаблоны классов, то для подъема можно использовать аналогичный шаблон для создания прототипов.