본문 바로가기
Web/js

인사이드 자바스크립트

by ormgm 2020. 3. 10.

자바스크립트 핵심 개념

  • 객체 : null과 undefined를 제외한 모든 것은 객체로 다룰 수 있다.
  • 함수 : 일반적인 객체보다 조금 더 많은 기능이 있는 일급 객체
  • 프로토타입 : 숨겨진 링크로 해당 객체를 생성한 생성자의 프로토타입 객체를 가리킨다.
  • 실행 컨텐스트와 클로저 : 실행 컨텍스트는 자신만의 유효 범위를 가지고 이 과정에서 클로저를 구현할 수 있다.

자바스크립트 단점

  • 느슨한 타입 체크로 인한 런타임 오류
  • 전역 객체로 인한 이름 충돌의 위험성
  • 브라우저 독립적인 코드 구현의 어려움

자바스크립트 기본 타입

  • 숫자 
    • 모든 숫자를 64비트 부동소수점 형태로 저장, C언어의 double타입과 유사
    • 5/2 = 2.5가 결과값으로 출력됨 
    • Math.floor()을 사용해 소수 부분을 버릴 수 있음
  • 문자열
    • 배열 처럼 인덱스를 이용한 접근 가능
    • 한 번 생성된 문자열은 읽기만 가능하고 수정은 불가능함
  • 불린
  • null
    • 개발자가 명시적으로 값이 비어있음을 나타냄
    • typeof의 결과가 object이다. 따라서 일치 연산자(===)를 사용해서 변수의 값을 직접 확인해야 한다.
  • undefined
    • 변수에 아무런 값이 할당되지 않은 것으로 값이 비어있음을 나타냄 
    • 타입이자 값을 나타냄

자바스크립트 참조 타입(객체 타입)

  • 객체는 키:값 형태의 프로퍼티들을 저장하는 컨테이너로 해시라는 자료구조와 유사
  • 프로퍼티의 성질에 따라 객체의 프로퍼티는 함수로 포함할 수 있으며 이러한 프로퍼티를 메서드라고 한다.

객체 생성 방법

  • Object() 생성자 함수 이용
    • var foo = new Object(); 
    • foo.name = 'foo';
  • 객체 리터럴 방식 이용
    • 객체를 생성하는 표기법
    • var foo = { name : 'foo', }
    • 프로퍼티 값이 함수일 경우 이러한 프로퍼티를 메서드라고 함
  • 생성자 함수 이용

객체 프로퍼티 접근 방법

  • 대괄호( [ ] ) 표기법 : foo.['name'] 
    • 접근하려는 프로퍼티 이름을 문자열 형태로 만들어야한다.
    • 프로퍼티 이름을 문자열 형태로 만들지 않으면 모든 자바스크립트 객체에서 호출 가능한 toString()이라는 메서드를 자동으로 호출해서 이를 문자열로 바꾸려는 시도를 한다.
    • 접근하려는 프로퍼티가 표현식이거나 예약어일 경우 대괄호 표기법만을 사용해야함 (ex. full-name)
  • 마침표( . ) 표기법 : foo.name
    • 접근하려는 프로퍼티가 표현식이거나 예약어일 경우 사용하면 NaN값이 출력됨
    • NaN(Not a Number) : 수치 연산 시 정상적인 값을 얻지 못할 때 출력되는 값

for in 문을 이용한 객체 프로퍼티 출력

  • var prop; for (prop in foo) { console.log(prop, foo[prop]) }
  • prop 변수에 foo 객체의 프로퍼티가 하나씩 할당됨
  • prop에 할당된 프로퍼티 이름을 이용

delete를 이용한 객체 프로퍼티 삭제

  • delete foo.name; 을 이용해 프로퍼티를 삭제한 후 출력해보면 undefined 출력
  • delete foo; 를 이용해 객체 자체를 삭제하려고 하면 객체가 삭제되지 않는다. 
  • delete 연산자는 프로퍼티만을 삭제한다.

참조 타입

  • 객체의 모든 연산이 실제 값이 아닌 참조값으로 처리된다.
  • var a = {n : 40 }; var b = a; 
  • b.n = 50; a.n도 50으로 변경된다.
  • 객체 자체를 저장하고 있는 것이 아니라 생성된 객체를 가리키는 참조값을 저장하고 있다.

객체 비교

  • 기본 타입의 경우 동등 연산자로 비교시 값을 비교한다.
  • 참조 타입의 경우 참조 값을 비교한다.

