global object
global object는 코드가 실행되기 이전 단계에 자바스크립트 엔젠에 의해 어떤 객체보다도 먼저 생성되는 특수한 객체
- Node.js 환경에서는 global이 전역 객체이고, 브라우저 환경에서는 window가 전역 객체이다.
- 전역 객체는 계층 구조 상 어떤 객체에도 속하지 않는 최상위 객체이다. (프로토타입 상속의 의미 X)

 

built in global property

전역 프로퍼티는 global을 생략하고 참조할 수 있다.

1.Infinity : 무한대를 나타내는 숫자 값

console.log(global.Infinity === Infinity);  //true
console.log(10/0);            //Infinity
console.log(-10/0);           //-Infinity
console.log(typeof Infinity); // number
 

2. NaN : 숫자가 아님(Not a Number)을 나타내는 숫자 값

=> Number.NaN 프로퍼티와 같다.
console.log(global.NaN);        //NaN
console.log(Number('abc'));     //NaN
console.log(10 * 'abc');        //NaN
console.log(typeof NaN);        //number

 

3. undefinded : 원시 타입 undefined

console.log(global.undefinded);
let nothing;
console.log(nothing);
console.log(typeof undefined);      //undefined

 

built in global function

1.isFinite

 전달 받은 인수가 정상적인 유한수인지 검사하여 유한수이면 true를 반환하고, 무한수이면 false를 반환

 전달 받은 인수가 숫자가 아닌 경우 숫자로 타입 변환 후 검사를 수행하며, NaN으로 평가되는 값이면 false를 반환
console.log(isFinite(10));              // true
console.log(isFinite('10'));            // true  형변환
console.log(isFinite(null));            // true  형변환

console.log(isFinite(Infinity));        // false
console.log(isFinite(-Infinity));       // false

console.log(isFinite(NaN));             // false
console.log(isFinite('abc'));           // false

 

2.isNaN

전달 받은 인수가 NaN인지 검사하여 그 검사 결과를 불리언 타입으로 반환
전달 받은 인수가 숫자가 아닌 경우 숫자로 타입 변환 후 검사를 수행
console.log(isNaN(NaN));                // true
console.log(isNaN(10));                 // false

console.log(isNaN('abc'));              // true
console.log(isNaN('10'));               // false
console.log(isNaN(''));                 // false ('' => 0)

console.log(isNaN(true));               // false (true => 1)
console.log(isNaN(false));              // false (false => 0)

console.log(isNaN(undefined));          // true 

console.log(isNaN({}));                 // true

 

3.parseFloat : 전달받은 문자열 인수를 부동 소수점 숫자, 실수로 해석하여 반환

console.log(parseFloat('10.01'));       // 10.01
console.log(parseFloat('10'));          // 10
// 공백으로 구분 된 문자열은 첫 번째 문자열만 변환
console.log(parseFloat('10 20 30'));    // 10
// 숫자가 아닌 문자열은 제외하고 변환
console.log(parseFloat('10cm'));        // 10
// 첫 번째 문자열을 숫자로 변환할 수 없다면 변환 불가
console.log(parseFloat('GS25'));        // NaN
// 앞뒤 공백은 무시
console.log(parseFloat('  1004  '));    // 1004

 

4. parseInt : 전달받은 문자열 인수를 정수로 해석하여 반환

console.log(parseInt('10'));            // 10
console.log(parseInt('10.01'));         // 10

 

5.encodeURI

 URI(인터넷에 있는 자원을 나타내는 유일한 주소)를 문자열로 전달받아 이스케이프 처리를 위해 인코딩한다.
네트워크를 통해 정보를 공유할 때 어떤 시스템에서도 읽을 수 있는 아스키 문자 셋으로 변환
const uri = 'http://greedy.com?name=홍길동&job=student';
const enc = encodeURI(uri);         
console.log(enc);                   
// http://greedy.com?name=%ED%99%8D%EA%B8%B8%EB%8F%99&job=student

6.decodeURI

인코딩 된 URI를 인수로 전달 받아 이스케이프 처리 이전으로 디코딩한다.
 
const dec = decodeURI(enc);
console.log(dec);                  
// http://greedy.com?name=홍길동&job=student

7.encodeURIComponent

 URI 구성 요소를 인수로 전달 받아 알파벳, 0~9의 숫자, - _ . ! ~ * ' ( ) 문자는 제외하고 인코딩한다.
 쿼리 스트링 구분자로 사용 되는 =, ?, &까지 인코딩한다.
const uriComp = 'name=홍길동&job=student';
const encComp = encodeURIComponent(uriComp);
console.log(encComp);               // name%3D%ED%99%8D%EA%B8%B8%EB%8F%99%26job%3Dstudent

// decodeURIComponent
const decComp = decodeURIComponent(encComp);
console.log(decComp);               // name=홍길동&job=student

 

배열 메소드
// 배열의 생성자 함수는 Array
console.log(arr.constructor === Array);   // true

// 배열의 프로토타입 객체는 Array.prototype -> 이 안에 constructor가 있고 arr는 Array.prototype을 상속한다.
console.log(Object.getPrototypeOf(arr) === Array.prototype);  // true
// => Array.prototype은 배열을 위한 빌트인 메서드를 제공한다.

 

Array.prototype.indexOf
  •  indexOf : 배열에서 요소가 위치한 인덱스를 리턴
  •  lastIndexOf : 배열의 요소가 위치한 마지막 인덱스를 리턴
  •  includes : 배열에 해당 요소 포함 여부 리턴
const foodList = ['물회', '삼계탕', '냉면', '수박', '물회'];

console.log(`foodList.indexOf('물회') : ${foodList.indexOf('물회')}`);            // 0
console.log(`foodList.indexOf('물회', 1) : ${foodList.indexOf('물회', 1)}`);      // 1번 인덱스부터 시작 4
console.log(`foodList.indexOf('삼겹살') :  ${foodList.indexOf('삼겹살')}`);             
// -1  인덱스로서 존재할 수 없는 숫자로 '찾지 못했다'는 걸 의미

console.log(`foodList.lastIndexOf('물회') : ${foodList.lastIndexOf('물회')}`);       // 4
console.log(`foodList.lastIndexOf('물회', 1) : ${foodList.lastIndexOf('물회', 1)}`);   
// 0  1번인덱스부터 0번쪽으로 읽는다
console.log(`foodList.lastIndexOf('삼겹살') : ${foodList.lastIndexOf('삼겹살')}`);   // -1

console.log(`foodList.includes('물회') : ${foodList.includes('물회')}`);             // true
console.log(`foodList.includes('삼겹살') : ${foodList.includes('삼겹살')}`);         // false

 

 
Array.prototype.push : 배열의 맨 뒤에 요소 추가
Array.prototype.pop : 배열의 맨 뒤에 요소 제거
const chineseFood= ['짜장면', '짬뽕', '우동'];

