[자바스크립트 중고급] 7. 논리적 정리

Speedwell🍀·2022년 2월 1일
0

프로퍼티 연동 방지

  • Object에 Object를 할당하면 프로퍼티 값이 연동됨
var origin {member: 100};
var dup = origin;
dup.member = 200;
log(origin.member);

// 200
  • 배열도 마찬가지로 연동됨
var origin = [1, 2, 3];
var dup = origin;
dup[1] = 200;
log(origin);

// [1, 200, 3]
  • 연동 방지: 프로퍼티 단위로 할당
var origin = {member: 100};
var dup = {};
for (var name in origin){
  dup[name] = origin[name];
};
dup.member = 200;
log(origin.member);
log(dup.member);

// 100
// 200

재귀 함수

  • Recursive Function

    • 함수 안에서 자신 함수를 호출하는 형태
  • 사용 사례

    • {name: {name: {name: value}}}
    • [[1, 2], [3, 4], [5, 6]]

재귀 함수 형태

var book = {
  member: {name: 100},
  point: {value: 200}
};
function show(param){
  for (var type in param){
    typeof param[type] === "object" ? show(param[type]) : log(type + ":", param[type]);
  }
};
show(book);

// name:100
// value:200
  1. show(book);

    • 마지막 줄에서 show(book)를 호출하면서 book 오브젝트를 파라미터 값으로 넘겨줌
  2. for (var type in param){...}

    • for-in으로 파라미터로 받은 오브젝트 전개
  3. typeof param[type] === "object" ? show(param[type]) : web.log(type + ":", param[type]);

  4. param[type] 타입이 "object"이면

    • show()를 호출. 자신을 호출하면서 param[type]을 넘겨줌
    • book["memeber"]이므로 {name: 100}이 넘어감
  5. param[type] 타입이 "object"가 아니면

    • member: {name: 100}에서 {name: 100}을 읽은 것이므로 값을 출력

정리 시간

** 데이터 형태 **
var member = {
	Jan : {item: {title: "JS북", amount: 100}, point: [10, 20, 30]},
    Feb: {item: {title: "JS북", amout: 200}, point: [40, 50, 60]}
}
  • 재귀 함수로 데이터를 출력하시오.

    • 오브젝트이면 프로퍼티 이름(title, amount)과 값을 출력하고
    • 배열이면 값([10, 20, 30])을 출력하고 값을 누적하기
  • 재귀 호출이 끝나면 누적한 값 출력하기


즉시 실행 함수

  • 함수 즉시 실행이란?
    • 엔진이 함수를 만났을 때
    • 자동으로 함수를 실행
    • 즉시에 실행하므로 즉시 실행 함수
(function(){
  log("JS북");
}());

// JS북
  • IIFE: Immediately Invoked Function Expression

  • (function(){...}()) 형태

    • 함수 이름이 없으므로 함수 선언문, 함수 표현식도 아님
    • 문법 에러가 발생하지 않음
    • 무명 함수, 익명 함수라고도 부름

함수 즉시 실행 과정

  • 표현식을 평가
    • 소괄호()는 그룹핑 연산자
var total = (1 + 2);
log(total);

// 3
  • 함수 이름 필요
    • 함수에 이름이 없으면 문법 에러
var value = function(){
  return 100;
};
log(value());

// 100
  • 함수 표현식 끝에 소괄호 작성
var value = function(){
  return 100;
}();
log(value);

// 100
  • 소괄호()에 함수 작성
var value = (function(){
  return 100;
}());
log(value);

// 100
  • 그룹핑 연산자에서 반환된 값이 할당되는 변수를 작성하지 않은 형태

  • })()처럼 소괄호를 끝에 작성 가능

(function(){
  log(100);
}());

// 100

/*
1. 그룹핑 연산자를 작성하지 않으면 함수 이름이 없으므로 문법 에러
2. 하지만, 그룹핑 연산자를 작성하면 표현식에 function을 작성한 것이므로 문법 에러가 발생하지 않음
즉, (1+2)에서 1+2 대신에 함수를 작성한 것
3. 표현식과 표현식 평가 결과는 평가 결과가 반환될 때까지 메모리에 저장하고 평가 결과를 반환하면 지워짐
4. (1+2)의 결과가 메모리에 저장된다면 매우 많은 메모리가 필요한 것
5. function(){}(); 코드로 만든 오브젝트도 메모리에 저장되지 않으며 실행 결과도 메모리에 저장되지 않음
6. 따라서 저장해야 할 것이 있다면 표현식 밖의 변수, 프로퍼티에 저장해야 함
7. 저장할 필요가 없는 1회성 코드이면서 엔진이 function 키워드를 만나는 시점에 즉시 실행해야 한다면
8. 그룹핑 연산자 안에 표현식으로 작성
9. 무명 함수는 그룹핑 연산자 안의 코드를 한 번만 사용할 때 사용. 주로 초기값을 설정할 때 사용
*/

클로저

  • Closure

    • function 오브젝트를 생성할 때 함수가 속한 스코프를 [[Scope]]에 설정하고
    • 함수가 호출되었을 때 [[Scope]]의 프로퍼티를 사용하는 메커니즘
  • [[Scope]]의 설정과 사용 방법을 이해하면 클로저는 단지 논리적인 설명


클로저 논리

