Appendix A: Exploring Further_ - hochan222/Everything-in-JavaScript GitHub Wiki

Implied Scopes

λ²”μœ„λŠ” λ•Œλ•Œλ‘œ λͺ…ν™•ν•˜μ§€ μ•Šμ€ μž₯μ†Œμ—μ„œ μƒμ„±λœλ‹€. μ‹€μ œλ‘œ μ΄λŸ¬ν•œ λ¬΅μ‹œμ  λ²”μœ„λŠ” ν”„λ‘œκ·Έλž¨ λ™μž‘μ— 자주 영ν–₯을 μ£Όμ§€μ•Šμ§€λ§Œ μ΄λŸ¬ν•œ λ¬Έμ œκ°€ λ°œμƒν•˜κ³  μžˆμŒμ„ μ•„λŠ” 것은 μ—¬μ „νžˆ β€‹β€‹μœ μš©ν•˜λ‹€.

β€’ Parameter scope
β€’ Function name scope

Parameter Scope

// outer/global scope: RED(1)
function getStudentName(studentID) { 
    // function scope: BLUE(2)
    // ..
}

ν•¨μˆ˜μ˜ νŒŒλΌλ―Έν„°μ™€ ν•¨μˆ˜μ˜ 지역 λ³€μˆ˜ λ²”μœ„κ°€ κ°™λ‹€κ³  μΈμ‹λ˜μ§€λ§Œ 항상 참은 μ•„λ‹ˆλ‹€. λ‹¨μˆœ λ§€κ°œλ³€μˆ˜λŠ” 지역 λ³€μˆ˜ λ²”μœ„μ™€ 같을 수 μžˆμ§€λ§Œ, λ‹¨μˆœ λ§€κ°œλ³€μˆ˜κ°€ μ•„λ‹Œ μ΄ˆκΈ°ν™” 값을 ν¬ν•¨ν•˜κ±°λ‚˜ ...등이 있으면 λ‹€λ₯΄λ‹€.

// outer/global scope: RED(1)
function getStudentName(/*BLUE(2)*/ studentID = 0) { 
    // function scope: GREEN(3)
    // ..
}

λ§€κ°œλ³€μˆ˜ λ²”μœ„λŠ” 자체 λ²”μœ„λ₯Ό κ°–κ³  ν•¨μˆ˜μ˜ λ²”μœ„λŠ” λ§€κ°œλ³€μˆ˜ λ²”μœ„λ₯Ό ν¬ν•¨ν•œ 쀑첩 λ²”μœ„λ₯Ό κ°–λŠ”λ‹€.

function getStudentName(studentID = maxID, maxID) { 
    // ..
}

μœ„μ™€ 같은 ν•¨μˆ˜λŠ” TDZ 였λ₯˜κ°€ λ°œμƒν•œλ‹€. maxIDλŠ” 선언은 λμ§€λ§Œ μ΄ˆκΈ°ν™”κ°€ λ˜μ§€ μ•Šμ•˜κΈ° λ•Œλ¬Έμ΄λ‹€. studentID = maxIDμ—μ„œ maxIDλŠ” μ„ μ–Έκ³Ό μ΄ˆκΈ°ν™” λ‘˜λ‹€ λ˜μ–΄μžˆμ–΄μ•Ό ν•œλ‹€.

function getStudentName(maxID, studentID = maxID) { 
    // ..
}

μˆœμ„œλ₯Ό λ°”κΎΈλ©΄ 였λ₯˜κ°€ 사라진닀.

λ§€κ°œλ³€μˆ˜ μ•ˆμ— ν•¨μˆ˜ ν‘œν˜„μ‹μ΄ μžˆμ„ 경우 더 λ³΅μž‘ν•΄μ§„λ‹€. 자체 ν΄λ‘œμ €λ₯Ό λ§Œλ“ λ‹€.

function whatsTheDealHere(id, defaultID = () => id) { 
    id = 5;
    console.log( defaultID() ); 
}

whatsTheDealHere(3);
// 5

ν•¨μˆ˜ ν‘œν˜„μ‹μ΄ λλ‚œλ‹€μŒ id = 5κ°€ μž¬ν• λ‹Ή 된 것을 λ³Ό 수 μžˆλ‹€.

function whatsTheDealHere(id, defaultID = () => id) { 
    var id = 5;
    console.log( defaultID() ); 
}

whatsTheDealHere(3);
// 3

id κ°€ var id = 5둜 인해 μ„€λ„μž‰μ΄ μΌμ–΄λ‚˜κ³  μžˆλŠ” 것을 λ³Ό 수 있고, λ‹€λ₯Έ λ²”μœ„λ₯Ό ν˜•μ„±ν•˜λŠ” 것을 λ³Ό 수 μžˆλ‹€.