console.log(`push 전 chineseFood : ${chineseFood}`);            // 짜장면,짬뽕,우동

chineseFood.push('탕수육');
chineseFood.push('양장피');
// chineseFood.push('탕수육', '양장피');

console.log(`push 후 arr :  ${chineseFood}`);                   // 짜장면,짬뽕,우동,탕수육,양장피

console.log(`chineseFood.pop() : ${chineseFood.pop()}`);        // 양장피
console.log(`chineseFood.pop() : ${chineseFood.pop()}`);        // 탕수육
console.log(`chineseFood.pop() : ${chineseFood.pop()}`);        // 우동

console.log(`pop 후 chineseFood :  ${chineseFood}`);            // 짜장면,짬뽕

 

 
Array.prototype.unshift : 배열의 맨 앞에 요소 추가
Array.prototype.shift : 배열의 맨 앞 요소 제거 후 그 요소를 반환
const chickenList = ['양념치킨', '후라이드', '파닭'];

console.log(`unshift 전 chickenList : ${chickenList}`);         // 양념치킨,후라이드,파닭

chickenList.unshift('간장치킨');
chickenList.unshift('마늘치킨');
// chickenList.unshift('간장치킨', '마늘치킨'); -> 이 경우 이 덩어리 그대로 앞에 추가됨

console.log(`unshift 후 chickenList : ${chickenList}`);         // 마늘치킨,간장치킨,양념치킨,후라이드,파닭

console.log(`chickenList.shift() : ${chickenList.shift()}`);    // 마늘치킨
console.log(`chickenList.shift() : ${chickenList.shift()}`);    // 간장치킨
console.log(`chickenList.shift() : ${chickenList.shift()}`);    // 양념치킨

console.log(`shift 후 chickenList : ${chickenList}`);           // 후라이드,파닭

 

Array.prototype.concat : 두 개 이상의 배열을 결합

const idol1 = ['아이브', '오마이걸'];
const idol2 = ['트와이스', '에스파'];
const idol3 = ['블랙핑크', '레드벨벳'];

// 배열명.concat(배열명1, 배열명2, ...)
const mix = idol1.concat(idol2);   // 나열된 순서대로 결합한다. 1+2
const mix2 = idol3.concat(idol1, idol2);  // 3+1+2

console.log(`idol1 기준으로 idol2 배열을 concat : ${mix}`);            // 아이브,오마이걸,트와이스,에스파
console.log(`idol3 기준으로 idol1, idol2 배열을 concat : ${mix2}`);    
// 블랙핑크,레드벨벳,아이브,오마이걸,트와이스,에스파

 

Array.prototype.slice : 배열의 요소 선택 잘라내기, 원본 배열은 변경하지 않는다.

Array.prototype.splice : 배열의 index 위치의 요소 제거 및 추가, 원본 배열을 변경한다.

const front = ['HTML', 'CSS', 'JavaScript', 'jQuery'];

// slice(시작인덱스, 종료인덱스->그 인덱스 앞에서 자른다)
console.log(`front.slice(1, 3) : ${front.slice(1, 3)}`);                     // CSS,JavaScript
console.log(`front : ${front}`);                                             // HTML,CSS,JavaScript,jQuery

// splice(index, 제거수, 추가값1, 추가값2, ...)
console.log(`front.splice(3, 1, "React") : ${front.splice(3, 1, "React")}`);    
//jQuery  3번인덱스부터 요소 1개를 제거하고 React를 추가한다.
console.log(`front : ${front}`);                                             // HTML,CSS,JavaScript,React

 

Array.prototype.join : 배열을 구분자로 결합하여 문자열로 반환

const snackList = ['사탕', '초콜렛', '껌', '과자'];
console.log(`snackList.join() : ${snackList.join()}`);         // 구분자를 안 쓰면 컴마->사탕,초콜렛,껌,과자
console.log(`snackList.join('/') : ${snackList.join('/')}`);   // 사탕/초콜렛/껌/과자

 

Array.prototype.reverse : : 배열의 순서를 뒤집는다. 원본도 바뀜.

console.log(`[1, 2, 3, 4, 5].reverse() : ${[1, 2, 3, 4, 5].reverse()}`);  
// 5,4,3,2,1

 

배열 고차 함수

고차 함수 : 함수를 인수로 전달받거나 함수를 반환하는 함수

Array.prototype.sort : 배열을 정렬 기준으로 정렬

et numbers = [];

for(let i = 0; i < 10; i++) {
    numbers[i] = Math.floor(Math.random() * 100 ) + 1;
}

//floor 로 내림처리

console.log(`정렬 전 numbers : ${numbers}`);
//정렬 전 numbers : 11,29,22,76,97,68,13,7,14,61

// 오름차순 정렬이 기본이며 정렬 후 정렬 순서를 유지한다. 
numbers.sort();
console.log(`정렬 후 numbers. : ${numbers}`);
//정렬 후 numbers. : 11,13,14,22,29,61,68,7,76,97

배열은 기본적으로 문자열 정렬이 되므로 한자리수, 세자리수가 올바르지 않게 정렬되는 모습을 보임
 => 다른 정렬 기준을 사용하려면 compare 함수를 인수로 전달해야 함

// 숫자 오름차순 정렬
function compare(a, b) {  
    if(a > b) return 1; // 양수면 위치를 바꾼다
    if(a == b) return 0;
    if(a < b) return -1;
}
//compare는 콜백함수 sort는 고차함수이다.
numbers.sort(compare);
console.log(numbers);
//출력
[
   7, 11, 13, 14, 22,
  29, 61, 68, 76, 97
]



// 숫자 내림차순 정렬
numbers.sort(function(a, b) { return b - a;});  
// b - a가 양수, 뒤에 자리가 더 크면 자리를 바꾼다 -> 내림차순
numbers.sort((a, b) => b - a);
console.log(numbers);

//출력
[
  97, 76, 68, 61, 29,
  22, 14, 13, 11,  7
]

 

Array.prototype.forEach : for를 대체할 수 있는 고차함수

배열.forEach(function(item, index, array){
        
    });

함수 몸체에 배열 요소 각각에 실행할 기능을 작성하며, 요소만 반복해도 된다.

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

numbers.forEach(function(item, index, array){
    console.log(`item : ${item}`);
    console.log(`item : ${index}`);
    console.log(`item : ${array}`);
}); //item : 요소 , array는 배열 전체

//출력

item : 1
item : 0
item : 1,2,3,4,5
item : 2
item : 1
item : 1,2,3,4,5
item : 3
item : 2
item : 1,2,3,4,5
item : 4
item : 3
item : 1,2,3,4,5
item : 5
item : 4
item : 1,2,3,4,5




// 각 요소 별로 * 10 한 값을 콘솔에 출력
numbers.forEach(item => console.log(item * 10));
//출력
10
20
30
40
50

 

Array.property.map : 배열 요소 전체를 대상으로 콜백 함수 호출 후 반환 값들로 구성된 새로운 배열 반환