함수 호출 방식

  • 기본 타입은 값에 의한 호출 방식으로 동작한다. 매개변수로 복사된 값이 전달된다.
  • 참조 타입은 참조에 의한 호출 방식으로 동작한다. 매개변수로 인자로 넘긴 객체의 참조값이 그대로 함수 내부로 전달된다.

프로토타입 

  • 자바스크립트의 모든 객체는 자신의 부모 역할을 하는 객체와 연결된다. ( 객체지향의 상속 개념과 같음 )
  • 부모 객체를 프로토타입 객체라고 한다.
  • 모든 객체는 자신의 프로토타입을 가리키는 [[Prototype]]이라는 숨겨진 프로퍼티를 가진다.
  • [[Prototype]] == _proto_ 
  • 모든 객체의 프로토타입은 자바스크립트의 룰에 따라 객체를 생성할 떄 결정된다. ( 프로토타입 체이닝 )
  • 객체 리터럴 방식으로 생성된 객체의 경우 Object.prototype 객체가 프로토타입 객체가 된다.
  • 부모 객체를 동적으로 바꿀 수도 있다.

배열

  • 배열 리터럴을 대괄호를 사용한다.
  • 각 요소의 값만을 포함하고 배열 내 위치 인덱스값을 넣어서 접근한다.
  • 값을 순차적으로 넣을 필요 없이 아무 인덱스 위치에나 값을 동적으로 추가할 수 있다.
  • 배열의 크기는 현재 배열의 인덱스 중 가장 큰 값을 기준으로 정해진다.

length 프로퍼티

  • 코드를 통해 명시적으로 값을 변경할 수도 있다.
  • length를 원래 값보다 작게 변경하면 변경한 값 이상의 인덱스에 속하는 값들이 실제로 삭제된다.
  • 배열 표준 메서드는 length 프로퍼티를 기반으로 동작한다.

배열과 객체

  • typeof의 연산 결과는 모두 object이다.
  • 객체는 Object.prototype 객체가 프로토타입이다.
  • 배열은 Array.prototype 객체가 프로토타입이고, Array.prototype의 프로토타입은 Object.prototype이다. 따라서 배열은 둘의 메서드 모두 사용 가능하다. ( 객체이름._proto_ 로 확인 가능 )

배열 프로퍼티 동적 생성

  • 객체처럼 동적으로 key: value 형태의 프로퍼티를 추가할 수 있다. 
  • 배열의 length 프로퍼티는 배열 원소의 가장 큰 인덱스가 변했을 경우만 변경된다.

배열 프로퍼티 열거와 요소 삭제

  • for in 문을 사용할 수 있지만 불필요한 프로퍼티가 출력될 수 있으므로 for문을 사용하는 것이 좋다.
  • delete 연산자를 이용해 삭제하면 해당 요소의 값을 undefined로 설정할 뿐 원소 자체를 삭제하지 않아 length는 그대로다.
  • 따라서 요소를 완전히 삭제할 경우 splice() 배열 메서드를 사용한다.
  • splice(start, deleteCount, item...)
  • splice(2, 1) ==> 인덱스가 2인 곳에서부터 1개를 삭제한다.
  • item : 삭제할 위치에 추가할 요소

Array() 생성자 함수

  • 배열 리터럴은 Array() 생성자 함수로 배열을 생성하는 과정을 단순화 시킨 것
  • 호출할 때 인자 개수에 따라 동작이 다름
  • 인자가 1개이고, 숫자인 경우 : 호출된 인자를 length로 갖는 빈 배열 생성
  • var foo = new Array(3); [undefined, undefined, undefined]
  • 그외의 경우 : 호출된 이자를 요소로 갖는 배열 생성
  • var bar = new Array(1, 2, 3); [1, 2, 3]

유사 배열 객체

  • length 프로퍼티를 가진 객체
  • 객체임에도 불구하고 표준 배열 메서드를 사용하는게 가능함
  • apply() 메서드를 사용하여 유사 배열 객체를 만들 수 있다.

기본 타입과 표준 메서드

  • 기본 타입 값들에 대해 객체 형태로 메서드를 호출할 경우 기본값이 메서드 처리 순간에 객체로 변환되어 각 타입별 표준 메서드를 호출하게 되고 호출이 끝나면 다시 기본값으로 복귀한다.
  • toExponential()은 표준 숫자형 메서드로 숫자를 지수 형태의 문자열로 변환한다. 인자로 받는 값은 소수점 아래 몇 번째 자리까지 표시할 것인지를 지정한다.
  • charAt()은 문자열에서 인자로 받은 위치에 있는 문자를 반환한다.