λ‹€μŒμ€ 더 이상해진닀.

function whatsTheDealHere(id, defaultID = () => id) { 
    var id;
    console.log(`local variable 'id': ${ id }`); 
    console.log(`parameter 'id' (closure): ${ defaultID() }` );
    console.log("reassigning 'id' to 5"); 

    id = 5;
    console.log(`local variable 'id': ${ id }`); 
    console.log(`parameter 'id' (closure): ${ defaultID() }` );
}

whatsTheDealHere(3);

// local variable 'id': 3 <--- ????? // parameter 'id' (closure): 3
// reassigning 'id' to 5
// local variable 'id': 5
// parameter 'id' (closure): 3

μ„€λ„μž‰ λ³€μˆ˜λŠ” 보톡 undefined둜 μ΄ˆκΈ°ν™” λ˜λŠ”λ° μ™œ.. local variable 'id': 3κ°€ λ‚˜μ™”μ„κΉŒ..
ν˜Έν™˜ 이유둜 μžλ°”μŠ€ν¬λ¦½νŠΈ μŠ€νŽ™μ—μ„œλŠ” idλ₯Ό undefined둜 μ΄ˆκΈ°ν™”ν•˜μ§€ μ•ŠλŠ”λ‹€. λŒ€μ‹  parameter κ°’μœΌλ‘œ μ΄ˆκΈ°ν™” ν•œλ‹€. 그리고 두 idλŠ” λ…λ¦½λœ λ²”μœ„λ₯Ό ν˜•μ„±ν•œλ‹€.

5κ°€ ν• λ‹Ήλœ μ΄ν›„μ—λŠ” λ§€κ°œλ³€μˆ˜λŠ” idλŠ” 3의 값을 지역 λ³€μˆ˜λŠ” idλŠ” 5의 값을 κ°–λŠ”λ‹€.

λ”°λΌμ„œ, μš°λ¦¬λŠ” 쑰심할 ν•„μš”κ°€ μžˆλ‹€. νŒŒλΌλ―Έν„°λ₯Ό 지역 λ³€μˆ˜λ‘œ μ„€λ„μž‰ ν•˜μ§€λ§μž.

Function Name Scope

μ•žμ—μ„œ ν•¨μˆ˜ ν‘œν˜„μ‹μ˜ 이름 μ‹λ³„μžλŠ” μ™ΈλΆ€ 및 λ‚΄λΆ€ κΈ°λ³Έ ν•¨μˆ˜ λ²”μœ„ 사이에 쀑첩 된 λ²”μœ„λ₯Ό ν¬ν•¨ν•˜λŠ” μ•”μ‹œμ  λ²”μœ„μ— μžˆλ‹€κ³  λ‹€λ€˜λ‹€.

var askQuestion = function ofTheTeacher(){
    // why is this not a duplicate declaration error? 
    let ofTheTeacher = "Confused, yet?";
};

let은 μž¬μ„ μ–Έμ„ ν—ˆμš©ν•˜μ§€ μ•ŠλŠ”λ‹€. 이둜써 λ‘˜μ€ λ‹€λ₯Έ λ²”μœ„μ— μžˆλŠ”κ²ƒμ„ μ•Œ 수 μžˆλ‹€. λ”°λΌμ„œ, μ„€λ„μž‰μ„ μΌμœΌν‚¬ 수 μžˆλŠ” ν•¨μˆ˜ μ‹λ³„μžλŠ” μ“°μ§€λ§μž.

Anonymous vs. Named Functions

Explicit or Inferred Names?

ν”„λ‘œκ·Έλž¨μ— λͺ¨λ“  ν•¨μˆ˜λŠ” λͺ©μ μ„ 가지고 μžˆλ‹€. λͺ©μ μ΄ μ—†μœΌλ©΄ ν•¨μˆ˜ λ°–μœΌλ‘œ 꺼내라. 그것은 λ©”λͺ¨λ¦¬ μ°¨μ§€λ§Œ ν•˜κ³  μžˆμ„ 뿐이닀.

읡λͺ… ν•¨μˆ˜λŠ” μŠ€νƒμΆ”μ μ— λ‚˜νƒ€λ‚˜μ§€ μ•Šμ•„ 디버깅에 도움이 μ•ˆλœλ‹€. μ•„λž˜λ₯Ό 비ꡐ해 보아라.

btn.addEventListener("click",function(){ 
    setTimeout(function(){
        ["a",42].map(function(v){ 
            console.log(v.toUpperCase());
        }); 
    },100);
});