배열.map(function(item, index, array){
        
    })

함수 몸체에 배열 요소 각각에 반환할 새로운 값을 작성한다.

const types = [true, 1, 'text'].map(item => typeof item); //item의 테이터 타입을 반환해라
console.log(types);
//[ 'boolean', 'number', 'string' ]

const lengths = ['apple', 'banana', 'cat', 'dog', 'egg'].map(item => item.length);
console.log(lengths);
//[ 5, 6, 3, 3, 3 ]

 

Array.prototype.filter : 배열 요소 전체를 대상으로 콜백 함수 호출 후 반환 값이 true인 요소로만 구성된 새로운 배열 반환

const odds = numbers.filter(item => item % 2); //2로 나누었을때 몫이 있는 요소 반환
console.log(odds);
//[ 1, 3, 5 ]

 

Array.prototype.reduce : 배열을 순회하며 각 요소에 대하여 이전의 콜백 함수 실행 반환값을 전달하여 콜백함수를 실행하고 그 결과를 반환

=>reduce 함수는 배열의 요소를 순차적으로 순회하면서 리듀서(reducer) 함수를 실행하고 하나의 결과값을 반환

arr.reduce((accumulator, currentValue, index, array) => {

}, initialValue);

- accumulator : 누산기 순회하면서 계속 더해져서 합쳐지는 값
- initialValue Optional : callback의 최초 호출에서 첫 번째 인수에 제공하는 값. 
초기값을 제공하지 않으면 배열의 첫 번째 요소가 previousValue로 선언되고 두 번째 요소가 currentValue로 선언된다.
- 리턴 값 : 누적 계산의 결과값
  •     previousValue : 이전 콜백의 반환 값
  •     currentValue : 배열 요소의 값
  •     currentIndex : 인덱스
  •     array : 메소드를 호출한 배열
numbers.reduce(function(previousValue, currentValue, currentIndex, array){
        console.log(`previousValue : ${previousValue}`);
        console.log(`currentValue : ${currentValue}`);
        console.log(`currentIndex : ${currentIndex}`);
        console.log(`array : ${array}`);
    })
    
    
    //출력
previousValue : 1
currentValue : 2
currentIndex : 1
array : 1,2,3,4,5
previousValue : undefined  //콜백을 한번만 해서?
currentValue : 3
currentIndex : 2
array : 1,2,3,4,5
previousValue : undefined
currentValue : 4
currentIndex : 3
array : 1,2,3,4,5
previousValue : undefined
currentValue : 5
currentIndex : 4
array : 1,2,3,4,5
// 합산
    const sum = numbers.reduce(function(previousValue, currentValue){
        return previousValue + currentValue;  //연산결과를 누산기, previousValue에 넣어 최종값을 얻는다.
    });
    console.log(`sum : ${sum}`)   // 1+2 = 3, 3+3 = 6 6+4 =10 10+5 =15


//최대값 취득
    const max = numbers.reduce(function(pre, cur) {
        return pre > cur ? pre : cur;   // 1 < 2 , 2 < 3, 3 < 4, 4 < 5
    });
    console.log(`max : ${max}`); //5

 

Array. prototype.some : 배열 내 일부 요소가 콜백 함수의 테스트를 통화하는지 확인하여 그 결과를 boolean으로 반환

// 배열 내 요소 중 10보다 큰 값이 1개 이상 존재하는지 확인
let result = [1, 5, 3, 2, 4].some(item => item > 10);
console.log(`result : ${result}`);  // false

// 배열 내 요소 중 3다 큰 값이 1개 이상 존재하는지 확인
 result = [1, 5, 3, 2, 4].some(item => item > 3);
console.log(`result : ${result}`);  //true

 

Array.prototype.every : 배열 내 모든 요소가 콜백 함수의 테스트를 통화하는지 확인하여 그 결과를 boolean으로 반환

// 배열 내 모든 값이 3보다 큰지 확인
result = [1, 5, 3, 2, 4].every(item => item > 3);
console.log(`result : ${result}`);  //false

// 배열 내 모든 값이 0보다 큰지 확인
result = [1, 5, 3, 2, 4].every(item => item > 0);
console.log(`result : ${result}`);  //ture

 

Arrays.prototype.find : 배열을 순회하며 각 요소에 대하여 인자로 주어진 콜백 함수를 실행하여 그 결과가 참인 첫번째 요소를 반환. 참인 요소가 존재하지 않다면 undefinded 반환

Arrays.prototype.findIndex : 배열을 순회하며 각 요소에 대하여 인자로 주어진 콜백 함수를 실행하여 그 결과가 참인 첫번째 요소의 인덱스를 반환. 참인 요소가 존재하지 않다면 -1 반환

 

const student = [
    { name : '유관순', score : 90},
    { name : '홍길동', score : 80},
    { name : '장보고', score : 70}
];

result = student.find(item => item.name == '유관순');
console.log(result);									//{ name: '유관순', score: 90 }
result = student.findIndex(item => item.name == '유관순');
console.log(result);									//0

result = student.find(item => item.name == '신사임당');
console.log(result);									//undefined
result = student.findIndex(item => item.name == '신사임당');
console.log(result);									//-1

// find, findIndex는 일치하는 요소를 찾으면 더 이상 탐색하지 않고 하나의 요소, 인덱스만 반환한다.

 

Array.property.filter : filter는 콜백함수의 실행 결과가 true인 배열 요소의 값만을 추출한 새로운 배열을 반환한다.

// 만약 80점 이상인 학생들만 찾고 싶다면?
result = student.filter(item => item.score >= 80);
console.log(result); 
//[ { name: '유관순', score: 90 }, { name: '홍길동', score: 80 } ]

result = student.find(item => item.score >= 80);
console.log(result);
// 첫 번째 요소인 유관순만 반환한다. { name: '유관순', score: 90 }

// 만약 80점 이상인 학생들만 찾고 싶다면?
result = student.filter(item => item.score >= 80);
console.log(result); // 유관순 하나만 반환한다.

result = student.find(item => item.score >= 80);
console.log(result);

배열의 정의
 
배열은 여러 개의 값을 순차적으로 나열한 자료구조이다.
 
 
배열의 생성
 
1. 배열의 리터럴을 통한 생성
 
배열이 가지고 있는 값을 요소라 부르며 자바스크립트의 모든 값은 배열의 요소가 될 수 있다.
const arr = ['바나나', '복숭아', '키위'];
console.log(arr);


//[ '바나나', '복숭아', '키위' ]

2. 배열 생성자 함수

const arr2 = new Array();
console.log(arr2);         //[]

전달된 인수가 1개이고 숫자인경우 length 프로퍼티 값이 인수인 배열을 생성한다.

const arr3 = new Array(10);
console.log(arr3);         //[ <10 empty items> ]

전달된 인수가 2개 이상이거나 숫자가 아닌 경우 인수를 요소로 갖는 배열을 생성한다.