연산자

  • + 연산자는 더하기 연산과 문자열 연결 연산을 수행함 
  • 모두 숫자일 경우에만 더하기 연산 수행
  • typeof 연산자는 null과 배열은 object를 반환하고 함수는 function을 반환한다.
  • == (동등) 연산자는 비교하려는 피연산자의 타입이 다를 경우 타입 변환을 거친 다음 비교한다.
  • ===(일치) 연산자는 피연산자의 타입이 다를 경우 타입을 변경하지 않고 비교한다.
  • !! 연산자의 역할은 피연산자를 불린값으로 변환하는 것으로 객체는 빈 객체라도 true로 변환된다.

함수 선언 방법

  • 함수 선언문
  • 함수 표현식
  • Function() 생성자 함수

함수 리터럴

  • function (x, y) { return x+y; }
  • 함수명은 선택사항으로 자바스크립트에서 함수명이 없는 함수를 익명 함수라고 한다.
  • 매개변수의 타입은 기술하지 않는다.

함수 선언문 

  • 반드시 함수명이 정의되어 있어야한다.
  • 함수 선언문 형식으로 정의된 함수는 자바스크립트 엔진에 의해 함수 표현식 형태로 변경된다.
  • function add(a, b) { return a+b } => var add = function add(a, b) { return a+b }
  • 함수 끝에 세미콜론을 붙이지 않아도 된다.

함수 표현식 

  • 함수도 하나의 값처럼 취급되어 숫자나 문자열처럼 변수에 할당할 수 있다.
  • 함수 리터럴로 하나의 함수를 만들고 생성된 함수를 변수에 할당하여 함수를 생성하는 것이다.
  • 함수 이름은 선택 사항이며 보통 사용하지 않는다.
  • 변수는 함수의 참조값을 가진다. 따라서 외부 코드는 함수 변수를 통해 실제 함수를 호출하는 것이 가능하다.
  • 익명 함수를 이용한 함수 표현식을 익명 함수 표현식이라고 한다.
  • 함수 이름이 포함된 함수 표현식을 기명 함수 표현식이라고 한다.
  • 기명 함수 표현식인 경우 함수명으로 호출할 때 not defined 에러가 발생한다.
  • 함수명은 외부 코드에서 접근이 불가능 하다.
  • 함수 표현식에 사용된 함수명은 함수 내부에서 재귀적으로 호출하거나 디버거 등에서 함수를 구분할 때 사용된다.
  • 함수 끝에 세미콜론을 붙이는 것을 권장한다.

Function() 생성자 함수를 통한 함수 생성

  • new Function (arg1, arg2, ... argN, functionBody)
  • arg1, arg2, ..., argN : 매개변수
  • functionBody : 함수가 호출될 때 실행될 코드를 포함한 문자열
  • 자주 사용되지 않음

함수 호이스팅

  • 함수 선언문과 함수 리터럴를 통한 함수 생성은 함수 호이스팅이 발생한다.
  • 함수를 사용하기 전에 반드시 선언해야 한다는 규칙을 무시하므로 코드의 구조를 엉성하게 만들 수도 있다.
  • 따라서 함수 표현식을 권장한다.
  • 함수 호이스팅의 원인은 자바스크립트의 변수 생성과 초기화의 작업이 분리돼서 진행되기 때문이다.

함수 객체

  • 함수 생성시 함수 코드는 함수 객체의 [[Code]] 내부 프로퍼티에 자동으로 저장된다.
  • 일반 객체처럼 프로퍼티를 추가할 수 있다.
  • 특정 기능의 코드를 수행하고 일반 객체처럼 자신의 프로퍼티를 가질 수 있는 특별한 객체
  • 함수는 일반 객체처럼 값으로 취급된다.

함수의 가능한 동작

  • 리터럴에 의해 생성
  • 변수나 배열의 요소, 객체의 프로퍼티 등에 할당 가능
  • 함수의 인자로 전달 가능
  • 함수의 리턴값으로 리턴 가능
  • 동적으로 프로퍼티를 생성 및 할당 가능
  • 이러한 동작이 모두 가능한 객체를 일급 객체라고 하며 함수형 프로그래밍이 가능하다.

