Поиск по этому блогу

среда, 27 июля 2016 г.

Ифи - IIFE - Immediately-invoked function expression ...and two sets of parentheses... и приемы сборки модулей js

Дабы не путаться в круглых скобках javascript здесь конспект и ссылки для понимания паттернов (function() { ... })(); и someFunctionCall()()
А потом добавил сюда 4 ссылки для двух статей JavaScript модули: сборка модулей. В следующих постах надо будет разобрать приемы сборки jQuery и Bootstrap (например).

Immediately-invoked function expression
Immediately-Invoked Function Expression (IIFE) In JavaScript, every function, when invoked, creates a new execution context. Because variables and functions defined within a function may only be accessed inside, but not outside, that context, invoking a function provides a very easy way to create privacy.
Advanced Javascript: Why is this function wrapped in parentheses? [duplicate]
Two sets of parentheses after function call
JS Function With Two Parentheses and Two Params
JavaScript syntax wiki defensive semicolon


Explain JavaScript's encapsulated anonymous function syntax
JavaScript модули: руководство для начинающих Если вы новичок в JavaScript, такие специфические выражения как «упаковка модулей», «Webpack и Browserify» и «AMD и CommonJS» могут вас напугать. Система модулей JavaScript может быть очень пугающей, но ее понимание имеет важное значение для веб-разработки. Кажется, что информации слишком много, но на самом деле это только верхушка айсберга, когда мы говорим о шаблоне модулей. Вот некоторые ресурсы, которые могут оказаться полезными:
Обучение шаблонам дизайна JavaScript: Эдди Османи: множество деталей в выразительной и легкой для прочтения статье. Полезный обзор с примерами расширенного использования шаблона модулей. Блог Карла Дэнли: обзор шаблона модулей и ресурсы для других шаблонов JavaScript.
JavaScript модули: сборка модулей Во второй части я расскажу, что именно означает «сборка» модулей: почему мы это делаем, способы сборки и будущее модулей в веб-разработке.
ECMA-262 5th Edition in HTML Format Complete document

+ is just one of the options

It forces the parser to treat the part following the + as an expression. This is usually used for functions that are invoked immediately, e.g.:

In [ ]:
+function() { console.log("Foo!"); }();

Without the + there, if the parser is in a state where it's expecting a statement (which can be an expression or several non-expression statements), the word function looks like the beginning of a function declaration rather than a function expression and so the () following it (the ones at the end of the line above) would be a syntax error (as would the absense of a name, in that example). With the +, it makes it a function expression, which means the name is optional and which results in a reference to the function, which can be invoked, so the parentheses are valid.

  • is just one of the options. It can also be -, !, ~, or just about any other unary operator. Alternately, you can use parentheses (this is more common, but neither more nor less correct syntactically):
In [ ]:
(function() { console.log("Foo!"); })();
// or
(function() { console.log("Foo!"); }());
In [ ]:
$filter('number')(number[, fractionSize])

It means that the first function ($filter) returns another function and then that returned function is called immediately. For Example:

In [ ]:
function add(x){
  return function(y){
    return x + y;
  };
}

var addTwo = add(2);

addTwo(4) === 6; // true
add(3)(4) === 7; // true

Here's how: You detect how many arguments your function has received and, if it's received only one, you return a function instead of a number — and have that function add in the second number if it gets called:

In [ ]:
function add(a,b) {
  if (arguments.length === 1) {
    return function(b2) { // You could call this arg `b` as well if you like,
      return a + b2;      // it would shadow (hide, supercede) the one above
    };
  }
  return a + b;
}
snippet.log(add(10, 10)); // 20
snippet.log(add(10)(10)); // 20
In [ ]:
(function() {
  // some code
})();

This provides a way to execute some JS code in it's own scope. It's usually used so that any variables created within the function won't affect the global scope. You could use this instead:

In [ ]:
function foo() {
  // some code
}
foo();

