. . .

Конструкция IIFE и директива let

10 Сентябрь 2019

Ранее в предыдущих статьях, например в статье о создании аккордеона я упоминал конструкцию Immediately-Invoked Function Expression, или немедленно вызываемой функции. Ее применение было необходимо для того, чтобы цикл for хранил нужное значение. В данной ситуации необходимость этой конструкции отпадает в связи с возможностями, которые дает стандарт ES-2015, а именно благодаря объявлению переменной через let.

ES-2015 разделил объявления переменных и констант на ключевые слова let и const соответственно.

Рассмотрим этот вопрос предметно на базе того же аккордеона. Сначала я опишу предыдущий способ, а затем решу эту же задачу не прибегая к IIFE.

<div class="tab-panel">
    <div class="tab-header header-active">Button 1</div>
    <div class="tab-text text-active">Some text under button 1</div>
    <div class="tab-header">Button 2</div>
    <div class="tab-text">Some text under button 2</div>
    <div class="tab-header">Button 3</div>
    <div class="tab-text">Some text under button 3</div>
</div>

CSS верстка:


.tab-panel {
    width: 80%;
    margin-left: 10%;
    margin-top: 100px;
}
.tab-header {
    font-size: 18px;
    font-weight: 900;
    font-family: Calibri;
    color: darkred;
    background-color: #999;
    padding: 15px;
    transition: .5s ease;
    margin-top: 5px;
    cursor: pointer;
}
.header-active {
    background-color: #777;
    padding: 15px 25px;
}
.tab-text {
    padding: 15px;
    border: 2px solid #999;
    border-top: none;
    font-family: Calibri;
    font-size: 16px;
    line-height: 1.3;
    transition: .5s ease;
    display: none;
}
.text-active {
    display: block;
}

JS с использованием IIFE:


var header = document.getElementsByClassName('tab-header');
var text = document.getElementsByClassName('tab-text');
for (var i=0; i<text.length; i++) {
  header[i].onclick = (function (a) {
    return function () {
      if (! header[a].classList.contains('header-active')) {
	    header[a].classList.add('header-active');
	    text[a].classList.add('text-active');
	    for (var x=0; x<a; x++) {
		  header[x].classList.remove('header-active');
		  text[x].classList.remove('text-active');
	    }
	    for (var y=a+1; y<text.length; y++) {
		  header[y].classList.remove('header-active');
		  text[y].classList.remove('text-active');
	    }
	  }
    }
  })(i);
}

А теперь мы напишем скрипт без применения IIFE, с синтаксисом ES2015:


const header = document.getElementsByClassName('tab-header');
const text = document.getElementsByClassName('tab-text');
for (let i=0; i<text.length; i++) {
  header[i].onclick = function () {
	if (! header[i].classList.contains('header-active')) {
	  header[i].classList.add('header-active');
	  text[i].classList.add('text-active');
	  for (var x=0; x<i; x++) {
		header[x].classList.remove('header-active');
		text[x].classList.remove('text-active');
	  }
	  for (var y=i+1; y<text.length; y++) {
		header[y].classList.remove('header-active');
		text[y].classList.remove('text-active');
	  }
	}
  }
}

Рабочий пример этого метода:

Button 1
Some text under button 1
Button 2
Some text under button 2
Button 3
Some text under button 3

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