const arr4 = new Array(1, 2, 3);
console.log(arr4);                //[ 1, 2, 3 ]

 

3. Array.of 메소드

전달된 인수를 요소로 갖는 배열을 생성한다.

console.log(Array.of(10));				//[ 10 ]
console.log(Array.of(1, 2, 3));			//[ 1, 2, 3 ]
console.log(Array.of("hello", "js"));	//[ 'hello', 'js' ]

배열은 요소의 개수, 즉 배열의 길이를 나타내는 length 프로퍼티를 갖는다.

console.log(arr.length);   //3

배열의 요소는 자신의 위치를 나타내는 인덱스를 가지며 배열의 요소에 접근할 때 사용한다.

console.log(arr[0]);  //바나나
console.log(arr[1]);  //복숭아
console.log(arr[2]);  //키위

for문을 통해 요소에 순차 접근할 수 있다.

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

//출력
바나나
복숭아
키위

 

배열은 별도의 타입이 존재하지 않으며 객체 타입이다.

console.log(typeof arr);  //Object

 

일반적인 배열과의 차이점
  • 일반적인 배열 

각 요소가 동일한 데이터 크기를 가지며, 빈틈 없이 연속적으로 이어져 있어 인덱스를 통해 임의의 요소에 한 번에 접근할 수 있는 고속 동작이 장점

  • 자바스크립트의 배열 
일반적인 배열의 동작을 흉내낸 특수한 객체이다.
각각의 메모리 공간이 동일한 크기를 갖지 않아도 되고 연속적으로 이어져 있지 않을 수도 있다.
인덱스로 배열요소에 접근하는 경우 일반적인 배열보다 느리지만, 요소의 삽입 삭제의 경우 빠르다.
console.log(Object.getOwnPropertyDescriptors([1,2,3]));

//출력
{
  '0': { value: 1, writable: true, enumerable: true, configurable: true },
  '1': { value: 2, writable: true, enumerable: true, configurable: true },
  '2': { value: 3, writable: true, enumerable: true, configurable: true },
  length: { value: 3, writable: true, enumerable: false, configurable: false }
}

  자바스크립트의 모든 값이 객체의 프로퍼티 값이 될 수 있으므로 모든 값이 배열의 요소가 될 수 있다.

const arr = [
    '홍길동',
    20,
    true,
    null,
    undefined,
    NaN,
    Infinity,
    [],
    {},
    function() {}
]

 

length property
length property는 요소의 개수를 나타내는 0이상의 정수의 값을 갖는다.
console.log([].length);			//0
const arr = [1, 2, 3, 4, 5];
console.log(arr.length);		//5

 

length property 값은 배열에 요소를 추가하거나 삭제하면 자동으로 갱신된다.
arr.push(6);	//push(6) : 배열 마지막에 6 추가
console.log(arr.length); //6

arr.pop();  //pop() : 마지막 요소 제거
console.log(arr.length); //5
 
length property에 임의의 숫자 값을 명시적으로 할당할 수 있고, 현재 length보다 작은 숫자 값을 할당하면 배열의 길이가 줄어든다.
arr.length = 3;
console.log(arr);		  //[ 1, 2, 3 ]
console.log(arr.length);  //3

 

재 length보다 큰 숫자를 할당하면 length 프로퍼티의 값은 변경되지만 배열의 길이가 늘어나지는 않는다.

-> length의 value 값이 10일뿐, 실제 공간이 늘어나지 않는다.

arr.length = 10;
console.log(arr);
console.log(arr.length);
console.log(Object.getOwnPropertyDescriptors(arr));

//출력
[ 1, 2, 3, <7 empty items> ]
10
{
  '0': { value: 1, writable: true, enumerable: true, configurable: true },
  '1': { value: 2, writable: true, enumerable: true, configurable: true },
  '2': { value: 3, writable: true, enumerable: true, configurable: true },
  length: { value: 10, writable: true, enumerable: false, configurable: false }
}

 

바스크립트는 배열의 요소가 연속적으로 위치하지 않고 일부가 비어있는 희소 배열을 문법적으로 허용한다.

const sparse  = [, 2, , 4];
console.log(sparse.length);
console.log(Object.getOwnPropertyDescriptors(sparse));

//출력
4
{
  '1': { value: 2, writable: true, enumerable: true, configurable: true },
  '3': { value: 4, writable: true, enumerable: true, configurable: true },
  length: { value: 4, writable: true, enumerable: false, configurable: false }
}
엄격 모드
개발자의 의도와 상관없이 발생한 암묵적 전역은 오류 발생의 원인이 될 수 있다. 이러한 잠재적 오류 발생을 근본적으로 막기 위해 ES5부터 엄격 모두가 추가됐다.
function test () {
    // 암묵적으로 전역 변수가 된다.
    x = 10;
}

test();

console.log(x);   //10 -> 정상출력되는 것이 오류

 

엄격 모드 적용

'use strict' 작성 시 엄격모드가 적용되는데 적용 위치에 따라 범위가 달라진다.

// 전역의 선두에 추가 - 스크립트 전체에 strict mode가 적용 된다.
// 'use strict';

function test() {
    // 함수 몸체의 선두에 추가 - 해당 함수와 중첩 함수에 strict mode가 적용된다.
    // 'use strict';

    x = 10;     // 엄격 모드 적용 시 ReferenceError: x is not defined

    // 코드의 선두가 아니면 strict mode 적용 불가로 오류 적용 X
    'use strict';
}

test();

console.log(x);

 

참고

전역에 strict mode를 적용하면 스크립트 태그 단위로 적용되어 strict mode와 non strict mode의 혼용으로 인한
오류가 발생할 수 있으니 유의한다.

<script>
    'use strict';
</script>
<script>
    x = 10; // 에러 발생하지 않음
</script>

 

서드 파티 라이브러리(외부에서 가져온 라이브러리)가 non strict mode인 경우 즉시 실행 함수로 스크립트 전체를 감싸서 스코프를 구분한다.

function(){
    'use strict';   // 즉시 실행 함수의 선두에 작성한다.

    // 작성 코드
    
})();

 

 

error case

1. 암묵적 전역

(function(){
    // 'use strict';

    x = 1;
    console.log(x); // ReferenceError: x is not defined
})();

 

2. 변수, 함수, 매개변수의 삭제

(function(){
    //'use strict';    작성하지 않을 시 delet가 작동함 
                       //(strict 모드에서는 식별자에 대해 'delete'를 호출할 수 없습니다.)

    var x = 1;
    // delete 연산자로 변수, 함수, 매개변수를 삭제하면 문법 에러 발생 (객체의 프로퍼티 삭제 시에 사용)
    delete x;
})();

 

3. 매개변수의 이름 중복

(function(){
    //'use strict';  작동하지 않을 시 마지막으로 받은 값 2 -> 4 반환

    function test(x, x) {   // SyntaxError: Duplicate parameter name not allowed in this context
        return x + x;
    }

    console.log(test(1, 2));

})();

 