함수 객체 기본 프로퍼티

  • name : 함수 이름 (익명함수는 빈 문자열을 가짐)
  • caller : 자신을 호출한 함수
  • arguments : 함수를 호출할 때 전달된 인자값
  • _proto_ : Function Empty() { }
    • apply(thisArg, argArray)메서드
    • call(thisArg, [, arg1 [,arg2, ]]) 메서드 등을 가진다.
  • length : 함수 작성 시 정의한 인자 개수
  • prototype 
    • [[prototype]]과는 다르다.
    • [[prototype]]은 객체 입장에서 자신의 부모 역할을 하는 프로토타입 객체를 가리키는 반면 prototype은 이 함수가 생성자로 사용될 때 이 함수를 통해 생성된 객체의 부모 역할을 하는 프로토타입 객체를 가리킨다.
    • 함수 생성 시 함수 자신과 연결된 프로토타입 객체를 동시에 생성한다.
    • prototype 프로퍼티는 constructor 프로퍼티 하나만 있는 객체를 가리킨다. constructor 프로퍼티는 자신과 연결된 함수를 가리켜 서로를 참조하게 된다.
    • 즉 함수의 prototype 객체에는 constructor과 _proto_ 라는 두 개의 프로퍼티가 있다.

함수 형태

  • 콜백 함수
    • 익명 함수의 대표적인 용도
    • 명시적으로 호출하는 함수가 아님
    • 어떤 이벤트가 발생했거나 특정 시점에 도달했을 떄 시스템에서 호출되는 함수
    • 특정 함수의 인자로 넘겨서 코드 내부에서 호출되는 함수 또한 콜백 함수가 될 수 있음
    • 대표적으로 이벤트 핸들러 처리가 있다.
  • 즉시 실행 함수
    • 함수를 정의함과 동시에 바로 실행하는 함수
    • 함수 리터럴을 괄호로 둘러싼 후 (함수 이름이 있든 없든 상관없음 ) 함수가 바로 호출될 수 있게 () 괄호 쌍을 추가한다. 이때 괄호 안에 값을 추가해 인자를 넘길 수 있다.
    • 최초 한 번의 실행만을 필요로 하는 초기화 코드 부분 등에 사용
    • jQuery와 같은 자바스크립트 라이브러리나 프레임워크 소스들에서 사용 됨
      • 라이브러리 코드가 처음 로드되어 초기화할 때, 즉시 실행 함수 패턴이 많이 사용 됨
      • 자바스크립트의 변수 유효 범위 특성 때문
      • 즉시 실행 함수 내에 라이브러리 코드를 추가하면 라이브러리 내의 변수들은 함수 외부에서 접근할 수 없게 되어 전역 네임스페이스를 더럽히지 않음
      • 다른 자바스크립트 라이브러리들이 동시에 로드가 되더라도 라이브러리 간 변수 이름 충동 같은 문제가 방지 됨
  • 내부 함수
    • 함수 내부에 정의된 함수
    • 클로저를 생성하거나 부모 함수 코드에서 외부에서의 접근을 막고 독립적인 헬퍼 함수를 구현하는 용도 등으로 사용한다.
    • 내부 함수에서는 자신을 둘러싼 부모 함수의 변수에 접근이 가능하다. (스코프 체이닝)
    • 내부 함수는 일반적으로 자신이 정의된 부모 함수 내부에서만 호출이 가능하다. (함수 스코프)
    • 기본적으로 함수 밖에서는 함수 스코프 안에 선언된 모든 변수나 함수에 접근이 불가능하다.
    • 스코프 체이닝 때문에 함수 내부에서는 함수 밖에서 선언된 변수나 함수의 접근이 가능하다.
    • 부모 함수 스코프의 변수를 참조하는 함수를 클로저라고 한다.
  • 함수를 리턴하는 함수
    • 함수 자체가 값처럼 리턴 될 수 있기 때문에
    • 함수를 호출함과 동시에 다른 함수로 바꾸거나 자기 자신을 재정의하는 함수를 구현할 수 있다.

arguments 객체

  • 함수 호출 시 함수 형식에 맞춰 인자를 넘기지 않더라도 에러가 발생하지 않음 
  • 함수의 인자보다 적게 넘기면 넘겨지지 않은 인자에는 undefinde값이 할당되고, 정의된 인자보다 많게 인자를 넘기면 초과된 인수는 무시된다. 따라서 런타임 시에 호출된 인자의 개수를 확인하고 이에 따라 동작을 다르게 해줘야 할 경우가 있는데 이를 가능케 하는 것이 argument 객체이다.
  • 함수 호출 시 인수들과 함께 암묵적으로 arguments 객체가 함수 내부로 전달된다.
  • 함수 호출 시 넘긴 임자들이 배열 형태로 저장된 객체를 의미
  • 실제 배열이 아닌 유사 배열 객체 (length 프로퍼티를 가지는 객체)
  • 인자들( 배열 형태 ), length 프로퍼티( 호출 시 넘겨진 인자 개수 ), callee 프로퍼티( 현재 실행 중인 함수의 참조 값 )으로 구성 됨
  • 매개변수 개수가 정확하게 정해지지 않은 함수 구현 또는 전달된 인자 개수에 따라 서로 다른 처리를 해줘야 하는 함수를 개발 하는데 유용하게 사용됨