// Uncaught TypeError: v.toUpperCase is not a function
// at myProgram.js:4
// at Array.map (<anonymous>)
// at myProgram.js:3
btn.addEventListener("click",function onClick(){ 
    setTimeout(function waitAMoment(){
        ["a",42].map(function allUpper(v){ 
            console.log(v.toUpperCase());
        }); 
    },100);
});

// Uncaught TypeError: v.toUpperCase is not a function
// at allUpper (myProgram.js:4)
// at Array.map (<anonymous>)
// at waitAMoment (myProgram.js:3)

ν•¨μˆ˜μ— 이름을 μ‚¬μš©ν•΄μ•Ό 디버깅이 쉽닀. λ˜ν•œ λŠ” js μ—”μ§„μ—μ„œ Array.map의 κ΅¬ν˜„μ΄ μžˆμŒμ„ 보여쀀닀.

이름 μžˆλŠ” ν•¨μˆ˜μ™€ 이름 μ—†λŠ” ν•¨μˆ˜κ°€ μžˆλ‹€.

function thisIsNamed() { // ..
}

makeRequest({
    data: 42,
    cb /* also not a name */: function(){ 
        // ..
    } 
});

μ•„λž˜ κ²½μš°λŠ” μ–΄λ–€ κ²½μš°μΈκ°€?

var notNamed = function(){ 
    // ..
};

var config = {
    cb: function(){
    // ..
    }
};

notNamed.name;
// notNamed

config.cb.name;
// cb

μš°λ¦¬λŠ” 이것듀을 μΆ”λ‘  이름(inferred name)이라 λΆ€λ₯Έλ‹€. 이번 λ‹¨μ›μ—μ„œλŠ” λ…Όμ˜μ— ν¬ν•¨λ˜μ–΄μžˆμ§€ μ•Šμ§€λ§Œ 이름 ν•¨μˆ˜μ™€ λΉ„μŠ·ν•˜λ‹€.

Missing Names?

λͺ¨λ“  κ²½μš°μ— 읡λͺ…보닀 μ΄λ¦„μžˆλŠ” ν•¨μˆ˜κ°€ μ’‹μ•„ 보인닀. 우리 생각보닀 이름 μžˆλŠ” ν•¨μˆ˜λŠ” μ˜λ„ν•˜μ§€ μ•ŠλŠ”ν•œ λŒ€λΆ€λΆ„μ΄ 읡λͺ… ν•¨μˆ˜μž„μ„ μ•Œ 수 μžˆλ‹€. λ‹€μŒ 경우λ₯Ό 보자.

function ajax(url,cb) { 
    console.log(cb.name);
}

ajax("some.url",function(){ 
    // ..
});
// ""

보톡 콜백으둜 λ“€μ–΄μ˜€λŠ” ν•¨μˆ˜λŠ” 이름이 μ—†λ‹€.

콜백 ν•¨μˆ˜ 말고도 λ‹€μŒ κ²½μš°μ—λ„ ν•΄λ‹Ήλœλ‹€.

var config = {};
config.cb = function(){ 
    // ..
}; 
config.cb.name;
// ""

var [ noName ] = [ function(){} ]; 
noName.name
// ""

ν•¨μˆ˜μ‹μ˜ 이름 할당도 λŒ€λΆ€λΆ„ μ‹€νŒ¨ν•œλ‹€.

Who am I?

이름이 μ—†μœΌλ©΄ μž¬κ·€, μž¬ν• λ‹Ήμ΄ λΆˆκ°€λŠ₯ν•˜λ‹€.

// broken
runOperation(function(num){
    if (num <= 1) return 1;
    return num * oopsNoNameToCall(num - 1);
});

// also broken
btn.addEventListener("click",function(){ 
    console.log("should only respond to one click!"); 
    btn.removeEventListener("click",oopsNoNameHere);
});

Names are Descriptors

λ˜ν•œ 이름은 ν•¨μˆ˜μ˜ λͺ©μ μ΄ 무언인지 μ„€λͺ…ν•˜λŠ” μ§€ν‘œμ΄κΈ°λ„ ν•˜λ‹€.

[ 1, 2, 3, 4, 5 ].filter(function(v){ return v % 2 == 1;
});
// [ 1, 3, 5 ]

[ 1, 2, 3, 4, 5 ].filter(function keepOnlyOdds(v){ return v % 2 == 1;
});
// [ 1, 3, 5 ]

Arrow Functions