4. with문의 사용

with문이란 명령문의 스코프 체인을 확장해주는 구문으로 특정 객체를 여러 번 사용하는 경우 객체명을 생략할 수 있게 한다.

with는 객체 이름을 생략할 수 있어 코드를 간단하게 할 수 있다는 장점이 있지만 성늘과 가독성이 나빠지는 문제로 사용하지 않는 것이 좋다.

(function(){
    // 'use strict';   -> 적용된다면 ('with' 문은 strict 모드에서 사용할 수 없습니다.)

    const test = { x : 1, y : 2, z : 3 };

    with(test) {

        console.log(x);  //1  ->test.x 에서 test 생략 
        console.log(y);  //2
        console.log(z);  //3

    }
})();

 

change case

 

1. 일반 함수의 this

생성자 함수가 아닌 일반 함수 내부에서는 this를 사용할 필요가 없기 때문에  strict mode에서는 함수를 일반 함수로 호출하면 this에 undefined가 바인딩 된다.
 
(function(){
    'use strict';   // 엄격모드가 아니라면 this 값이 출력된다.

    function test() {
        console.log(this);
    }

    test();

    new test();  //undefined

})();

 

2. arguments 객체

 매개변수에 전달 된 인수를 재할당하여 변경해도 arguments 객체에 반영되지 않는다.
(function(x){
    'use strict';   // 엄격모드가 적용되지 않으면 [Arguments] { '0': 2 } 출력됨

    x = 2; //재할당.

    console.log(arguments);

})(1);  // [Arguments] { '0': 1 }
프로토타입 상속
자바스크립트의 객체는 [[Prototype]]이라는 숨김 프로퍼티를 갖는다.
이 프로퍼티 값은 null이거나 다른 객체에 대한 참조가 되는데, 다른 객체를 참조하는 경우 참조 대상을 프로토타입(prototype)이라 부른다.
 
 
const user = {
    activate : true,
    login : function () {
        console.log('로그인 되었습니다')
    }
};

const student = {
    passion : true 
};

.__proto__ [[Prototype]] 의 getter, setter라 할 수 있다.

student.__proto__ = user;

console.log(student.activate);    //true
student.login();				  //로그인 되었습니다.
console.log(student.passion);     //true

 

 => "student의 프로토타입은 user이다." 또는 "student는 user를 상속 받는다." 라고 표현한다.
 => 프로토타입에서 상속 받은 프로퍼티를 상속 프로퍼티라고 한다.

 

프로토타입 체인

object에서 프로퍼티를 읽으려 할 때 해당 프로퍼티가 없으면 자바스크립트는 자동으로 프로토타입에서 프로퍼티를 찾는다.

const greedyStudent = {
    class : 11,
    __proto__ : student
};

console.log(greedyStudent.activate);    //user에서 상속 받은 상속 프로퍼티
console.log(greedyStudent.passion);     //student에서 상속 받은 상속 프로퍼티

 

프로토타입 특징
const user = {
    id : 'user',
    login () {
        console.log(`${this.id}님 로그인 되었습니다.`)
    }
};

const student = {
    __proto__ : user
};

 

 

  • 프로퍼티 추가, 수정 삭제
프로토타입은 프로퍼티를 읽을 때만 사용하며 프로퍼티 추가, 수정, 삭제 연산은 객체에 직접한다.
login 메소드 내의 this는 프로토타입에 영향 받지 않으며 this는 언제나 동작할 때 .앞에 있는 객체를 의미한다. (student)
 
student.id = 'user01';

student.login();  //user01님 로그인 되었습니다.
 
  • for in 반복문
for in 반복문은 상속 프로퍼티도 순회 대상에 포함시킨다.
 
hasOwnProperty(key) : key에 대응하는 프로퍼티가 상속 프로퍼티가 아니라 객체에 직접 구현된 프로퍼티일 경우 true 반환
 
for(let key in student) {
   
    console.log(key);


    let isOwn = student.hasOwnProperty(key);

    if(isOwn) {
        console.log(`객체 자신의 property : ${key}`);
    }
   
}

// 출력
id
객체 자신의 property : id
login

 

생성자 함수 프로퍼티
 new 연산자를 사용해 만든 객체는 생성자 함수의 프로토타입 정보를 사용해서 [[Prototype]]을 설정한다.
const user = {
    activate : true,
    login () {
        console.log('로그인 되었습니다');
    }
};

function Student(name) {
    this.name = name;
}
F.prototypenew F를 호출할 때만 사용된다.
new F를 호출할 때 만들어지는 새로운 객체의 [[Prototype]]을 할당한다. (DNA가 전달된다고 생각해보자)
Student.prototype = user;  // new Student를 호출할 때 만들어지는 새로운 객체마다 user를 상속하게 한다.

let student = new Student('홍길동');    //student.__proto__ == user  (student의 조상은 user이다)
let student2 = new Student('유관순');

console.log(student.activate);    // true
student2.login();    // 로그인 되었습니다
 
 
Student의 다양한 DNA 정보에 user를 추가한다고 생각한다면 new Student로 만들어진 객체에 user의 DNA 정보가 상속된다.
prototype 은 .__proto__ 와 달리 name, age와 같은 일반 프로퍼티인데, 다만 위와 같은 기능을 갖고 있는 것이다.

 

Object prototype

Object는 내장 객체 생성자 함수인데 이 생성자 함수의 prototype은 toString을 비롯한 다양한 메소드가 구현 된 거대한 객체를 참조한다.

 new Object()나 {}를 사용해 객체를 만들 때 만들어진 객체의 [[Prototype]]은 Object.prototype을 참조한다.

(Object의 DNA 정보 즉, 다양한 메소드를 참조한다.)

const obj = {};    // new Object(); 와 같다
console.log(obj.__proto__ === Object.prototype);            //true obj도 객체로서 Object의 메소드들을 쓸 수 있다.
console.log(obj.toString === obj.__proto__.toString);       //true 상속 받은 toString이다.
console.log(obj.toString === Object.prototype.toString);	//true DAN정보에 있는 toString이다

 

built in object prototype
Fucntion, String, Number를 비롯한 내장 객체들 역시 프로토타입에 메서드를 저장한다.
모든 내장 프로토타입 상속 트리 꼭대기에는 Object.prototype이 있어야 한다고 규정한다.

 

const num = new Number(100);

// num은 Number.prototype을 상속 받았는가?
console.log(num.__proto__ == Number.prototype);      //ture

// num은 Object.prototype을 상속 받았는가?
console.log(num.__proto__.__proto__ === Object.prototype);    //true

// 체인 맨 위에는 null이 있다.
console.log(num.__proto__.__proto__.__proto__); 			//null

 

Object.prototype에도 toString이 있지만 중복 메서드가 있는 경우 체인에서 가까운 메서드가 사용되어 Number.prototype의 toString이 사용된다.
 
