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


История


Новые добавления в язык называются ECMAScript 6. Или ES6 или ES2015+.


С момента появления в 1995, JavaScript развивался медленно. Новые возможности добавлялись каждые несколько лет. ECMAScript появился в 1997, его целью было направить развитие JavaScript в нужное русло. Выходили новые версии – ES3, ES5, ES6 и так далее.



Как видите, между версиями ES3, ES5 и ES6 есть пропуски длиной в 10 и 6 лет. Новая модель – делать маленькие изменения каждый год. Вместо того, чтобы накопить огромное количество изменений и выпустить их все за раз, как это было с ES6.


Browsers Support


Все современные браузеры и среды исполнения уже поддерживают ES6!



Chrome, MS Edge, Firefox, Safari, Node и многие другие системы имеют встроенную поддержку большинства возможностей JavaScript ES6. Так что, все из этого пособия можно использовать прямо сейчас.


Поехали!


Главные возможности ES6


Все сниппеты можно вставлять в консоль браузера и запускать.


Block scope variables


В ES6 мы перешли от var к let/const.


Что не так с var?


Проблема var в том, что переменная "протекает" в другие блоки кода, такие как циклы for или блоки условий if:


ES5
var x = 'outer';
function test(inner) {
  if (inner) {
    var x = 'inner'; // scope whole function
    return x;
  }
  return x; // gets redefined on line 4
}

test(false); // undefined 
test(true); // inner

В строке test(false) можно ожидать возврат outer, но нет, мы получаем undefined. Почему?


Потому что даже не смотря на то, что блок if не выполняется, на 4й строке происходит переопределение var x как undefined.


ES6 спешит на помощь:


ES6
let x = 'outer';
function test(inner) {
  if (inner) {
    let x = 'inner';
    return x;
  }
  return x; // gets result from line 1 as expected
}

test(false); // outer
test(true); // inner

Изменив var на let мы откорректировали поведение. Если блок if не вызывается, то переменная x не переопределяется.


IIFE (immediately invoked function expression)


Давайте сначала рассмотрим пример:


ES5
{
  var private = 1;
}

console.log(private); // 1

Как видите, private протекает наружу. Нужно использовать IIFE (immediately-invoked function expression):


ES5
(function(){
  var private2 = 1;
})();

console.log(private2); // Uncaught ReferenceError

Если взглянуть на jQuery/lodash или любые другие проекты с открытым исходным кодом, то можно заметить, что там IIFE используется для содержания глобальной среды в чистоте. А глобальные штуки определяются со специальными символами вроде _$ или jQuery.


В ES6 не нужно использовать IIFE, достаточно использовать блоки и let:


ES6
{
  let private3 = 1;
}

console.log(private3); // Uncaught ReferenceError

Const


Можно также использовать const если переменная не должна изменяться.


Итог:


  • забудьте var, используйте let и const.
  • Используйте const для всех референсов; не используйте var.
  • Если референсы нужно переопределять, используйте let вместо const.

Template Literals


Не нужно больше делать вложенную конкатенацию, можно использовать шаблоны. Посмотрите:


ES5
var first = 'Adrian';
var last = 'Mejia';
console.log('Your name is ' + first + ' ' + last + '.');

С помощью бэктика () и интерполяции строк ${}` можно сделать так:


ES6
const first = 'Adrian';
const last = 'Mejia';
console.log(`Your name is ${first} ${last}.`);

Multi-line strings


Не нужно больше конкатенировать строки с + \n:


ES5
var template = '<li *ngFor="let todo of todos" [ngClass]="{completed: todo.isDone}" >\n' +
'  <div class="view">\n' +
'    <input class="toggle" type="checkbox" [checked]="todo.isDone">\n' +
'    <label></label>\n' +
'    <button class="destroy"></button>\n' +
'  </div>\n' +
'  <input class="edit" value="">\n' +
'</li>';
console.log(template);

В ES6 можно снова использовать бэктики:


ES6
const template = `<li *ngFor="let todo of todos" [ngClass]="{completed: todo.isDone}" >
  <div class="view">
    <input class="toggle" type="checkbox" [checked]="todo.isDone">
    <label></label>
    <button class="destroy"></button>
  </div>
  <input class="edit" value="">