this 인자

  • arguments 객체와 함께 함수 내부로 암묵적으로 전달됨

객체의 메서드 호출할 때 this 바인딩

  • 메서드 호출 시 메서드 내부 코드에서 사용된 this는 해당 메서드를 호출한 객체로 바인딩됨

함수를 호출할 때 this 바인딩

  • 함수 호출 시 해당 함수 내부 코드에 사용된 this는 전역 객체( 브라우저 - window 객체 / Node.js - global 객체 )에 바인딩 된다.
  • 내부 함수의 this는 함수 호출 패턴 규칙에 따라 전역 객체에 바인딩된다. 이 한계를 극복하기 위해 부모 함수의 this를 내부 함수가 접근 가능한 다른 변수에 저장하는 방법을 사용한다.
  •  관례상 this 값을 저장하는 변수 이름을 that이라고 짓는다. 내부 함수에서는 that 변수로 부모 함수의 this가 가리키는 객체에 접근할 수 있다.
  • 자바스크립트는 this바인딩을 명시적으로 할 수 있도록 call과 apply 메서드를 제공한다.
  • 자바스크립트 라이브러리등은 bind라는 이름의 메서드를 통해 사용자가 원하는 객체를 this에 바인딩할 수 있는 기능을 제공한다.

생성자 함수 호출할 때 this 바인딩

  • 기존 함수에 new 연산자를 붙여서 호출하면 해당 함수는 생성자 함수로 동작한다. 
  • 생성자 함수 내부에서 this는 함수 호출 방식에서의 this 바인딩과는 다르게 동작한다.
  • 생성자 함수 동작 방식
    1. 빈 객체 생성 및 this에 빈 객체 바인딩
    2. 함수 코드 내부에서 this를 통한 프로퍼티 생성
    3. this로 바인딩된 새로 생성된 객체 리턴 

객체 리터럴 방식과 생성자 함수를 통한 객체 생성 방식의 차이 

  • 객체 리터럴 방식의 경우 자신의 프로토타입 객체가 Object(Object.prototype)이다.
  • 생성자 함수 방식의 경우 Person(Person.prototype)이다.
  • 이러한 차이는 객체 생성 규칙 때문으로 자신을 생성한 생성자 함수의 prototype 프로퍼티가 가리키는 객체를 자신의 프로토타입 객체로 설정하기 때문이다.

생성자 함수를 new를 붙이지 않고 호출할 경우 

  • 생성자 함수를 new 없이 호출할 경우 this가 window전역 객체에 바인딩 (일반 함수)
  • new를 붙여 호출할 경우 새로 생성되는 빈 객체가 this에 바인딩  (생성자 함수)
  • 일반 함수 호출 시 undefined가 리턴되고, 생성자 함수 호출 시 새로 생성된 객체가 리턴된다.
  • 생성자 함수로 사용할 함수는 첫 글자를 대문자로 표기하는 네이밍 규칙이 있다.
  • 강제로 인스턴스 생성하기
    • if(!(this instanceof arguments.callee)) { return new 생성자 함수 이름(arg); }

call(), apply 메서드 

  • this를 특정 객체에 명시적으로 바인딩시키는 방법
  • 모든 함수들의 부모 객체인 Function.prototype 객체의 메서드
  • function.apply(thisArg, argArray)
    • thisArg는 apply 메서드를 호출한 함수 내부에서 사용한 this에 바인딩할 객체를 가리킴
    • argArray는 함수 호출 시 넘길 인자들의 배열을 가리킴
    • 즉, 두 번쨰 인자인 배열을 자신을 호출한 함수의 인자로 사용하되, 이 함수 내부에서 사용된 this는 첫 번째 인자인 객체로 바인딩해서 함수를 호출하는 기능을 한다.
  • call 메서드는 apply 메서드와는 기능이 같고 넘겨받는 인자의 형식만 다름
  • apply 메서드의 본질적인 기능은 함수 호출 ( Person()이라는 함수가 있고 Person.apply() 를 호출하면 Person()함수를 호출 하는 것 )
  • arguments 객체와 같은 유사 배열 객체에서 배열 메서드를 사용할 수 있게 함
    • var args = Array.prototype.slice.apply(arguments);
    • Array.prototype.slice() 메서드를 호출하고, this는 arguments 객체로 바인딩
    • arguments 객체가 Array.prototype.slice() 메서드를 자신의 메서드인 것처럼 arguments.slice()와 같은 형태로 메서드를 호출하라는 것

