Reflow란?

브라우저에서 DOM의 위치와 크기를 계산하는 일

 

Layout Thrashing이란?

Reflow는 시간이 오래 걸리는 무거운 작업이므로 브라우저 렌더링 엔진은 Reflow가 발생할 때마다 실행하지 않고 큐에 모아서 어느 순간에 한꺼번에 batch로 처리하는데, getClientBoundingRect(), scrollTop과 같은 함수들이 호출되면 최신 스타일을 반영해서 돌려주기 위해 그동안 모아놨던 큐를 비우면서 전부 처리한다. 브라우저가 성능 향상을 위해 기껏 reflow 발생시키는 애들을 큐에 모아놨는데, 자꾸 scrollTop, innerWidth 등을 호출해버리면 reflow가 그만큼 매번, 자주 일어나게 되어 성능 저하로 이어진다. 왜 저런 프로퍼티, 메소드가 DOM을 변화시키는 것도 아닌데 왜 reflow를 발생시킨다고 하는지 궁금했는데 해결되었다. 

나중에 다시 정리할 것

Debounced async function returns no promise
Debounce return undefined if func return promise
DEBOUNCING AN API CALL WITH PROMISE CHAINING

자바스크립트에는 배열이나 객체를 순회할 수 있는 여러 가지 메소드가 존재한다. 

1. for

사용법

for (var i = 0; i < 반복횟수; i++) {

 ...

}

기능

배열을 순회할 때 사용할 수 있다.

여느 언어의 for문과 마찬가지로 break, continue를 이용해 원하는 조건을 만났을 때 빠져나올 수 있다.

예제

var fruits = ['apple', 'banana', 'carrot', 'mango', 'kiwi'];

for(var i=0; i<fruits.length; i++) {
    if (fruits[i] === 'carrot') {
        console.log('carrot is not a fruit');
        break;
    }
    else {
        console.log(fruits[i], 'is a fruit.');
    }
}

/** 실행결과
apple is a fruit.
banana is a fruit.
carrot is not a fruit. (브레이크됨)
 */

 

 

 

2. forEach

사용법

배열.forEach(function callback(element, index) => {

...

});

기능

배열을 순회해서 하나씩 개체를 꺼내 무언가 일을 수행해야 할 때 사용한다. 배열에서 element를 하나씩 가져와서 콜백함수를 실행한다.

forEach는 무조건 순회를 완료한다. 중간에 빠져나올 수 없다. 'carrot'읆 만나면 return false를 써서 break를 걸려고 해 봤으나 작동하지 않고 kiwi까지 순회했음을 알 수 있다.

예제

    var fruits = ['apple', 'banana', 'carrot', 'mango', 'kiwi'];

    fruits.forEach(f => {
        if (f === 'carrot') {
            console.log('carrot is not a fruit.');
            return false;  // 작동하지 않는다.
        }
        else {
            console.log(f, 'is a fruit.');
            return true;
        }
    });
    
/** 실행결과
apple is a fruit.
banana is a fruit.
carrot is not a fruit.
mango is a fruit.
kiwi is a fruit.
 */

 

 

 

3. map

사용법