But this requires giving a name to the function, which is not always necessary. Using a named function also means at some future point the function could be called again which might not be desirable. By using an anonymous function in this manner you ensure it's only executed once.

This syntax is invalid:

In [ ]:
function() {
  // some code
}();

Because you have to wrap the function in parens in order to make it parse as an expression. More info here: http://benalman.com/news/2010/11/immediately-invoked-function-expression/

So to recap quickly on the IIFE pattern

In [ ]:
(function() {
  // some code
})();

#

In [ ]:
(function () {  })();
(function () {  }());  // Douglas Crockford's style

There are other ways to enforce a function expression:

In [ ]:
!function () {}();
~function () {  }();
-function () {  }();
+function () {  }();

In contexts where an expression is expected, wrapping in parentheses is not necessary:

In [ ]:
var f = function () {  }();
true && function () {  }();
0, function () {  }();

Passing variables into the scope is done as follows:

In [ ]:
(function(a, b) {  })("hello", "world");

JavaScript syntax wiki defensive semicolon

In [7]:
a = b + c
;(function () {
  // code
})();
  File "<ipython-input-7-836dd2459463>", line 3
    (/, code)
    ^
IndentationError: unexpected indent

…to avoid being parsed as c(…)

In [8]:
%%javascript
var b=2;
var c=3;
var a = b + c;
;;;;debugger;
;(function (x) {
  return console.log(x+2); // 7
})(a);

Итак, "защитный семиколон" (точка с запятой) используется еще и при сборке склейке js файлов. А что будет, если этих самых семиколонов будет два подряд? Пять подряд? Попробовал - ничего не будет, консоль chrome выводит 7 и помалкивает.

Пример 1: Анонимное замыкание

In [ ]:
(function () {
        ...
        }());

Пример 2: Глобальный импорт

In [ ]:
(function (globalVariable) {
        ...
        globalVariable.filter = function(collection, test) {....}
        ...
}(globalVariable));        

Пример 3: Объектный интерфейс.

In [ ]:
var myGradesCalculate = (function () {
    
  // Keep this variable private inside this closure scope
  var myGrades = [93, 95, 88, 0, 55, 91];
 
  // Expose these functions via an interface while hiding
  // the implementation of the module within the function() block
 
  return {
    average: function() {
      var total = myGrades.reduce(function(accumulator, item) {
        return accumulator + item;
        }, 0);
        
      return'Your average grade is ' + total / myGrades.length + '.';
    },
 
    failing: function() {
      var failingGrades = myGrades.filter(function(item) {
          return item < 70;
        });
 
      return 'You failed ' + failingGrades.length + ' times.';
    }
  }
})();
 
myGradesCalculate.failing(); // 'You failed 2 times.' 
myGradesCalculate.average(); // 'Your average grade is 70.33333333333333.'

Пример 4: Выявление шаблона модуля.

Это чем-то напоминает предыдущий подход за исключением того, что все методы и переменные по умолчанию остаются приватными, пока не подвергаются явному воздействию:

In [ ]:
var myGradesCalculate = (function () {
    
  // Keep this variable private inside this closure scope
  var myGrades = [93, 95, 88, 0, 55, 91];
  
  var average = function() {
    var total = myGrades.reduce(function(accumulator, item) {
      return accumulator + item;
      }, 0);
      
    return'Your average grade is ' + total / myGrades.length + '.';
  };
 
  var failing = function() {
    var failingGrades = myGrades.filter(function(item) {
        return item < 70;
      });
 
    return 'You failed ' + failingGrades.length + ' times.';
  };
 
  // Explicitly reveal public pointers to the private functions 
  // that we want to reveal publicly
 
  return {
    average: average,
    failing: failing
  }
})();
 
myGradesCalculate.failing(); // 'You failed 2 times.' 
myGradesCalculate.average(); // 'Your average grade is 70.33333333333333.'

CommonJS и AMD

Вам, наверное, интересно, есть ли способ построения интерфейса модуля без прохождения через глобальную область видимости?