함수 리턴

  • 자바스크립트 함수는 항상 리턴값을 반환한다.
  • 일반 함수나 메서드는 리턴값을 지정하지 않을 경우 undefined 값이 리턴된다.
  • 생성자 함수에서 리턴값을 지정하지 않을 경우 생성된 객체가 리턴된다.
    • 리턴값을 명시하면 해당 객체나 배열이 리턴된다.
    • 리턴값으로 넘긴 값이 객체가 아닌 불린, 숫자, 문자열의 경우 무시하고 this로 바인딩된 객체가 리턴된다.

 

프로토타입 의미

  • 자바스크립트는 프로토타입 기반의 객체지향 프로그래밍을 지원한다.
  • 모든 객체는 [[Prototype] 프로퍼티에 저장되는 암묵적 프로토타입 링크가 있다. 이는 자신의 부모인 프로토타입 객체를 가리킨다.
  • 모든 객체는 자신을 생성한 생성자 함수의 prototype 프로퍼티가 가리키는 프로토타입 객체를 자신의 부모 객체로 설정하는  [[prototype]]링크로 연결한다.

객체 리터럴 방식으로 생성된 객체의 프로토타입 체이닝

  • hasOwnProperty() 메서드는 이 메서드를 호출한 객체에 인자로 넘긴 문자열 이름의 프로퍼티나 메서드가 있는지 체크하는 자바스크립트 표준 API 함수다.
  • 프로토타입 체이닝은 특정 객체의 프로퍼티나 메서드에 접근하려고 할 때 해당 객체에 접근하려는 프로퍼티 또는 메서드가 없다면 [[Prototype]]링크를 따라 자신의 부모 역할을 하는 프로토타입 객체의 프로퍼티를 차례대로 검색하는 것이다.
  • 객체 리터럴 방식으로 생성된 객체의 부모 객체는 Object.prototype 객체이다.

생성자 함수로 생성된 객체의 프로토타입 체이닝

  • 생성자 함수의 부모객체는 생성자함수이름.prototype 객체가 된다. 그리고 생성자함수이름.prototype의 부모객체는 Object.prototype 객체이다.

프로토타입 체이닝의 종점

  • Object.prototype 객체는 프로토타입 체이닝의 종점이다.
  • 모든 자바스크립트 객체는 프로토타입 체이닝으로  Object.prototype 객체가 가진 프로퍼티와 메서드에 접근하고 서로 공유 가능하다.

기본 데이터 타입 확장

  • Object.prototype에 정의된 메서드들은 자바스크립트의 모든 객체들의 표준 메서드이다.
  • 표준 빌트인 프로토타입 객체에도 사용자가 직접 정의한 메서드들을 추가할 수 있다.

prototype 객체

  • 디폴트로 constructor 프로티만을 가진 객체
  • 일반 객체처럼 동적으로 프로퍼티를 추가/삭제하는 것이 가능
  • 프로토타입 메서드를 호출할 때 this는 호출한 객체에 바인딩됨
    • Person.prototype.getName() // Person.prototype 객체에 바인딩됨
    • kim.getName() // kim 객체에 바인딩됨
  • 디폴트 프로토타입을 다른 일반 객체로 변경 가능함
    • 이 특징을 이용해 객체지향 상속을 구현한다.
    • 생성자 함수의 프로토타입 객체가 변경되면 변경된 시점 이후에 생성된 객체들은 변경된 프로토타입 객체로 [[Prototype]] 링크를 연결한다.
    • 생성자 함수의 프로토타입이 변경되기 전에 생성된 객체들은 기존 프로토타입 객체로의 [[Prototype]]링크를 그대로 유지한다.
  • 프로토타입 체이닝은 객체의 프로퍼티 읽기나 메서드를 실행할 때만 동작한다.

실행 컨텍스트

  • 실행 가능한 코드를 형상황하고 구분하는 추상적인 개념
  • 실행 가능한 자바스크립트 코드 블록이 실행되는 환경
  • 실행 컨텍스트가 형성되는 경우
    • 전역 코드로 실행되는 코드
    • eval() 함수로 실행되는 코드
    • 함수 안의 코드를 실행할 경우
  • 현재 실행되는 컨텍스트에서 이 컨텍스트와 관련 없는 실행 코드가 실행되면, 새로운 컨텍스트가 생성되어 스택에 들어가고 제어권이 그 컨텍스트로 이동한다.

실행 컨텍스트 생성 과정

  • 활성 객체 생성
    • 실행 컨텍스트 생성 시 만들어짐
    • 앞으로 사용하게 될 매개변수나 사용자가 정의한 변수 및 객체를 저장한다.
    • 새로 만들어진 컨텍스트로 접근 가능
    • 사용자가 접근할 수는 없음
  • arguments 객체 생성
    • 앞서 만들어진 활성 객체는 arguments 프로퍼티로 이 arguments 객체를 참조한다.
  • 스코프 정보 생성
    • 현재 컨텍스트의 유효 범위를 나타냄
    • 현재 실행 중인 실행 컨텍스트 안에서 연결 리스트와 유사한 형식으로 만들어짐
    • 리스트로 현재 컨텍스트의 변수와 상위 실행 컨텍스트의 변수에도 접근이 가능
    • 이러한 리스트를 스코프 체인이라고 하고 [[scope]]프로퍼티로 참조됨
  • 변수 생성
    • 현재 실행 컨텍스트 내부에서 사용되는 지역 변수의 생성
    • 앞서 생성된 활성 객체가 변수 객체로 사용됨 ( 즉, 활성 객체 == 변수 객체 )
    • 이 과정에서는 변수나 내부 함수를 단지 메모리에 생성하고, 초기화는 각 변수나 함수에 해당하는 표현식이 실행되기 전까지는 이루어지지 않음
    • 표현식의 실행은 변수 객체 생성이 다 이루어진 후 시작됨
  • this 바인딩
    • this 키워드를 사용하는 값이 할당됨
    • this가 참조하는 객체가 없으면 전역 객체를 참조
  • 코드 실행
    • 전역 실행 컨텍스트는 arguments 객체가 없고, 전역 객체 하나만을 포함하는 스코프 체인이 있음
    • 전역 실행 컨텍스트는 변수를 초기화하고 이것의 내부 함수는 일반적인 탑 레벨의 함수로 선언됨
    • 전역 실행 컨텍스트에서는 변수 객체가 곧 전역 객체이다.
    • 전역적으로 선언된 함수와 변수가 전역 객체의 프로퍼티가 된다.
    • Node.js 에서는 var을 사용하지 않을 경우 전역 객체인 global에 들어가서 전역 객체를 오염시키는 원인이 됨

스코프 체인

  • 자바스크립트에서는 함수내의 블록(for, if)은 유효 범위가 없다. 오직 함수만이 유효 범위의 한 단위가 된다.
  • 유효 범위를 나타내는 스코프가 [[scope]]프로퍼티로 각 함수 객체 내에서 연결 이스트 형식으로 관리되는데 이를 스코프 체인이라고 한다.
  • 스코프 체인은 각 실행 컨텍스의 변수 객체가 구성 요소인 리스트와 같다.
  • 각각의 함수는 [[scope]] 프로퍼티로 자신이 생성된 실행 컨텍스트의 스코프 체인을 참조한다.
  • 함수가 실행되는 순간 실행 컨텍스트가 만들어지고, 이 실행 컨텍스트는 실행된 함수의 [[scope]] 프로퍼티를 기반으로 새로운 스코프 체인을 만든다.
  • 스코프 체인 = 현재 실행 컨텍스트의 변수 객체 + 상위 컨텍스트의 스코프 체인

전역 실행 컨텍스트의 스코프 체인

  • 먼저 전역 실행 컨텍스트가 생성되고 변수 객체가 만들어진다.
  • 전역 실행 컨텍스트 단 하나만 실행되고 있어 참조할 상위 컨텍스트가 없기 때문에 자신이 최상위에 위치하는 변수 객체가 된다.
  • 변수 객체의 스코프 체인은 자기 자신만을 가진다. 즉 [[scope]]는 변수 객체 자신을 가리킨다.

함수를 호출한 경우 생성되는 실행 컨텍스트의 스코프 체인

  • 함수가 실행되면 새로운 컨텍스트가 만들어진다.
  • 함수의 실행 컨텍스트의 스코프 체인은 실행된 함수의 [[scope]]프로퍼티를 그대로 복사하여 현재 생성된 변수 객체를 복사한 스코프 체인의 맨 앞에 추가된다.
  • 전역 객체 하나만을 가질 경우 함수의 실행 컨텍스트의 스코프 체인은 [함수 변수 객체 - 전역 객체] 가 된다.
  • 각 함수 객체가 처음 생성될 당시 실행 컨텍스트가 무엇인지 생각해야 한다.

클로저

  • 이미 생명 주기가 끝난 외부 함수의 변수를 참조하는 함수 
  • 이미 생명 주기가 끝난 외부 함수의 변수를 자유 변수라고 한다.
  • 자유 변수에 엮여있는 함수
  • 함수형 언어(함수를 일급 객체로 취급)에서 주요하게 사용되는 특성
  • 클로저는 스코프 체인에서 뒤쪽에 있는 객체에 자주 접근하므로 성능을 저하시키는 이유로 지목된다.

클로저의 활용 

  • 정해진 형식의 함수를 콜백해주는 라이브러리가 있을 경우, 그 정해진 형식과는 다른 형식의 사용자 정의 함수를 호출할 때 

클로저 활용 시 주의사항 

  • 클로저의 프로퍼티값이 쓰기 가능하므로 그 값이 여러 번 호출로 항상 변할 수 있음에 유의해야 한다.
  • 하나의 클로저가 여러 함수 객체의 스코프 체인에 들어가 있는 경우도 있다.
  • 루프 안에서 클로저를 활용할 때는 주의하자
    • setTimeout()이 루프 안에서 실행될 때 자유변수를 참조하는 시점이 자유 변수가 속한 함수의 실행이 종료된 이후이기 때문에 원하지 않는 값이 들어간다.

클래스

  • 클래스 안의 메서드를 정의할 때는 프로토타입 객체에 정의 한 후 new로 생성한 객체에서 접근할 수 있게하는 것이 좋다. ( 프로토타입 - 프로토타입 체인 )
  • Function.prototype.method = function(name,func) { if (!this.prototype[name]) this.prototype[name] = func; } } // 메서드 정의 시 사용

