[ydkjsy]Scope & Closures-A-Exploring Further

p*0*q·2021년 1월 18일
0

YDKJS

목록 보기
13/16

필자의 주관이 들어가 있다.

Implied Scopes

큰 문제는 일어나지 않겠지만...

Parameter Scope

parameter의 scope는 다음과 같은 경우 약간 달라질 수 있다.

non-simple parameter forms(parameters with default values, rest parameters (using ...), and destructured parameters)

//1.
// outer/global scope: RED(1)

function getStudentName(/*BLUE(2)*/ studentID = 0) {
    // function scope: GREEN(3)

    // ..
}
//with function expression and CLOSURE
function whatsTheDealHere(id,defaultID = () => id) {
    id = 5;
  //case2: var id = 5; // shadow id!
    console.log( defaultID() );
}

whatsTheDealHere(3);
// 5
//case2: 3
//Huh
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   <--- Huh!? Weird!
//id에 undefined 아닌 3
// parameter 'id' (closure): 3
// reassigning 'id' to 5
// local variable 'id': 5
// parameter 'id' (closure): 3

위의 이상한 상황을 방지하기 위해 지역변수로 shadow하지 말고, parameter로 클로즈 오버하는 defulat parameter function을 지양하자.

Function Name Scope

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

ofTheTeacher 식별자가 함수 내부로 간다고 했지만 사실은 바깥 스코프와 main 함수 스코프 중간의 implied scope에 있다.

Anonymous vs. Named Functions

함수 이름과 관련하여

  • Name inference is incomplete
  • Lexical names allow self-reference
  • Names are useful descriptions
  • Arrow functions have no lexical names
  • IIFEs also need names

Explicit or Inferred Names?

inferred names 추론된 이름도 stack traces에서 보여지긴 하나..

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

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

notNamed.name;
// notNamed

config.cb.name;
// cb

Missing Names?

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

ajax("some.url",function(){
    // ..
});
// ""
//?.. 익명함수로 넣어줘서 그런거 아님?....... 일단.. keep goin..
//다음과 같이 할당을 할 경우에는 추론 실패
var config = {};

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

config.cb.name;
// ""

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

Who am I?

또한 function 선언식 처럼 lexical name identifier가 없으면 자기 자신을 가리킬 수 없다.
(recursion, event handling)

// 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

이름에 목적을 잘 드러내자. 심지어 한 줄 짜리어도!(In map, then)

lookupTheRecords(someData)
.then(function extractSalesRecords(resp){
   return resp.allSales;
})
.then(storeRecords);

Arrow Functions

arrow function의 목적은 lexical this 동작이다. 즉, 화살표 함수에서의 this는 다른 변수 참조와 비슷하게 작동한다(없을 시 바깥으로 가는 것을 말하는 듯). var self = this.bind(this)를 이용해 바깥 함수에서의 this를 계승하기를 원한다면 화살표 함수를 써라. 유의해야할 점은 화살표 함수는 기본적으로 inferred name을 갖는다.

IIFE Variations

여기서도 이름을 써주자. 그리고 IIFE의 첫번째 괄호는 그저 JS parser에 function 키워드를 함수 선언으로 인식하지 않게 하기 위함이라 이것을 대체할 방법들이 있다. 함수 앞에 !, +, ~ 등 단항 연산자를 붙여서 만들 수 있고, void를 앞에 붙여서 standalone IIFE를 정의할 수 있다.
void를 쓰면 값이 반환되지 않을 것이라고 명백히 할 수 있다.

Hoisting: Functions and Variables

호이스팅의 이점을 살펴보자(주관적).

Function Hoisting

  • 실행가능한 코드 먼저, 마지막에 함수 선언.
    실행할 코드를 위에서 쉽게 찾을 수 있기 때문에.

Variable Hoisting

  • 변수 선언의 위치를 의미있게.
    varialbe를 위처럼 하는 것은 좋지 않다. CommonJS 모듈 정의에서 var 위치를 보자.
// dependencies
var aModuleINeed = require("very-helpful");
var anotherModule = require("kinda-helpful");

// public API
var publicAPI = Object.assign(module.exports,{
    getStudents,
    addStudents,
    // ..
});

// ********************************
// private implementation below cache, otherData

var cache = { };
var otherData = [ ];

function getStudents() {
    // ..
}

function addStudents() {
    // ..
}

간혹 exported public API 전에 변수의 값을 둬야할 필요가 있다.

cache = {};   // used here, but declared below

// public API
var publicAPI = Object.assign(module.exports,{
    getStudents,
    addStudents,
    refreshData: refreshData.bind(null,cache)
});

// ********************************
// private implementation

var cache /* = {}*/;

The Case for var

  • var was never broken
  • let is your friend
  • const has limited utility
  • The best of both worlds: var and let