Если вы программировали на Node.js, вы уже умеете работать в CommonJS

In [ ]:
function myModule() {
  this.hello = function() {
    return 'hello!';
  }
 
  this.goodbye = function() {
    return 'goodbye!';
  }
}
 
module.exports = myModule;

Asynchronous Module Definition, или AMD для краткости. Загрузка модулей с помощью AMD выглядит примерно так:

In [ ]:
define(['myModule', 'myOtherModule'], function(myModule, myOtherModule) {
  console.log(myModule.hello());
});
1
2
3
define(['myModule', 'myOtherModule'], function(myModule, myOtherModule) {
  console.log(myModule.hello());
});

Определяемая функция берет в качестве первого аргумента массив из зависимостей модуля. Эти зависимости загружаются в фоновом (неблокирующемся) режиме, и после загрузки происходит обращение к обратному вызову функции. Далее, обратный вызов функции принимает в качестве аргументов зависимости, которые были загружены – в нашем случае, myModule и myOtherModule  – позволяя функции использовать эти зависимости. И, наконец, сами зависимости также должны быть определены с помощью ключевого слова define.

In [ ]:
define([], function() {
 
  return {
    hello: function() {
      console.log('hello');
    },
    goodbye: function() {
      console.log('goodbye');
    }
  };
});

UMD

Для объектов, которым требуется поддержка функций AMD и CommonJS, есть еще один вариант: Universal Module Definition (UMD).

UMD позволяет использовать сразу оба метода и в то же время поддерживать определение глобальных переменных.

In [ ]:
(function (root, factory) {
  if (typeof define === 'function' && define.amd) {
      // AMD
    define(['myModule', 'myOtherModule'], factory);
  } else if (typeof exports === 'object') {
      // CommonJS
    module.exports = factory(require('myModule'), require('myOtherModule'));
  } else {
    // Browser globals (Note: root is window)
    root.returnExports = factory(root.myModule, root.myOtherModule);
  }
}(this, function (myModule, myOtherModule) {
  // Methods
  function notHelloOrGoodbye(){}; // A private method
  function hello(){}; // A public method because it's returned (see below)
  function goodbye(){}; // A public method because it's returned (see below)
 
  // Exposed public methods
  return {
      hello: hello,
      goodbye: goodbye
  }
}));

Native JS

Как вы уже заметили, ни один из модулей выше не является родным для JavaScript. Вместо этого мы создали способы для имитации модулей системы, используя шаблон, CommonJS или AMD. К счастью, умные люди в TC39 (стандарт, определяющий синтаксис и семантику ECMAScript) добавили ES2015 встроенные модули.

ES2015 предлагает разнообразные возможности для импорта и экспорта модулей, вот некоторые из этих ресурсов: jsmodules.io exploringjs.com

Чем ES2015 отличаются от CommonJS или AMD и как ему удается вместить в себя лучшее из обоих: компактный и декларативный синтаксис, асинхронную загрузку и дополнительные преимущества – улучшенная поддержка циклических зависимостей. Моей любимой особенностью ES2015 является то, что импорт имеет живые отображения экспорта. (В отличии от CommonJS, где импорт является только копией экспорта). Вот пример того, как это работает:

In [ ]:
// lib/counter.js
 
var counter = 1;
 
function increment() {
  counter++;
}
 
function decrement() {
  counter--;
}
 
module.exports = {
  counter: counter,
  increment: increment,
  decrement: decrement
};
 
 
// src/main.js
 
var counter = require('../../lib/counter');
 
counter.increment();
console.log(counter.counter); // 1

Посты чуть ниже также могут вас заинтересовать

1 комментарий:

MD Rasel комментирует...

Wilder vs Fury 3: Date and epoch Fury is slated to fight Wilder for the third period against after the Bronze Bomber took up his rematch clause. The date and location of the bout has not officially been stated yet and was time-lucky to be held at the MGM Grand in the middle of considering than more in credit to July 18.

Wilder vs Fury 3