console.log(num);  //[Number: 100]
console.log(num.toString()); //100      ->toString 은 value값만 보여준다.
 
내장 프로토타입 수정 가능하다.
하지만 되도록 변경하지 않아야 하며, 명세서에 새로 등록된 기능을 쓰고 싶지만 자바스크립트 엔진에 구현되지 않은 경우 정도에만 변경한다. 
String.prototype.hello = function () {
    console.log(`hello, ${this}`);
}

"JavaScript".hello();     //hello, JavaScript

 

프로토 타입 접근 시 사용하는 모던 메소드
  • Object.create(proto) : [[Prototype]] 가 proto를 참조하는 빈 객체를 만듦
const user = {
    activate : true
};

const student = Object.create(user);
console.log(student.activate);    //true
  •  Object.getPrototypeOf(obj) - obj의 [[Prototype]]을 반환
console.log(Object.getPrototypeOf(student));           //{ activate: true }
console.log(Object.getPrototypeOf(student) === user);  //true
  •  Object.setPrototypeOf(obj, proto) - obj의 [[Prototype]]을 proto가 되도록 설정
Object.setPrototypeOf(student, {});
console.log(Object.getPrototypeOf(student));              // {}
console.log(Object.getPrototypeOf(student) === user);     // false
 __proto__를 getter, setter로 직접 사용하면 키가 "__proto__" 일 때 에러가 발생하는 의도치 않은 상황이 발생할 수 있다.
const obj = Object.create(user);  
let key = "__proto__";
console.log(obj[key]);  					// = obj.__proto__ 와 같기에 { activate: true } 출력
obj[key] = { test : "새로운 객체 덮어쓰기"} 
console.log(obj[key]);						//{ test: '새로운 객체 덮어쓰기' }
console.log(obj.__proto__);					//{ test: '새로운 객체 덮어쓰기' }

 

object 생성자 함수

new 연산자와 함께 Object 생성자 함수를 호출하면 빈 객체를 생성하며 반환한다.

// 빈 객체 생성
const student = new Object();

//프로퍼티 추가
student.name = '유관순';
student.age = 16;

console.log(student);   // { name: '유관순', age: 16 }

 

생성자 함수에 의한 객체 생성
객체 리터럴을 이용한 객체 생성 방식은 직관적이고 간편하지만, 단 하나의 객체만 생성하므로
동일 프로퍼티를 갖는 객체를 여러 개 생성해야 하는 경우 매번 같은 프로퍼티를 기술하므로 비효율적이다.
const student = {
    name : '유관순',
    age : 16,
    getInfor () {
        reurn `${this.name}(은)는 ${this.age}세 입니다.`;
    }
};

const student2 = {
    name : '홍길동',
    age : 20,
    getInfor () {
        reurn `${this.name}(은)는 ${this.age}세 입니다.`;
    }
};

const student3 = {
    name : '선덕여왕',
    age : 30,
    getInfor () {
        reurn `${this.name}(은)는 ${this.age}세 입니다.`;
    }
};

 

- 객체를 생성하기 위한 템플릿처럼 생성자 함수를 사용하여 프로퍼티 구조가 동일한 객체 여러 개를 생성할 수 있다.
- 빈 객체 생성 이후 프로퍼티 또는 메서드를 추가하여 객체를 완성할 수 있다.
function Student(name, age) {
    // 생성자 함수 내부의 this는 생성자 함수가 생성할 인스턴스를 가리킨다.
    this.name = name;
    this.age = age;
    this.getInfor = function () {
        return `${this.name}(은)는 ${this.age}세 입니다.`;
    }
}

// 인스턴스의 생성
const student4 = new Student('장보고', 35);
const student5 = new Student('신사임당', 40);

console.log(student4);   //Student { name: '장보고', age: 35, getInfor: [Function (anonymous)] }
console.log(student5);   //Student { name: '신사임당', age: 40, getInfor: [Function (anonymous)] }

 

인스턴스 생성 과정
function Student(name, age) {

    1. 암묵적으로 인스턴스가 생성되고 this에 바인딩되는 과정이 런타임 이전에 실행된다.
    console.log(this); // this = Student {}   =>  student 타입의 빈 객체
    
    2. this에 바인딩 되어 있는 인스턴스를 초기화 한다.
    this.name = name;
    this.age = age;
    this.getInfor = function () {
        return `${this.name}(은)는 ${this.age}세 입니다.`;
    }
    
    3. 완성된 인스턴스가 바인딩 된 this가 암묵적으로 반환된다.
       // Student {} 출력
        만약 명시적으로 객체를 반환하면 암묵적인 this 반환이 무시된다.
        return {};  // Student {} 가 아니라 {}가 반환됨
        하지만 명시적으로 원시값을 반환하면 this가 반환된다.
        return 1;   // Student {} 반환
       
        => 생성자 내부에서 return은 생략하는 것이 기본이다.
}

const student = new Student('홍길동', 20);
console.log(student);  

// 위의 각 경우의 출력값과 함께 
//Student { name: '홍길동', age: 20, getInfor: [Function (anonymous)] }

 

일반 함수와의 차이점 

일반 함수와 생성자 함수의 형식적인 차이는 없다.(첫 문자를 대문자로 기술하여 구별하고자 노력한다.)

단, new 연산자와 함께 호출될 때 생성자 함수로 동작하며 만약 new 연산자와 함께 호출되지 않으면 일반 함수로 동작한다.
function Student(name, age) {
    console.log(this);  //전역 객체 
    this.name = name;
    this.age = age;
    this.getInfor = function () {
        return `${this.name}(은)는 ${this.age}세 입니다.`;
    }
}

일반 함수로 호출된 Student는 반환문이 없으므로 undefined를 반환한다.

const student = Student('강감찬', 35);

console.log(student);    //undefined

 

일반 함수로 호출된 Student내의 this는 전역 객체를 가리킨다. 전역에서 쓸 수 있는 여러가지 기능이 담겨있다.

console.log(age);            //35
console.log(name);           //강감찬
console.log(getInfor());     //강감찬(은)는 35세 입니다.

 

new.target
  • 생성자 함수가 new 연산자 없이 호출되는 것을 방지하기 위해 ES6에서는 new.target을 지원한다.
  • new 연산자와 함께 생성자 함수로서 호출되면 함수 내부의 new.target은 함수 자신을 가리킨다.
  • new 연산자 없이 일반 함수로 호출된 함수 내부의 new.target은 undefined이다.

 

function Dog(name, age) {

    if(!new.target) {  
        
        return new Dog(name, age); 
    }  
    
    this.name = name;
    this.age = age;

}

=> new 연산자와 함께 호출되지 않은 경우 undefined이므로 new 연산자와 함께 생성자 함수를 재귀 호출하여 생성된 인스턴스를 반환한다.

그 후엔 new 연산자 없이 호출해도 new.target을 통해 생성자 함수로서 호출 된다.