ν™”μ‚΄ν‘œ ν•¨μˆ˜λŠ” μΆ”λ‘  된 이름을 μ œκ³΅ν•˜λŠ” λ°©μ‹μœΌλ‘œ μ‚¬μš© λ˜λ”λΌλ„ 항상 읡λͺ…이닀. ν™”μ‚΄ν‘œ ν•¨μˆ˜λŠ” var self = thisλ‚˜ bind(this)의 문제λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•΄ μ„€κ³„λ˜μ—ˆλ‹€. ν•˜μ§€λ§Œ, μš°λ¦¬λŠ” λ‹€μŒμ˜ 문법적 κ°•μ œλ₯Ό 톡해 문제λ₯Ό ν•΄κ²°ν•˜μ§€λ§Œ 읡λͺ… ν•¨μˆ˜μ˜ 단점도 κ°–λŠ”λ‹€λŠ” 것을 항상 μΈμ§€ν•΄μ•Όν•œλ‹€.

IIFE Variations

λͺ¨λ“  ν•¨μˆ˜μ—λŠ” 이름이 ν•„μš”ν•˜λ‹€. μ΄λŠ” IIFE도 λ‹€λ₯΄μ§€ μ•Šλ‹€.

(function(){
    // don't do this!
})();

(function doThisInstead(){ 
    // ..
})();

μš°λ¦¬κ°€ function μ„ μ–Έ ꡬ문으둜 JS parserκ°€ ν•΄μ„ν•˜λŠ”κ±Έ ν”Όν•˜κΈ°μœ„ν•΄ (...)()을 μ“°λŠ”λ°, IIFEλ₯Ό μ •μ˜ν•˜λŠ” 방법은 (...)()만 μžˆλŠ”κ±΄ μ•„λ‹ˆλ‹€.

!function thisIsAnIIFE(){ 
    // ..
}();

+function soIsThisOne(){ 
    // ..
}();

~function andThisOneToo(){ 
    // ..
}();

μš°λ¦¬λŠ” !, +, ∼ 와 λͺ‡κ°€μ§€ 단항 μ—°μ‚°μžλ₯Ό λ°°μΉ˜ν•΄ ν‘œν˜„μ‹μœΌλ‘œ λ°”κΏ€ 수 μžˆλ‹€. voidλ₯Ό ν•¨μˆ˜ μ•žμ— λΆ™μ΄λŠ” κ²½μš°μ™€ λΉ„μŠ·ν•˜λ‹€.

void function yepItsAnIIFE() { 
    // ..
}();

void의 이점은 ν•¨μˆ˜μ˜ μ‹œμž‘ 뢀뢄에 이 iifeκ°€ μ–΄λ–€ 값도 λ°˜ν™˜ν•˜μ§€ μ•Šμ„ κ²ƒμž„μ„ λͺ…ν™•ν•˜κ²Œ μ „λ‹¬ν•œλ‹€λŠ” 것이닀.

였... (function...)(); 말ꡬ void function(); 을 써야겠ꡰ...

Hoisting: Functions and Variables

Function Hoisting

μ•ž λ‚΄μš©μ—μ„œ μ•„λž˜ ν”„λ‘œκ·Έλž¨μ€ ν˜Έμ΄μŠ€νŒ… 덕뢄에 μž‘λ™ ν•˜λŠ” 것을 μ•Œ 수 μžˆμ—ˆλ‹€.

getStudents();
// ..

function getStudents() { 
    // ..
}

ν•¨μˆ˜ 선언은 컴파일 쀑에 ν‘œμ‹œλœλ‹€. getStudentsλŠ” 전체 λ²”μœ„μ— λŒ€ν•΄ μ„ μ–Έ 된 μ‹λ³„μžμ΄κ³ , getStudents μ‹λ³„μžλŠ” λ‹€μ‹œ λ²”μœ„μ˜ μ‹œμž‘ λΆ€λΆ„μ—μ„œ ν•¨μˆ˜ 참쑰둜 μ΄ˆκΈ°ν™”λœλ‹€.

getStudents();

// *************
function getStudents() {
    var whatever = doSomething();
    
    // other stuff
    return whatever;

   // *************
    function doSomething() { 
        // ..
    } 
}

ν•¨μˆ˜ ν˜Έμ΄μŠ€νŒ…μ€ λ˜ν•œ 가독성을 λ†’μ—¬μ€€λ‹€.

Variable Hoisting

pleaseDontDoThis = "bad idea";
// much later
var pleaseDontDoThis;