</li>`;
console.log(template);

Оба блока кода генерируют одинаковый результат


Destructuring Assignment


ES6 destructing – полезная и лаконичная штука. Посмотрите на примеры:


Получение элемента из массива


ES5
var array = [1, 2, 3, 4];

var first = array[0];
var third = array[2];

console.log(first, third); // 1 3

То же самое:


ES6
const array = [1, 2, 3, 4];

const [first, ,third] = array;

console.log(first, third); // 1 3

Обмен значениями


ES5
var a = 1;
var b = 2;

var tmp = a;
a = b;
b = tmp;

console.log(a, b); // 2 1

То же самое:


ES6
let a = 1;
let b = 2;

[a, b] = [b, a];

console.log(a, b); // 2 1

Деструктуризация нескольких возвращаемых значений


ES5
function margin() {
  var left=1, right=2, top=3, bottom=4;
  return { left: left, right: right, top: top, bottom: bottom };
}

var data = margin();
var left = data.left;
var bottom = data.bottom;

console.log(left, bottom); // 1 4

В строке 3 можно вернуть в виде массива:


return [left, right, top, bottom];

но вызывающему коду придется знать о порядке данных.


var left = data[0];
var bottom = data[3];

С ES6 вызывающий выбирает только нужные данные (строка 6):


ES6
function margin() {
  const left=1, right=2, top=3, bottom=4;
  return { left, right, top, bottom };
}

const { left, bottom } = margin();

console.log(left, bottom); // 1 4

Заметка: В строке 3 содержатся другие возможности ES6. Можно сократить { left: left } до  { left }. Смотрите, насколько это лаконичнее по сравнению с версией ES5. Круто же?


Деструктуризация и сопоставление параметров


ES5
var user = {firstName: 'Adrian', lastName: 'Mejia'};

function getFullName(user) {
  var firstName = user.firstName;
  var lastName = user.lastName;

  return firstName + ' ' + lastName;
}

console.log(getFullName(user)); // Adrian Mejia

То же самое (но короче):


ES6
const user = {firstName: 'Adrian', lastName: 'Mejia'};

function getFullName({ firstName, lastName }) {
  return `${firstName} ${lastName}`;
}

console.log(getFullName(user)); // Adrian Mejia

Глубокое сопоставление


ES5
function settings() {
  return { display: { color: 'red' }, keyboard: { layout: 'querty'} };
}

var tmp = settings();
var displayColor = tmp.display.color;
var keyboardLayout = tmp.keyboard.layout;

console.log(displayColor, keyboardLayout); // red querty

То же самое (но короче):


ES6
function settings() {
  return { display: { color: 'red' }, keyboard: { layout: 'querty'} };
}

const { display: { color: displayColor }, keyboard: { layout: keyboardLayout }} = settings();

console.log(displayColor, keyboardLayout); // red querty

Это также называют деструктуризацией объекта (object destructing).


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


Советы:


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

Классы и объекты


В ECMAScript 6 мы перешли от “функций-конструкторов” к “классам” .


Каждый объект в JavaScript имеет прототип, который является другим объектом. Все объекты в JavaScript наследуют методы и свойства от своего прототипа.

В ES5 объектно-ориентированное программирование достигалось с помощью функций-конструкторов. Они создавали объекты следующим образом:


ES5
var Animal = (function () {
  function MyConstructor(name) {
    this.name = name;
  }
  MyConstructor.prototype.speak = function speak() {
    console.log(this.name + ' makes a noise.');
  };
  return MyConstructor;
})();

var animal = new Animal('animal');
animal.speak(); // animal makes a noise.

В ES6 есть новый синтаксический сахар. Можно сделать то же самое с меньшим кодом и с использованием ключевых слов class и construсtor. Также заметьте, как четко определяются методы: construсtor.prototype.speak = function () vs speak():


ES6
class Animal {
  constructor(name) {
    this.name = name;
  }
  speak() {
    console.log(this.name + ' makes a noise.');
  }
}

const animal = new Animal('animal');
animal.speak(); // animal makes a noise.

Оба стиля (ES5/6) дают одинаковый результат.


Советы:


  • Всегда используйте синтаксис class и не изменяйте prototype напрямую. Код будет лаконичнее и его будет легче понять.
  • Избегайте создания пустого конструктора. У классов есть конструктор по умолчанию если не задать собственный.

Наследование


Давайте продолжим предыдущий пример с классом Animal. Допустим, нам нужен новый класс Lion.


В ES5 придется немного поработать с прототипным наследованием.


ES5
var Lion = (function () {
  function MyConstructor(name){
    Animal.call(this, name);
  }

  // prototypal inheritance
  MyConstructor.prototype = Object.create(Animal.prototype);
  MyConstructor.prototype.constructor = Animal;

  MyConstructor.prototype.speak = function speak() {
    Animal.prototype.speak.call(this);
    console.log(this.name + ' roars ');
  };
  return MyConstructor;
})();

var lion = new Lion('Simba');
lion.speak(); // Simba makes a noise.
// Simba roars.

Не будем вдаваться в детали, но заметьте несколько деталей:


  • Строка 3, напрямую вызываем конструкторAnimal с параметрами.
  • Строки 7-8, назначаем прототип Lion прототипом класса Animal.
  • Строка 11, вызываем метод speak из родительского класса Animal.

В ES6 есть новые ключевые слова extends и super.


ES6
class Lion extends Animal {
  speak() {
    super.speak();
    console.log(this.name + ' roars ');
  }
}

const lion = new Lion('Simba');
lion.speak(); // Simba makes a noise.
// Simba roars.

Посмотрите, насколько лучше выглядит код на ES6 по сравнению с ES5. И они делают одно и то же! Win!


Совет:


  • Используйте встроенный способ наследования – extends.

Нативные промисы


Переходим от callback hell к промисам (promises)


ES5
function printAfterTimeout(string, timeout, done){
  setTimeout(function(){
    done(string);
  }, timeout);
}

printAfterTimeout('Hello ', 2e3, function(result){
  console.log(result);

  // nested callback
  printAfterTimeout(result + 'Reader', 2e3, function(result){
    console.log(result);
  });
});

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


Все становится совсем плохо когда нужно добавить третий или четвертый коллбек. Давайте посмотрим, что можно сделать с промисами:


ES6
function printAfterTimeout(string, timeout){
  return new Promise((resolve, reject) => {
    setTimeout(function(){
      resolve(string);
    }, timeout);
  });
}

printAfterTimeout('Hello ', 2e3).then((result) => {
  console.log(result);
  return printAfterTimeout(result + 'Reader', 2e3);

}).then((result) => {
  console.log(result);
});

С помощью then можно обойтись без вложенных функций.


Стрелочные функции


В ES5 обычные определения функций не исчезли, но был добавлен новый формат – стрелочные функции.


В ES5 есть проблемы с this:


ES5
var _this = this; // need to hold a reference

$('.btn').click(function(event){
  _this.sendData(); // reference outer this
});

$('.input').on('change',function(event){
  this.sendData(); // reference outer this
}.bind(this)); // bind to outer this

Нужно использовать временный this чтобы ссылаться на него внутри функции или использовать bind. В ES6 можно просто использовать стрелочную функцию!


ES6
// this will reference the outer one
$('.btn').click((event) =>  this.sendData());

// implicit returns
const ids = [291, 288, 984];
const messages = ids.map(value => `ID is ${value}`);

For…of


От for переходим к forEach а потом к for...of:


ES5
// for
var array = ['a', 'b', 'c', 'd'];
for (var i = 0; i < array.length; i++) {
  var element = array[i];
  console.log(element);
}

// forEach
array.forEach(function (element) {
  console.log(element);
});

ES6 for…of позволяет использовать итераторы


ES6
// for ...of
const array = ['a', 'b', 'c', 'd'];
for (const element of array) {
    console.log(element);
}

Параметры по умолчанию


От проверки параметров переходим к параметрам по умолчанию. Вы делали что-нибудь такое раньше?


ES5
function point(x, y, isFlag){
  x = x || 0;
  y = y || -1;
  isFlag = isFlag || true;
  console.log(x,y, isFlag);
}

point(0, 0) // 0 -1 true 
point(0, 0, false) // 0 -1 true 
point(1) // 1 -1 true
point() // 0 -1 true

Скорее всего да. Это распространенный паттерн проверки наличия значения переменной. Но тут есть некоторые проблемы:


  • Строка 8, передаем 0, 0 получаем 0, -1
  • Строка 9, передаем false, но получаем true.

Если параметр по умолчанию это булева переменная или если задать значение 0, то ничего не получится. Почему? Расскажу после этого примера с ES6 ;)


В ES6 все получается лучше с меньшим количеством кода:


ES6
function point(x = 0, y = -1, isFlag = true){
  console.log(x,y, isFlag);
}

point(0, 0) // 0 0 true
point(0, 0, false) // 0 0 false
point(1) // 1 -1 true
point() // 0 -1 true

Получаем ожидаемый результат. Пример в ES5 не работал. Нужно проверять на undefined так как falsenullundefined и 0 – это все falsy-значения. С числами можно так:


ES5
function point(x, y, isFlag){
  x = x || 0;
  y = typeof(y) === 'undefined' ? -1 : y;
  isFlag = typeof(isFlag) === 'undefined' ? true : isFlag;
  console.log(x,y, isFlag);
}

point(0, 0) // 0 0 true
point(0, 0, false) // 0 0 false
point(1) // 1 -1 true
point() // 0 -1 true

С проверкой на undefined все работает как нужно.


Rest-параметры


От аргументов к rest-параметрам и операции spread.


В ES5 работать с переменным количеством аргументов неудобно.


ES5
function printf(format) {
  var params = [].slice.call(arguments, 1);
  console.log('params: ', params);
  console.log('format: ', format);
}

printf('%s %d %.2f', 'adrian', 321, Math.PI);

С rest  ... все намного проще.


ES6
function printf(format, ...params) {
  console.log('params: ', params);
  console.log('format: ', format);
}

printf('%s %d %.2f', 'adrian', 321, Math.PI);

Операция Spread


Переходим от apply() к spread. Опять же, ... спешит на помощь:


Помните: мы используем apply() чтобы превратить массив в список аргументов. например, Math.max() принимает список параметров, но если у нас есть массив, то можно использовать apply.

ES5
Math.max.apply(Math, [2,100,1,6,43]) // 100

В ES6 используем spread:


ES6
Math.max(...[2,100,1,6,43]) // 100

Мы также перешли от concat к spread'у:


ES5
var array1 = [2,100,1,6,43];
var array2 = ['a', 'b', 'c', 'd'];
var array3 = [false, true, null, undefined];

console.log(array1.concat(array2, array3));

В ES6:


ES6
const array1 = [2,100,1,6,43];
const array2 = ['a', 'b', 'c', 'd'];
const array3 = [false, true, null, undefined];

console.log([...array1, ...array2, ...array3]);

Заключение


JavaScript сильно изменился. Эта статья покрывает только базовые возможности, о которых должен знать каждый разработчик.

Поделиться с друзьями
-->

Комментарии (37)


  1. freetonik
    25.10.2016 12:44
    -7

    Кстати, наши онлайн-курсы учат новому стандарту с самого начала, с «основ программирования» :-)


    1. novoxudonoser
      25.10.2016 16:22
      -3

      А я долго гадал думал, зачем в таком большом количестве вы выбрасываете переводы так себе статей (хотя некоторые интересны). Теперь то понятно — пиарить совой проект обучения web проганью, вот только этих сервисов развелось как грязи в последнее время, чему то там конечно обучают, но только минимальным основам и так себе (Кто умеет, тот делает; кто не умеет, тот учит — пословица такая). В итоге на итак перегретый рынок it разработки выплёскиваются люди прошедшие пару курсов с завышенным чсв и требованием 1000$ в месяц и красной ковровой дорожки чтобы просто прийти на собеседование (к сожалению это гиперболизированная но всё же реальность). Вообщем печалька.


      1. Ofigenen
        25.10.2016 17:07
        +2

        Пусть выплескиваются. За минувший год у меня на собеседовании были десятка таких «талантов» с уровнем зарплатных ожиданий от *совсем мало* (студент на парт-тайм) до *офигеть сколько* (вуз закончил или даже не начинал) тысяч. Одному, пришедшему с претензией на senior, предложили junior-позицию за вменяемую сумму. Согласился, до сих пор с нами работает.
        А изначальную позицию закрыли опытным и сильным девелопером. Стоит он дорого, но такие люди на контрасте с общей массой начинают еще больше цениться.


  1. khayr
    25.10.2016 13:29
    -3

    А как насчет поддержки ES6 устаревшими браузерами? Тот же IE9, который является последней версией для Висты (которая до сих является поддерживаемым ОС), сможет выполнить код? Часто заказчики вообще требуют поддержки IE6.


    1. Alvaro
      25.10.2016 13:38
      +4

      Ладно IE6 или IE9. Многое из описанного не поддерживается и в IE11 о чем почему-то умолчали в статье


      1. menelion_elensule
        25.10.2016 14:04
        +1

        А что-то таки поддерживается уже в IE 11?


        1. Alvaro
          25.10.2016 14:58

          let, const. Таблица


    1. balamyt92
      25.10.2016 14:24
      +1

      Вам помогут полифилы.


    1. k12th
      25.10.2016 15:09

      Полифиллы, транспиляция.


    1. ts_hark
      25.10.2016 15:17

      https://babeljs.io/


    1. AlexanderBlago
      25.10.2016 15:17
      +9

      Часто заказчики вообще требуют поддержки IE6.

      вокруг вас там черти с вилами не бегают?


  1. Sirion
    25.10.2016 13:46
    +3

    Слава Богу, наконец-то кто-то написал псто про новые фичи в ES6.


    1. AndreyRubankov
      25.10.2016 18:25
      +4

      25 октября 2016
      Слава Богу, наконец-то кто-то написал просто про новые фичи в ES2015.


      1. Keyten
        25.10.2016 22:15
        +1

        псто !== просто


  1. iShatokhin
    25.10.2016 14:24

    забудьте var, используйте let и const.

    Между тем, node.js team пока старается избегать их в местах, критичным к скорости выполнения (например, в циклах). https://github.com/nodejs/node/pull/8873 и https://gist.github.com/Salakar/6d7b84f7adf1f3bc62a754752a6e5d0e

    Со времен JS-движки оптимизируют под новый стандарт.


    1. Zibx
      25.10.2016 15:51
      +2

      Но как? В байткоде let плодит замыкание, у него нет других вариантов кроме как сделать замыкание, а это не бесплатная вещь. Фактически такие статьи сейчас предлагают завернуть любой цикл таким образом:

      for(var i = 0; i < length; i++)
        (function(i){ 
          тело
        })(i);
      

      Это несомненно решает проблему когда в теле оказывается ассинхронный код, единственное что могут изобрести js-движки — jit оптимизацию, которая проверит что код синхронен и тогда заменит let на var (логику поведения). Но что если мне действительно надо в ассинхронном коде использовать последнее значение i?
      ES6 принёс очень много загадочных мест.


    1. Klimashkin
      26.10.2016 09:27

      Да, над этим работают
      https://docs.google.com/document/d/1EA9EbfnydAmmU_lM8R_uEMQ-U_v4l9zulePSBkeYWmY


  1. ROBsoer
    25.10.2016 14:29
    +4

    переменная "протекает" в другие блоки кода

    протекает наружу

    Всега было "поднимается" ну или "всплыввает", зачем вводить новое понятие да еще и такое запутанное.
    Все же четко: объявление переменной "поднимается" в начало области видимости (функции). И не нужно объяснять почему переменная настолько вредная, что "проникает" в другие блоки


    1. alek0585
      25.10.2016 23:08

      Видимо чтобы вызвать ассоциацию с тем, что протекает. По трубам.


  1. Timoschenko
    25.10.2016 17:35

    В целом, очень даже ничего.
    Некоторые фишки очень даже понравились.Особенно «class, constructor».
    Но вот это как-то не очень:

    ES6
    function settings() {
      return { display: { color: 'red' }, keyboard: { layout: 'querty'} };
    }
    
    const { display: { color: displayColor }, keyboard: { layout: keyboardLayout }} = settings();
    
    console.log(displayColor, keyboardLayout); // red querty
    


    1. Source
      25.10.2016 19:34

      Это, на мой взгляд, просто пример неуместного использования деструктуринга, строка получилась очень длинной и плохо читаемой, при этом вариант settings.display.color гораздо читабельнее в данном случае.
      Но это не потому что деструктуринг плох, а потому что надо в меру фичи использовать.
      То же можно сказать и про [...array1, ...array2, ...array3]. Лучше бы + починили (ну или какой-нибудь ++ добавили), если concat не нравится.


    1. SPAHI4
      26.10.2016 09:27
      -1

      Да вроде нормально, если отформатировать по строкам


  1. sand14
    26.10.2016 09:27
    -1

    Удивительно дело.

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

    В C++ изначально было сделано по уму — friend-функции
    А вот дальше пошло хуже, но хоть как-то развивалось:

    • в Delphi «протекали» private-поля внутри модуля, потом протечки залепили с помощью strict protected;
    • в Java protected-поля протекают внутри пакета,
    • но потом появился ее близнец C#, в котором protected не протекает, а для контролируемых протечек внутри сборки придумали internal.

    На мой взгляд, когда private var виден наружу, это именно «протечка», лучше называть вещи своими именами.
    Ок, придумали, let и const теперь. Неясно, почему это нельзя было придумать сразу, с учетом опыта других языков.

    Тем более путаница ключевых слов — если с const все понятно, то разницу между var и let нужно держать в голове, или гуглить. Опять же, неужели опыт других языков не учит, где тоже полно схожих ключевых слов с разной семантикой.


    1. justboris
      26.10.2016 12:21

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

      Javascript появился в 1995 году, опыта других языков типа Java и тем более C# еще не было.


      1. sand14
        26.10.2016 14:58

        Здесь было немного сарказма про «модные веб-языки», но тем не менее:

        1. в 1995-м появился LiveScript, а не JavaScript
        2. Сколько раз его можно было переделать, пока браузеры не стали массово исполнять JS как стандарт де факто?
        3. в 1995-м уже давно был C++, и вовсю была Delphi, было на что ориентироваться
        4. Java вышла в 1995-96-м

        Так что по ходу развития JS было на что ориентироваться.
        Поэтому и неясно, почему такие детские болезни, как var в JS, дожили аж до 2016.


        1. justboris
          26.10.2016 15:09

          Ну когда смогли, тогда и сделали.


          Лучше поздно, чем никогда, правда?


          1. sand14
            26.10.2016 15:24

            Все верно, лучше поздно, чем никогда.

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

            А теперь если что-то и исправляется, то преподносится как откровение (а в индустрии в целом это уж 10-20-30 лет как решено).

            Проблема в том, что веб давно стал очень актуальным, и дальше — больше (но — со всеми своими детскими болезнями), и было бы интересно понять причины, предпосылки сложившегося состояния дел именно в веб-отрасли.

            А так, считаю, что и традиционным языкам есть чему получиться у того же JS (например, доступ к элементами класса как словарю «ключ — значение» — по сути, это рефлексия, встроенная в язык, а не в библиотеки платформы).


            1. justboris
              26.10.2016 15:31

              Ответ на первой картинке в посте.


              С 2000 года почти 10 лет никто языком не занимался вообще, браузеры реализовывали функциональность кто во что горазд.
              Потом понадобилось некоторое время, чтобы разобраться с проблемами и прийти к какому-то удовлетворительному решению.


              1. taujavarob
                27.10.2016 20:57

                >С 2000 года почти 10 лет никто языком не занимался вообще

                Была попытка неудавшаяся протащить ООП в JavaScript в ES4.

                Так что работа велась. ;-)


  1. fijj
    26.10.2016 13:00

    После первого примера дальше даже читать не стал. Область видимости для var — функция(она же объект). Почему вернуло undefined? Да потому что переменная объявляется внутри функции независимо от наличия условия, оно влияет только на присвоение значения этой переменной.


  1. denisei
    01.11.2016 11:29

    В примере с классами для ES5 не слишком ли усложнена обертка? Можно ведь и так написать:

    var Animal = function(name){
      this.name = name;
      this.speak = function(){
        console.log(this.name + ' makes a noise.');
      }
    }
    


    1. Sirion
      01.11.2016 12:46

      Это не одно и то же. Там метод вынесен в прототип.


      1. denisei
        02.11.2016 06:13

        Ок, просьба не пинать больно, а что нам дает замыкание в функцию по сравнению с такой записью?

        var Animal = function(name){
          this.name = name;  
        }
        Animal.prototype.speak = function(){
            console.log(this.name + ' makes a noise.');
        }
        


        1. Sirion
          02.11.2016 08:08

          А вот на этот вопрос у меня нет хорошего ответа.


          1. denisei
            02.11.2016 11:35

            Вроде бы на примере с наследованием становится ясно что с классами проще, но опять же

            var lion = Object.create(new Animal('Simba'))
            lion.speak = function(){
            	Animal.prototype.speak.call(this);
            	console.log(this.name + ' roars.');
            }
            
            lion.speak(); // Simba makes a noise.
            

            Тоже нормально читается. В общем не отпускает чувство что пример нарочно усложнен для контраста, «как все клево стало».


        1. justboris
          02.11.2016 11:50
          +1

          В вашем первом примере на каждый новый экземпляр Animal будет создан свой инстанс метода speak.


          Если вынести метод в прототип, то он будет один на все экземпляры. В теории, это сэкономит вам немного памяти, если объектов много.


          1. Sirion
            02.11.2016 13:04

            В той записи, которую вы прокомментировали, метод вынесен в прототип.