const dog = Dog('뽀삐', 3);
console.log(dog);
//Dog { name: '뽀삐', age: 3 }

 

 

대부분의 빌트인 생성자 함수(Object, String, Number, Boolean, Date, ...)는 new 연산자와 함께 호출되었는지 확인 후 적절한 값을 반환한다.
 
String, Number, Boolean의 경우 new 연산자 없이 호출하면 객체 값이 아닌 문자열 숫자, 불리언 값(원시 값)을 반환한다. 이를 데이터 타입 변환에 활용하는 것이다.

 

전역과 지역 스코프

  • 전역 스코프
전역은 코드의 가장 바깥 영역을 말하며 전역은 전역 스코프를 만든다.
전역에 변수를 선언하면 전역 스코프를 갖는 전역 변수가 되며 전역 변수는 어디서든 참조 가능하다.
 
  • 지역 스코프
지역이란 함수 내부 몸체를 말하며 지역은 지역 스코프를 만든다.
지역에 변수를 선언하면 지역 스코프를 갖는 지역 변수가 되며, 자신의 지역 스코프와 하위 지역 스코프에서 유효하다.

 

var x = 'global x';
var y = 'global y';

function outer () {
    var z = "outer's local z";

    console.log(x);     // global x
    console.log(y);     // global y
    console.log(z);     // outer's local z

    function inner () {
        var x = "inner's local x";

        console.log(x);     // inner's local x
        console.log(y);     // global y
        console.log(z);     // outer's local z
    }

    inner();

}

outer();

console.log(x);     // global x
console.log(y);     // global y
console.log(z);     // ReferenceError: z is not defined 전역에는 z가 없다
  • 스코프 체인
 
전역 스코프 (x, y, outer) <- outer 지역 스코프 (z, inner) <- inner 지역 스코프 (x)
 
자바스크립트 엔진은 변수를 참조할 때 스코프 체인을 통해 변수를 참조하는 코드의 스코프에서 시작하여
상위 스코프 방향으로 이동하며 선언된 변수를 검색한다.
 
=> 상위 스코프에서 유효한 변수는 하위 스코프에서 자유롭게 참조 가능,
      하위 스코프에서 유효한 변수는 상위 스코프에서 참조 불가능

 

함수 레벨 스코프

- C, Java 등 대부분의 프로그래밍 언어는 함수 몸체만이 아니라 모든 코드 블록(if, for, while, try/catch 등)이
지역 스코프를 만드는 "블록 레벨 스코프"를 가진다.
-> 블록 안에 선언된 변수를 바깥에서 사용할 수 없다.
`
- 하지만 var 키워드로 선언 된 변수는 오로지 함수의 코드 블록(함수의 몸체)만을 지역 스코프로 인정하는 "함수 레벨 스코프"를 가진다.
- ES6에서 도입된 let, const 키워드는 블록 레벨 스코프를 지원한다.
// i는 전역 변수
var i = 0;

// 전역 변수로 이미 선언 된 i가 중복 선언 된다.
for(var i = 0; i < 10; i++) {}

// for 코드 블럭 내부의 값 변화가 반영된다. 
console.log(i);
 

 

var

ES5까지 변수를 선언할 수 있는 유일한 방법은 var 키워드를 사용하는 것이었는데 이는 몇가지 문제를 유발한다.

1. 변수 중복 선언

- var 키워드로 선언된 변수는 같은 스코프 내에서 중복 선언이 허용된다.

- 초기화 문이 있는 변수 선언문은 자바스크립트 엔진에 의해 var 키워드가 없는 것처럼 행동한다.

- 초기화 문이 없는 변수 선언문은 무시된다.

var msg = "안녕하세요";
console.log(msg);          // 안녕하세요

var msg = "안녕히가세요";
console.log(msg);          // 안녕히가세요

var msg;
console.log(msg);		   // 안녕히가세요

 

2. 함수 레벨 스코프

 

3. 변수 호이스팅

- 변수 선언문이 스코프의 선두로 끌어올려진 것처럼 동작하여 변수 선언문 이전에 참조할 수 있다.

console.log(test);  	//undefined
test = "반갑습니다";
console.log(test); 	    //반갑습니다
var test;

 

let

var 키워드의 단점을 보완하기 위해 ES6에서는 새로운 변수 선언 키워드인 let, const를 도입했다.

1. 변수 중복 선언 금지

- 같은 스코프 내에서 중복 선언을 허용하지 않는다.

let msg = "안녕하세요";

let msg = "안녕히가세요";  -> 블록 범위 변수 'msg'을(를) 다시 선언할 수 없습니다.

2. 블록 레벨 스코프

- 모든 코드 블록을 지역 스코프로 인정한다.

let i = 0;
for(let i = 0; i < 10; i++) {
    console.log(`지역 변수 i : ${i}`);    // 0~10 출력
}
console.log(`전역 변수 i : " ${i}`);	  // 전역 변수 i : 0

 

3. 변수 호이스팅

- 변수 호이스팅이 발생하지 않은 것처럼 동작한다.

console.log(x); //ReferenceError: Cannot access 'x' before initialization
let x;

* var : 선언, 초기화 동시   /    let : 선언, 초기화 분리되어 동작

 

const

let 키워드에서 알아본 특징은 모두 동일하며, 상수(const) 선언에서만 사용된다는 점이 특징이다.

- const 키워드로 선언한 변수는 반드시 선언과 동시에 초기화해야 한다.

- const 키워드로 선언한 변수는 재할당 금지

const x;          // 'const' 선언은 초기화해야 합니다.

const x = 1;

x = 2; 			 // TypeError: Assignment to constant variable.

- 일반적으로 상수의 이름은 대문자로 선언해 상수임을 나타내며, 여러 단어로 이루어진 경우 _(언더스코어)로 구분해서 스네이크 케이스로 표현하는 것이 일반적이다.

const DISCOUNT_RATE = 0.15;

- const 키워드로 선언된 변수에 객체를 할당할 경우 프로퍼티 값을 변경할 수 있다.

- 객체 재할당은 불가능하다.

const student = {
    name : '홍길동',
    age : 20
};



student.name = '유관순';
console.log(student);        // { name: '유관순', age: 20 }



student = {};                // TypeError: Assignment to constant variable.

 

화살표 함수 기본 문법

ES6에서 도입 된 화살표 함수는 function 키워드 대신 화살표를 사용해 보다 간략하게 함수를 선언하는 방법이다.
 
  • 화살표 함수는 항상 익명함수로 정의하며 본문이 한 줄인 함수를 작성할 때 유용하다.
var message;

// 기존 function 정의
message = function() {
    return 'hello world!';
};

console.log(message());               //hello world!

// function 키워드 생략
message = () => {
return 'Arrow Function!';
};

console.log(message());              //Arrow Function!
-명령문이 하나만 있을 경우 중괄호 생략 가능
 