μœ„μ˜ μ˜ˆμ‹œλ§Œ 보더라도 Variable Hoisting이 μ–Όλ§ˆλ‚˜ μ•ˆμ’‹μ€ 문법인지 μ•Œ 수 μžˆλ‹€.
ν•¨μˆ˜ ν˜Έμ΄μŠ€νŒ…μ—μ„œλŠ” μ’‹μ•˜μ§€λ§Œ, 일반적으둜 λ³€μˆ˜μ—μ„œ μΆ”λ‘ ν•˜κΈ°μ—λŠ” μ–΄λ €μ›Œ 보인닀.

The Case for var

const-antly Confused

constλŠ” λ³€κ²½ ν•  μˆ˜μ—†λŠ” 값을 λ§Œλ“œλŠ” μ²™ν•œλ‹€. ν•˜μ§€λ§Œ, μ‹€μ œλ‘œλŠ” μž¬ν• λ‹Ήμ„ λ°©μ§€ν•˜λŠ” 것이닀.

const studentIDs = [ 14, 73, 112 ];

studentIDs.push(6); // whoa, wait... what!?

μž¬ν• λ‹Ή λ¬Έμ œλ‚˜ μƒμˆ˜μΌλ•Œλ§Œ constλ₯Ό μ‚¬μš©ν•˜μž.
λ‹€μ‹œ ν• λ‹Ήλ˜μ§€ μ•ŠλŠ” let (λ˜λŠ” var)은 컴파일러 보증이 없더라도 이미 "μƒμˆ˜"둜 λ™μž‘ν•œλ‹€. μ΄κ²ƒλ“€λ‘œ 이미 μΆ©λΆ„ν•˜λ‹€.

var and let

letκ³Ό var을 적절히 μ‚¬μš©ν•˜μž. ν•¨μˆ˜μ˜ μ‹œμž‘, 쀑간 λ˜λŠ” 끝에 관계없이 항상 λͺ¨λ“  κΈ°λŠ₯의 μ΅œμƒμœ„ λ²”μœ„μ—μ„œ varλ₯Ό μ‚¬μš©ν•˜μž. let은 μ§€μ—­ν™”λœ λ²”μœ„λ₯Ό var은 ν•¨μˆ˜ μ „μ²΄λ²”μœ„λ₯Ό λ‚˜νƒ€λ‚΄λŠ”λ° μ‚¬μš©ν•˜μž.

What’s the Deal with TDZ?

Where It All Started

{
    // what should print here?
    console.log(studentName);

    // later
    const studentName = "Frank"; 
    // ..
}

초기 ES6 개발 μž‘μ—… 쀑에 TC39λŠ” const(및 let)의 λ™μž‘μ„ var처럼 ν˜Έμ΄μŠ€νŒ… 되게 λ§Œλ“€κΉŒ 생각을 ν–ˆλ‹€. κ·ΈλŸ¬λ‚˜ μœ„ μ½”λ“œμ—μ„œ 그럴경우 constλŠ” μƒμˆ˜ 값인데 λ‘œμ§μƒ λΆˆνŽΈν•˜κ±°λ‚˜ coolν•˜μ§€ λͺ»ν•¨μ„ μ•Œ 수 μžˆλ‹€. λ˜ν•œ, undefined 둜 μžλ™ μ΄ˆκΈ°ν™” ν•˜μ§€ μ•ŠλŠ” μ΄μœ λ„ μƒμˆ˜κ°’μΈλ° 두가지 μƒνƒœλ₯Ό κ°–λŠ”λ‹€λŠ” 것 μžμ²΄κ°€ μ΄μƒν•˜κΈ° λ•Œλ¬Έμ΄λ‹€. λ”°λΌμ„œ 그듀은 μ„ μ–Έ μ‹œκ°„κ³Ό ν• λ‹Ήμ‹œκ°„μ„ μ •μ˜ν•˜κ²Œ 됐고 TDZκ°€ λ§Œλ“€μ–΄μ‘Œλ‹€.

Are Synchronous Callbacks Still Closures?

  • ν΄λ‘œμ €λŠ” ν•¨μˆ˜κ°€ λ‹€λ₯Έ λ²”μœ„μ—μ„œ μ „λ‹¬λ˜κ³  ν˜ΈμΆœλ˜λŠ” κ²½μš°μ—λ„ μ™ΈλΆ€ λ³€μˆ˜λ₯Ό κΈ°μ–΅ν•˜λŠ” ν•¨μˆ˜ μΈμŠ€ν„΄μŠ€μ΄λ‹€.

closure

Classic Module Variations

Asynchronous Module Defintion (AMD)

define([ "./Student" ],function StudentList(Student){ 
    var elems = [];

    return { 
        renderList() {
            // ..
        }
    }
});

Universal Modules (UMD)

⚠️ **GitHub.com Fallback** ⚠️