배열.map(function callback(element, index) => {

...

}

기능

배열을 순회해서 새로운 배열을 만들어낼 때 사용한다. 원래 배열의 개체들을 하나씩 순회해서 일대일로 결과물을 만들어내는 메소드이기 때문에 애초에 break나 continue가 존재하지 않는다.

예제

    var fruits = ['apple', 'banana', 'carrot', 'mango', 'kiwi'];

    var fruits_sentences = fruits.map(f => {
        if (f === 'carrot') return f + ' is not a fruit.';
        else return f + ' is a fruit.';
    });

    console.log(fruits_sentences);

    /**
     * 
     * [
        'apple is a fruit.',
        'banana is a fruit.',
        'carrot is not a fruit.',
        'mango is a fruit.',
        'kiwi is a fruit.'
        ]
     */

 

 

 

4. some

사용법

배열.some(function callback(element, index) {
  if (찾고자 하는 조건) return true; // 원하는 조건의 개체가 있다면 순회 종료
  else return false; // 없어도 되는 문장
}

기능

배열에 원하는 조건의 개체가 하나라도 존재하는지 찾는다. 콜백함수 내에서 true가 반환되면 some 순회가 종료된다.

예제

    var fruits = ['apple', 'banana', 'carrot', 'mango', 'kiwi'];

    var isExistCarrot = fruits.some(f => {
        if (f === 'carrot') return true;
    });

    console.log('Does a carrot exist?', isExistCarrot);

    // 실행결과
    // Does a carrot exist? true

 

 

 

5. every

사용법

배열.every(function callback(element, index) {
  if (!모두 만족해야 하는 조건) return false;
  else return true;
}

기능

배열의 모든 개체가 조건을 만족하는 지 검사한다. 콜백함수가 false를 리턴하면 조건을 충족시키지 않는다고 판단하고 순회를 종료한다.

예제

    var fruits = ['apple', 'banana', 'carrot', 'mango', 'kiwi'];

    var isAllFruit = fruits.every(f => {
        if (f === 'carrot') return false;
    });

    console.log('Is all a fruit?', isAllFruit);

	// 실행결과
    // Is all a fruit? false

 

 

 

6. filter

사용법

var 결과물 = 배열.filter(function callback(element, index) {
  if (찾고자 하는 조건) return true;
  else return false;
}

기능

조건을 통과하는 개체로만 이루어진 새로운 배열을 만들어낼 때 사용한다. 콜백이 true를 반환하면 해당 개체가 새로운 배열에 추가되고, 아니면 무시된다.

예제

    var fruits = ['apple', 'banana', 'carrot', 'mango', 'kiwi'];

    var onlyFruits = fruits.filter(f => {
        if (f !== 'carrot') return true;
        else return false;
    });

    console.log(onlyFruits);

    // 실행결과
    // [ 'apple', 'banana', 'mango', 'kiwi' ]

 

 

 

7. reduce

사용법

var 결과 = 배열.reduce(function callback(누적값, 현재값) {
  if (조건에 맞으면) return 누적값 + 현재값;
  else return 누적값; // return 누적값 + 0;
}, 초깃값)

기능

배열을 순회해서 원하는 조건을 충족시키는 개체로만 무언가 누적해서 결과를 만들어낼 때 사용한다. 덧셈이라던가...

예제

    var numbers = [1, 2, 3, 4, 5];

    var sumOfOneTwoThreeFour = numbers.reduce((prev, curr) => {
        if (curr < 5) return prev + curr;
        else return prev;
    }, 0);

    console.log(sumOfOneTwoThreeFour);

    // 실행결과
    // 10
for (var i = 0; i < 5; i++) {
  (function(j) {
    $('#target' + j).on('click', function() {
      alert(j);
    });
  })(i);
}

 

클로저로 for문으로 이벤트리스너를 연결하는 방법은 위와 같다고 한다. 

패턴은 그렇지만 이유를 알아보자.

 

2~6번째줄의 즉시실행함수는 한번 실행 후 다시 부를 방법이 없다(끝이 난다).

즉, i가 j로 대입되고 한번 실행된 후 바로 바깥쪽 for문의 i와의 연결고리는 끊기게 된다.

(function () {

 var j = i;

 ...

})();

와 같다고 보면 좀더 이해하기 쉬울 것이다. (j는 고정값이 된다는 뜻)

 

3번째줄 이벤트리스너 안쪽의 function ()은

for문 안에서 매번 선언될 때 변수에 대한 scope를 갖게 되는데 (선언될때=렉시컬스코프)

바로 바깥쪽 function (j)의 스코프를 상위 스코프 체인으로 계속 갖고 있게 되므로 

고정된 j 값을 가질 수 있게 된다.

j는 이벤트 리스너 안의 function () 의 비공개변수가 되는 것이다.

달리 말해 이벤트리스너 안의 function ()의 클로저가 생성되었다고 할 수 있다.

 

for (var i = 0; i < 5; i++) {
  var makeEventListener = function() {
  	var j = i;
	return function () {
    	$('#target' + j).on('click', function() {
      		alert(j);
    	});
    }
  };
  var _eventListener = makeEventListener();
  _eventListener();
}

같은 일을 하는 함수인데, 이렇게 보면 이벤트리스너 안에 있는 function () 의 클로저가 어떻게 생기고

비공개변수를 스코프에 가지고 간다는 것을 좀더 명확하게 볼 수 있다.

<생성자>
다른 언어에서의 class
대문자로 시작

function Calc(first, second) {
  this.first = first;
  this.second = second;
  this.add = function () {
    return this.first + this.second;
  }
}

this.add를 생성자 함수 안에 넣는 대신에 이렇게 할 수도 있다.

Calc.prototype.add = function () {
  return this.first + this.second;
}

위처럼 prototype으로 공통된 함수를 정의하면 메모리를 아낄 수 있다.
prototype은 생성자 함수에 사용자가 직접 넣는 것이고, __proto__는 new를 호출 할 때 prototype을 참조해 자동으로 만들어지는 객체

이벤트버블링이란?

- 하위 DOM에 연결된 이벤트가 상위로 전파되는 것.

 

이벤트캡쳐란?

- 상위 DOM에 연결된 이벤트를 하위 DOM으로 전파시키는 것. addEventListener를 할 때 옵션객체에서 capture: true로 지정해서 수행한다.

 

이벤트위임(Delegation) 이란?

- 상위 DOM에서 하위 DOM의 이벤트를 제어하도록 위임하는 것. 이렇게 하면 DOM마다 이벤트를 연결하지 않아도 되서 코드 양과 이벤트리스너마다 할당될 메모리 사용량이 줄어들고, 버튼 등의 DOM의 추가와 삭제가 유연해진다.

 

event.stopPropagation()

- 상위나 하위 DOM으로의 이벤트 전파(버블링 or 캡쳐)을 막는다.


event.stopImmediatePropagation()

- 같은 DOM의 다른 이벤트들의 발생을 막는다.

 

event.preventDefault()

- 태그의 기본 이벤트 수행을 막는다. ex) a 태그의 href 이동 기능을 preventDefault로 방지

 

return false; (jQuery에서)

- event.stopPropagation + event.preventDefault (원하는 DOM의 한 이벤트만 동작시키고 싶을 때)

아래 코드는 간단하게 numbers 배열을 순회하면서 출력하는 코드이다. for - of 문을 사용하면 간결하게 쓸 수 있다.

const numbers = [10, 20, 30, 40, 50];

for (let i = 0; i < numbers.length; i++) {
  console.log(numbers[i]);
}

// output:
// 10
// 20
// 30
// 40
// 50

 

 

아래 코드는 위 코드를 for - of를 사용해 간결하게 바꾼 코드이다. 파이썬의 for와 비슷하다.

 

 

const numbers = [10, 20, 30, 40, 50];

for (let number in numbers) {
  console.log(number);
}

// output:
// 10
// 20
// 30
// 40
// 50

 

 

그렇다면 for - in 은 무엇일까? 바로 오브젝트에서 key를 가져오는 것이다. 

line 7~9의 console.log는 doggy 오브젝트에서 키, 밸류, 키와 밸류 모두를 가져오는 다양한 방법을 보여준다.

그리고 가장 아래의 for - in 문은 for - in이 오브젝트에서 key 값을 가져온다는 것을 보여주기 위한 예시이다.

const doggy = {
  name: "멍멍이",
  sound: "멍멍",
  age: 2
};

console.log(Object.entries(doggy));  // dict.items() in python
console.log(Object.keys(doggy));     // dict.keys() in python
console.log(Object.values(doggy));   // dict.values() in python

// output:
// [Array[2], Array[2], Array[2]] <- iterate할 수 있는 배열
// ["name", "sound", "age"]
// ["멍멍이", "멍멍", 2]

for (let key in doggy) {
  console.log(`${key}: ${doggy[key]}`);
}

// output:
// name: 멍멍이
// sound: 멍멍
// age: 2

array라는 배열이 있고, 이 배열 안에 있는 모든 값들의 제곱을 얻어서 squared라는 배열에 저장하려고 한다.

이럴 때 여러 가지 방법이 있다.

 

1. for문

const array = [1, 2, 3, 4, 5, 6, 7, 8, 9];

const squared = [];
for (let i = 0; i < array.length; i++) {
  squared.push(array[i] * array[i]);
}

 

2. forEach문

- forEach는 매개변수에 함수를 넣어서, 배열에서 하나씩 꺼내 함수에 집어넣는다. forEach의 리턴 값은 undefined이므로, 함수 실행 결과를 새로운 배열에 저장하고 싶으면 squared.push()처럼 함수 내에서 넣어주어야 한다.

- 주석 처리한 부분은 화살표 함수로 쓴 것

const array = [1, 2, 3, 4, 5, 6, 7, 8, 9];

const squared = [];

array.forEach(function(n) {
  squared.push(n * n);
});

/*
array.forEach(n => {
  squared.push(n * n);
});
*/

 

3. map

- map은 배열에서 하나씩 꺼내서 괄호 안에 있는 함수에 집어넣은 결과를 리턴한다.

- 아래 코드처럼 화살표 함수를 사용해 square 함수를 정의한 후, map에 square 함수를 콜백으로 넣어줄 수도 있고,

- 간결하게 쓰려면 주석처럼 square 함수를 따로 정의 안 하고 바로 map 안에 넣어줄 수도 있다.

const array = [1, 2, 3, 4, 5, 6, 7, 8, 9];

const square = n => n * n;
const squared = array.map(square);

// const squared = array.map(n => n * n);

+ Recent posts