실행 콘텍스트: {
	렉시컬 환경 컴포넌트: {
    	환경 레코드: {
        	선언적 환경 레코드: {}.
            오브젝트 환경 레코드: {}
		},
        외부 렉시컬 환경 참조: {}
	}
}
  • 실행 중인 function 오브젝트에

    • 작성한 변수, 함수를 선언적 환경 레코드에 설정
  • [[Scope]]의 변수, 함수를

    • 외부 렉시컬 환경 참조에 바인딩
    • 변수 이름으로 접근하여 값을 사용하거나 변경할 수 있음
    • 함수를 호출할 수 있음

클로저 논리 전개

function book(){
  var point = 100;
  var getPoint = function(param){
    point = point + param;
    return point;
  };
  return getPoint;
};
var obj = book();
log(obj(200));

// 300
  1. var obj = book();

    • book()을 호출하면 엔진은 아래 방법으로 처리
    • getPoint()의 클로저가 만들어짐

실행 준비 단계

  1. 실행 콘텍스트(EC) 생성

  2. 3개의 컴포넌트 생성

    • 렉시컬/변수 환경 컴포넌트, this 바인딩 컴포넌트
  3. function 오브젝트의 [[Scope]]를

    • 외부 렉시컬 환경 참조에 바인딩

<여기까지 모습>

실행 콘텍스트 = {
	렉시컬 환경 컴포넌트 = {
    	환경 레코드: {
        	선언적 환경 레코드: {},
            오브젝트 환경 레코드: {}
		},
        외부 렉시컬 환경 참조: {[[scope]]}
	},
    변수 환경 컴포넌트 = {Same}
    this 바인딩 컴포넌트: {}
}

초기화 및 실행 단계

  1. var point; var getPoint;

    • 변수 이름을 선언적 환경 레코드에 설정
  2. var point = 100;

    • 선언적 환경 레코드의 point에 100 할당
  3. var getPoint = function(param){코드};

    • function 오브젝트 생성
    • 스코프를 [[Scope]]에 바인딩
    • point:100이 [[Scope]]에 바인딩됨

getPoint 오브젝트 모습

렉시컬 환경 컴포넌트 = {
	환경 레코드: {
    	선언적 환경 레코드: {}.
	},
    외부 렉시컬 환경 참조: {
    	point: 100
	}
}
  1. return getPoint;

    • getPoint function 오브젝트 반환
  2. var obj = book();

    • return getPoint에서 반환한
    • getPoint function 오브젝트를 obj에 할당
  3. console.log(obj(200));

    • obj()를 호출하면 getPoint(200) 함수가 호출됨
    • 클로저와 관련된 부분만 추려보면 아래 처리를 하게 됨

클로저와 관련된 부분

  1. 실행 콘텍스트(EC) 생성

    • getPoint 오브젝트의 [[Scope]]를 외부 렉시컬 환경 참조에 바인딩
    • 파라미터 이름에 값을 매핑하고 결과를 선언적 환경 레코드에 설정

<여기까지 모습>

렉시컬 환경 컴포넌트 = {
	환경 레코드: {
    	선언적 환경 레코드: {
        	param: 200
		},
	},
    외부 렉시컬 환경 참조: {
    	point: 100
	}
}
  1. 함수 안의 코드 실행

  2. point = point + param;

    • point를 선언적 환경 레코드에서 식별자 해결

      • point가 없으므로 외부 렉시컬 환경 참조에서 식별자 해결
      • point가 있으며 값이 100
    • param을 선언적 환경 레코드에서 식별자 해결

      • param이 있으며 값이 200
    • 100과 200을 더해 외부 렉시컬 환경 참조의 point에 할당

  1. 변수가 선언적 환경 레코드에 없으며 외부 렉시컬 환경 참조에서 식별자 해결

15. 이것이 클로저 논리


클로저와 무명 함수

  • 무명 함수 안에 작성한 값, 함수는

    • 무명 함수가 끝나면 지워짐
    • 따라서 다시 사용하려면 저장 필요
    • 한편, 무명 함수는 저장하지 않으려는 의도로 사용
  • 클로저 활용

    • 클로저는 함수 밖 스코프의 변수와 함수를 사용할 수 있음
    • 변수는 외부에서 직접 접근할 수 없으므로 정보 보호
    • 무명 함수 안에서 클로저의 변수를 가진 함수를 반환하면 함수의 재사용과 정보 보호를 할 수 있음

var book = (function(){
  var point = 100;
  function getPoint(param){
    return point + param;
  };
  return getPoint;
}());
log(book(200));

// 300
  • function getPoint(param){...}

    • [[Scope]]에 스코프 설정
  • return getPoint;

    • 즉시 실행 함수에서 getPoint 함수 반환
    • book 변수에 할당
    • point 변수값을 사용할 수 있음
  • console.log(book(200));

    • 반환된 함수를 호출하면서 200을 파라미터 값으로 넘겨줌
  • function getPoint(param){
    return point + param;
    };

    • getPoint function 오브젝트의 [[Scope]]에

JS에서 클로저

  • 함수에서 함수 밖의 변수 사용은

    • JS의 기본 메커니즘
  • 논리적 근거는

    • 외부 렉시컬 환경 참조에 함수가 속한 스코프가 설정되기 때문
  • 클로저는 이를 나타내는 용어

    • 용어보다 논리적 구조 이해

0개의 댓글