const는 primitive에만 쓰자.
var는 함수 최상위 레벨 영역(시작부 등 무관) 혹은 전역.
let은 블록 단위, 지역적으로 쓰자.

또 var를 활용.... do...while,

function getStudents() {
    try {
        // not really a block scope
        var records = fromCache("students");
    }
    catch (err) {
        // oops, fall back to a default
        var records = [];
    }
    // ..
}
function getStudents() {
    var data = [];

    // do something with data
    // .. 50 more lines of code ..

    // purely an annotation to remind us
    var data;

    // use data again
    // ..
}

Are Synchronous Callbacks Still Closures?

  • Calling back to what (or where)?
  • Maybe "synchronous callback" isn't the best label
  • IIFE functions don't move around, why would they need closure?
  • Deferring over time is key to closure

What is a Callback?

(I)콜백 비동기 동기 잘 모름.

비동기 콜백은 나중에 호출되는 함수 참조로, 여기서의 콜백은 완료, 일시정지, 자체적으로 연기되었다가 나중에 호출 시 돌아가 재개한다는 의미다.

setTimeout(function waitForASecond(){
    // this is where JS should call back into
    // the program when the timer has elapsed
},1000);

// this is where the current program finishes
// or suspends

calling back이 다양한 의미로 쓰였지만 우선 특정 위치로 돌아간다는 의미로 콜백은 비동기다.

Synchronous Callback?

function getLabels(studentIDs) {
    return studentIDs.map(
        function formatIDLabel(id){
            return `Student ID: ${
               String(id).padStart(6)
            }`;
        }
    );
}

getLabels([ 14, 73, 112, 6 ]);
// [
//    "Student ID: 000014",
//    "Student ID: 000073",
//    "Student ID: 000112",
//    "Student ID: 000006"
// ]

프로그램이 일시정지되거나 종료된적이 없기 때문에 callback이 없다.
Dependency Injection (DI) or Inversion of Control (IoC)이라고 생각할 수 있겠다.
여기서 DI는 일을 완성하기 위해서 필수적인 부분을 다른 곳으로 옮기는 것(물려준다고 봐도 무방할 듯). map => formatIDLabel
IoC는 control 관점으로 본 것. formatDLabel의 호출 control을 map에 준 것.
이를 framework와 library의 차이점을 설명하기 위해 적용할 수 있는데.
라이브러리의 함수는 내가 호출하는 것. framework는 이것이 나의 함수를 호출.

필자의 제안. 동기 콜백을 inter-invoked functions (IIFs)라고 하자. 그럼 비동기 콜백은 비동기식으로 호출되는 IIF.

Synchronous Closure?

function printLabels(labels) {
    var list = document.getElementByID("labelsList");

    labels.forEach(
        function renderLabel(label){
            var li = document.createELement("li");
            li.innerText = label;
            list.appendChild(li);
        }
    );
}
function printLabels(labels) {
    var list = document.getElementByID("labelsList");

    for (let label of labels) {
        // just a normal function call in its own
        // scope, right? That's not really closure!
        renderLabel(label);
    }

    // **************

    function renderLabel(label) {
        var li = document.createELement("li");
        li.innerText = label;
        list.appendChild(li);
    }
}

당연히 아님.

Defer to Closure

function printLabels(labels) {
    var list = document.getElementByID("labelsList");
    var renderLabel = renderTo(list);//클로저 맞지.

    // definitely closure this time!
    labels.forEach( renderLabel );

    // **************

    function renderTo(list) {
        return function createLabel(label){
            var li = document.createELement("li");
            li.innerText = label;
            list.appendChild(li);
        };
    }
}

Classic Module Variations

var StudentList = (function defineModule(Student){
    var elems = [];

    var publicAPI = {
        renderList() {
            // ..
        }
    };

    return publicAPI;

})(Student);//계승으로 다른 모듈.

Where's My API?

대부분 클래식 모듈은 원래 publicAPI를 사용하지 않고 객체를 직접 반환.

Asynchronous Module Defintion (AMD)

AMD는 클래식 모듈의 다른 형태
RequireJS.

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

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

Universal Modules (UMD)

덜 구체적이고 정확한 format. 유사한 format들의 모음. 브라우저(AMD식 로더)나 노드에서 빌드 도구 변환없이 모듈에 대해 더 나은 interop을 만들 수 있도록 설계되어 있다. 그냥 IIFE.

(function UMD(name,context,definition){
    // loaded by an AMD-style loader?
    if (
        typeof define === "function" &&
        define.amd
    ) {
        define(definition);
    }
    // in Node?
    else if (
        typeof module !== "undefined" &&
        module.exports
    ) {
        module.exports = definition(name,context);
    }
    // assume standalone browser script
    else {
        context[name] = definition(name,context);
    }
})("StudentList",this,function DEF(name,context){

    var elems = [];

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

});

if..else를 통해 세가지 환경을 구분한다.

0개의 댓글

관련 채용 정보