상속

  • 객체 프로토타입 체인을 이용한 상속의 종류
    • 클래스 기반 전통적인 상속 방식 흉내
    • 클래스 개념 없이 객체의 프로토타입으로 상속을 구현 - 프로토타입을 이용한 상속
  • 프로토타입을 이용한 상속
    • 프로토타입의 특성을 활용하여 상속을 구현
    • 객체 리터럴로 생성된 객체의 상속
    • ECMA5에서 Object.create() 함수로 제공됨
    • 부모 -> 자식
      • function create_object(o) { function F() {}; F.prototype = o; return new F;
    • 자신의 메서드 재정의 혹은 추가
      • jQuery.extend = jQuery.fn.extend = function(obj, prop) { if (!prop) { prop = obj; obj = this; } for (var i in prop) obj[i] = prop[i]; return obj; }
      • extend 함수 구현 시 대상이 객체일 경우 깊은 복사를 하는 것이 일반적이다. 함수 객체인 경우는 그대로 얕은 복사를 진행한다.
  • 클래스 기반의 상속
    • 프로토타입을 이용한 상속과 거의 비슷
    • 클래스 역할을 하는 함수로 상속 구현

캡슐화

  • this의 객체의 프로퍼티로 선언하면 외부에서 new 키워드로 생성한 객체로 접근할 수 있다. ( public )
  • var로 선언된 멤버들은 외부에서 접근이 불가능하다. ( private )
  • public 메서드가 클러저 역할을 하면서 private 멤버인 변수에 접근할 수 있다. ( 자바스크립트에서 할 수 있는 기본적인 정보 은닉 방법 )
  • 메서드가 담겨있는 객체를 반환하는 함수에서 접근하는 private 멤버가 객체나 배열이면 얕은 복사로 참조만을 반환한다. ( 사용자가 쉽게 변경 가능한 단점 )
    • 깊은 복사로 복사본을 만들어 반환하는 것이 좋다.
    • 사용자가 반환받은 객체는 해당 함수 객체의 프로토타입에 접근할 수 없다.
    • 따라서 객체가 아닌 함수를 반환하는 것이 좋다.
  • 즉시 실행 함수에서 반환되는 클로저를 활용하면 좋은 캡슐화가 된다.

 

 

'Web > js' 카테고리의 다른 글

JavaScript(2)  (0) 2020.02.06
JavaScript(1)  (0) 2020.02.04

댓글