-함수 몸체 내부의 문이 값으로 평가 될 수 있는 표현식인 문이라면 암묵적으로 반환(return 생략 가능)
message = () => 'Arrow Functions are simple!';

 

  • 매개 변수가 있을 경우
message = (val1, val2) => 'Arrow ' + val1 + val2;

console.log(message('Function', '!'));

//Arrow Function!

 

  • 매개변수가 하나이면 소괄호를 생략
매개변수가 없거나 여러 개일 경우 생략 불가
message = val1 => "Arrow " + val;
console.log(message('Functions are GOOD!!!'));


//Arrow Functions are GOOD!!!

 

즉시 실행 함수

함수 정의와 동시와 즉시 호출이 되는 함수로 단 한번만 호출 되며 다시 호출할 수 없다.
즉시 실행 함수는 반드시 () - 그룹 연산자로 감싸야 한다.
 
  • 함수 이름이 없는 익명 함수를 사용하는 것이 일반적이다.
(function() {
	console.log('익명 즉시 실행함수! 함수 정의와 동시에 호출!' )
 })();
  • 이름을 지어도 다시 호출할 수 없기에 의미가 없다.
(function hello(name){  -> 매개변수
	console.log('기명 즉시 실행함수! 함수 정의와 동시에 호출!');
    console.log(`${name}님 안녕하세요`);
})('홍길동');  -> 전달값


hello('유관순');   // ReferenceError: hello is not defined




//출력
기명 즉시 실행함수! 함수 정의와 동시에 호출!
홍길동님 안녕하세요

 

즉시 실행 함수 용도
 
: 즉시 실행 함수 내에 코드를 모아두면 밖에 있는 코드들과 영역을 분리하여 혹시 있을수도 있는 변수나 함수의 이름 충돌을 방지할 수 있다.

 

재귀 함수

함수가 자기 자신을 호출하는 것을 재귀 호출이라고 한다.
이러한 재귀 호출을 수행하는 함수인 재귀 함수는 반복되는 처리를 위해 사용한다.
 
 
- 팩토리얼(n! = 1 * 2 * ... * (n-1) * n;)을 재귀함수로 표현할 경우
function factorial(n) {
	
    //n이 1 이하일 때 1을 반환하여 반복을 멈춤
    if(n <= 1) return 1;
    
	return n * factorial(n-1);  //재귀 호출

}
 

-> 재귀 함수로 표현하는 것이 직관적인 경우 사용, 잘못 사용하면 스택오버플로우 현상이 발생할 수 있으므로 남용X

 

중첩 함수

함수 내부에 정의 된 함수를 중첩 함수 또는 내부 함수라고 한다.
 
- 중첩 함수를 포함하는 함수는 외부 함수라고 한다.
- 외부 함수의 변수를 내부 함수에서 참조할 수 있다.
- 일반적으로 중첩 함수는 자신을 포함하는 외부 함수를 돕는 헬퍼 함수의 역할을 한다.
 
function outer () {
    var outerVal = '외부 함수';

    function inner () {
        var innerVal = '내부 함수';
        console.log(outerVal + ", " + innerVal); 
    }

    inner();  //내부 함수를 호출해야 내부 함수의 동작을 가져다 쓴다. 출력 가능

}

outer();  // 출력불가. outer 함수 선언만 읽혀진다.
inner();  // outer 안에서 동작하는 함수이기 때문에 인식 안됨.


// 출력
외부 함수, 내부 함수

 

콜백 함수

함수의 매개변수를 통해 다른 함수의 내부로 전달되는 함수를 콜백 함수라고 한다.
- 매개변수를 통해 함수의 외부에서 콜백 함수를 전달 받은 함수를 고차 함수라고 한다.
- 콜백 함수는 고차 함수에 전달 되어 헬퍼 함수의 역할을 한다.
// 전달 받는 값을 증가 시켜주는 함수
function increase(value) {
    return value + 1;
}

// 전달 받는 값을 감소 시켜주는 함수
function decrease(value) {
    return value - 1;
}
 
 - 고차 함수는 매개변수를 통해 전달 받은 콜백 함수의 호출 시점을 결정해서 호출한다.
 - 콜백 함수는 고차 함수에 의해 호출 되며 이 때 고차 함수는 필요에 따라 콜백 함수에 인수를 전달할 수 있다.
 
 
// 전달 받은 함수에 전달 받은 값을 적용 시켜주는 고차 함수
function apply(func, value) {
  
    return func(value);
}

// 고차 함수로 콜백 함수를 전달하며 호출한다.

console.log(apply(increase, 5));   // 6
console.log(apply(decrease, 5));   // 4

 

- 콜백 함수가 고차 함수 내부에서만 호출 된다면 콜백 함수를 익명 함수 리터럴로 정의하면서 바로 고차 함수에 전달하는 것이 일반적이다.

 

console.log(apply(function(value){ return value + 1;}, 10));   // 11  -> 함수명 생략
console.log(apply(value => value -1, 10));       // 9  -> function 생략, 중괄호, 리턴 생략

 

- 함수의 변하지 않는 공통 로직은 미리 정의해두고 경우에 따라 변경 되는 로직은 추상화해서 함수의 외부에서 내부로 전달하는 방식이다.
- 콜백 함수는 비동기 처리(이벤트, 타이머, ajax)에서 활용 되는 중요한 패턴이며 배열 고차 함수에서도 사용 된다.

 

* 참고

배열은 대괄호[]

sort() 함수는 배열을 정렬하는 함수. 정렬 기준을 전달 받는다.sort() 함수는 고차 함수, 정렬기준 함수는 콜백 함수이다.

console.log([3, 1, 4, 2, 5].sort((left, right) => right - left));


-> 양수가 반환되면 순서를 바꾼다. 역순으로 정렬됨 [5, 4, 3, 2, 1]

 

순수함수와 비순수함수

순수 함수 : 외부 상태에 의존하지도 않고 변경하지도 않는 함수
비순수 함수 : 외부 상태에 의존하거나 외부 상태를 변경하는 함수
 
-> 함수 외부 상태의 변경을 지양하는 순수 함수를 사용하는 것이 좋다.

 

- 순수 함수는 최소 하나 이상의 인수를 전달 받으며 인수의 불변성을 유지한다.

var cnt = 0;

function increase(n) {
    return ++n;
}

- 순수 함수가 반환한 결과 값을 변수에 재할당해서 상태를 변경한다.

cnt = increase(cnt);
console.log(cnt);     // cnt 는 1이 출력된다

 

- 비순수 함수는 외부 상태에 의존하며 외부 상태를 변경한다.

function decrease() {
    return --cnt;
}

 

- 비순수 함수는 외부 상태(cnt)를 변경하므로 상태 변화를 추적하기 어려워진다.

-> 함수는 input을 받아서 연산한 값을 반환하는 형태가 좋다.

decrease();        // --cnt로 변경
console.log(cnt);  // 0

 

+ Recent posts