express란?


  • express는 Node.js에서 가장 많이 사용되는 웹 서버 프레임 워크중 하나입니다. koa나 hapi같은 웹 서버 프레임워크도 있지만 express가 다운로드 수가 가장 많으며, 많은 사람이 사용하는 만큼 버그가 적고 기능 추가 및 유지보수가 가장 활발하게 일어나는 웹 서버 프레임 워크입니다.




express 시작


import express from "express";

const app = express();

app.set("port", process.env.PORT);

app.listen(app.get("port"), () => {});

  • express는 내부에 http 모듈이 내장되어 있기 때문에 위와같이 express 모듈을 import하고 express app 생성 후 listen 상태로 만들어서 웹 서버를 실행할 수 있습니다.




express 메서드


app.set(key, value)


  • app.set 메서드는 app에 key와 value를 저장할 수 있습니다. 그리고 나중에 app.get(key)을 통해서 저장했던 값을 가져올 수 있습니다.




app.get(주소, 라우터)


  • 라우터에 req,res,next를 인자로 받는 콜백 함수를 등록할 수 있으며, req에는 요청에 관한 정보가 들어있으며, res에는 응답에 관한 정보가 들어있습니다. next는 현재 콜백 함수가 최종 목적지가 아닐경우 next()를 호출하여 다음 middleware가 호출될 수 있도록 합니다.




GET, POST, PUT, PATCH, DELETE


  • express 메서드에는 HTTP 메서드와 맵핑된 메서드들이 있으며 이를 이용해서 요청된 HTTP 메서드에 따라서 get, post, put, patch, delete 등의 메서드가 호출됩니다.




app.use


  • app.use는 middleware를 등록하거나 라우터를 등록할 때 사용합니다.

  • 첫 번째 인자로 주소를 넣지 않는다면 해당 middleware는 모든 요청에서 실행됩니다.




middleware


  • express에서 middleware는 요청과 응답 중간에 위치하는 콜백 함수입니다. middleware를 통해서 요청과 응답을 조작하여 기능을 추가하거나 잘못된 요청을 걸러낼 수도 있습니다.

  • middleware에서 사용하는 인자는 (req, res, next) 입니다. next를 통해서 다음 미들웨어나 finaleware의 호출 여부를 결정짓습니다.

  • middleware는 app.use를 통해서만 등록할 수 있는 것은 아니며, get, post, put, delete 등의 http 메소드를 특정하여 해당 요청에서만 호출될 수 있도록 할 수 있습니다.




(err, req, res, next) middleware


  • 특정 middleware에서 error 가 발생되어 next 호출 시 error를 전달하였다면 (err, req, res, next)를 인자로 받는 middleware가 호출되어 에러를 핸들링 하도록 할 수 있습니다.




route


router
  .route("/abc")
  .get((req, res) => {})
  .post((req, res) => {});

  • route 메서드를 이용해서 동일한 Url로 요청한 메서드들을 가시성 있게 프로그래밍할 수 있습니다.




'Node.js' 카테고리의 다른 글

npm(node package manager)란?  (0) 2022.12.18
Node.js의 파일 시스템  (0) 2022.12.17
Node.js와 모듈  (0) 2022.12.11
Node.js란?  (0) 2022.12.11

클로저와 렉시컬 환경

 

const x = 1;

function outer() {
  const x = 10;
  const inner function () {
    console.log(x);
  };
  return inner;
}

const innerFunc = outer();
innerFunc();

 

  • outer 함수 실행 후 innerFunc를 실행하면 전역의 x가 아닌 outer 함수 내부에 있는 x 변수가 출력됩니다. 이처럼 외부 함수보다 중첩 함수가 더 오래 유지되는 경우 중첩 함수는 이미 생명 중기가 종료한 외부 함수의 변수를 참조할 수 있는데 이러한 중첩 함수를 클로저(closure)라고 부릅니다.

 

  • 함수는 평가되고 함수 객체가 생성될 때 현재 렉시컬 환경을 [[Environment]] 내부 슬롯으로 참조하고 있다가 해당 함수가 호출되어 실행 컨텍스트를 생성한 후 실행 컨텍스트의 외부 렉시컬 환경에 대한 참조값으로 사용한다. 이러한 메커니즘을 통해서 클로저가 외부 함수의 생명주기가 종료되어도 외부 렉시컬 환경에 대해 참조가 가능하며 가비지 컬렉터 또한 해당 렉시컬 환경을 컬렉팅 하지 않습니다.




클로저와 자바스크립트 최적화

 

사례 1

 

function foo() {
  const x = 1;
  const y = 2;

  function bar() {
    const z = 3;
    console.log(z);
  }

  return bar;
}

const bar = foo();
bar();

 

  • 위 코드를 본다면은 bar 중첩 함수가 return되어 호출되기 때문에 foo의 렉시컬 환경이 유지될것 같지만 모던 브라우저에서 실행한다면은 최적화를 통해 상위 렉시컬 환경인 foo의 렉시컬 환경을 기억하지 않습니다. 그래서 bar는 클로저라 할 수 없습니다.




사례 2

 

function foo() {
  const x = 1;

  function bar() {
    console.log(x);
  }

  bar();
}

foo();

 

  • 위 코드같은 경우는 bar는 중첩 함수이지만 foo 함수의 생명주기보다 짧기 때문에 클로저라고 하지 않습니다.




사례 3

 

function foo() {
  const x = 1;
  const y = 2;

  function bar() {
    console.log(x);
  }
  return bar;
}

const bar = foo();
bar();

 

  • 위 예제를 보면은 bar는 상위 스코프의 식별자를 참조하고 있으므로 클로저입니다. 위와 같이 중첩 함수가 상위 스코프의 식별자를 참조하고 있고 중첩 함수가 외부 함수보다 더 오래 유지되는 경우에만 클로저 라고 합니다.

 

  • 위 코드에서 x만을 클로저가 참조하고 있습니다. 이러한 경우 자바스크립트 엔진의 최적화를 통해서 y 변수는 제외하고 x만을 참조하여 기억합니다. x와 같이 클로저에 의해 참조되어 기억되는 변수를 자유 변수(free variable)이라고 합니다.




클로저 활용

 

예제 1

 

const counter = (function () {
  let number = 0;
  return
  {
    increment(){
      return ++number;
    },
    decrement(){
      return --number;
    }
  };
}());

console.log(counter.increment());
console.log(counter.decrement());

 

  • 위 코드는 클로저 메커니즘을 활용하여 number를 다른 외부에서 참조하지 못하도록하고 오로지 return된 객체의 메서드를 통해서만 값의 변경을 줄 수 있도록하여 코드의 안정성을 높일 수 있습니다.




예제 2

 

const Counter = function () {
  let number = 0;

  function Counter() {}

  Counter.prototype.increment = function () {
    return ++number;
  };

  Counter.prototype.decrement = function () {
    return --number;
  };

  return Counter;
};

const co = new Counter();
console.log(co.increment());

 

  • 생성자 함수를 생성할 때 외부에 공개하지 않을 데이터 만들 때 클로저 메커니즘을 활용할 수 있습니다.




예제 3

 

function makeCounter(aux) {
  let number = 0;

  return function () {
    return aux(number);
  };
}

function increment(number) {
  return ++number;
}

const func = makeCounter(increment);
func();

 

  • 클로저 메커니즘을 활용해서 위와 같은 프로그래밍 또한 가능합니다.




캡슐화와 정보은닉

 

const Player = (function () {
  let mAge = 0;

  function Player(name, age) {
    this.name = name;
    mAge = age;
  }

  Player.prototype.sayHi = function () {
    console.log(`name : ${this.name}, age : ${mAge}`);
  };

  return Player;
})();

const me1 = new Player("devhun1", 20);
me1.sayHi();
const me2 = new Player("devhun1", 30);
me2.sayHi();
me1.sayHi(); // mAge의 값이 30으로 바뀜

 

  • 클로저를 활용해서 정보 은닉성을 구현할 수 있지만, C/C++, Java 같은 언어에서 제공하는 완벽한 정보 은닉성을 구현할 수는 없습니다.




'Programming Language > JavaScript' 카테고리의 다른 글

ES6에서의 함수  (0) 2022.12.19
자바스크립트와 클래스  (0) 2022.12.15
자바스크립트의 실행 컨텍스트란?  (0) 2022.12.13
자바스크립트의 스코프란?  (5) 2022.12.12
this와 메서드 호출 관계  (0) 2022.12.10

자바스크립트의 소스코드 타입


소스코드 타입 해설
전역 코드 전역에 존재하는 소스코드를 말합니다. 전역에 정의된 함수, 클래스 등의 내부 코드는 포함되지 않습니다.
함수 코드 함수 내부에 존재하는 소스코드를 말합니다. 함수 내부에 중첩된 함수, 클래스 등의 내부 코드는 포함되지 않습니다.
eval 코드 빌트인 전역 함수인 eval 함수에 전달되어 실행되는 소스코드를 말합니다.
모듈 코드 모듈 내부에 존재하는 소스코드를 말합니다. 모듈 내부의 함수, 클래스 등의 내부 코드는 포함되지 않습니다.

  • ECMAScript 사양은 위와같이 소스코드를 4가지 타입으로 구분합니다.

  • 소스코드를 4가지 타입으로 구분하는 이유는 소스코드의 타입에 따라 실행 컨텍스트를 생성하는 과정과 관리 내용이 다르기 때문입니다.




소스코드 타입마다 실행 컨텍스트가 필요한 이유


전역 코드


  • 전역 코드는 전역 변수를 관리하기 위해 최상위 스코프인 전역 스코프를 생성해야합니다. 그리고 var로 선언된 전역 변수와 함수 선언문으로 정의된 전역 함수를 전역 객체의 프로퍼티와 메서드로 바인딩하고 참조하기 위해 전역 객체와 연결되어야 합니다. 이를 위해 전역 코드가 평가되면 전역 실행 컨텍스트가 생성됩니다.




함수 코드


  • 함수 코드는 지역 스코프를 생성하고 지역 변수, 매개변수, arguments 객체를 관리해야 합니다. 그리고 생성한 지역 스코프를 전역 스코프에 연결해야 하며, 이를 위해 함수 코드가 평가되면 함수 실행 컨텍스트가 생성됩니다.




eval 코드


  • eval 코드는 strict mode(엄격 모드)에서 자신만의 독자적인 스코프를 생성합니다. 이를 위해 eval 코드가 평가되면 eval 실행 컨텍스트가 생성됩니다.




모듈 코드


  • 모듈 코드는 모듈별로 독립적인 모듈 스코프를 생성합니다. 이를 위해 모듈 코드가 평가되면 모듈 실행 컨텍스트가 생성됩니다.




소스코드 평가와 실행


  • 모든 소스코드는 실행에 앞서 평가 과정을 거치며 코드를 실행하기 위한 준비를 합니다. 즉, 자바스크립트 엔진은 "소스코드 평가", "소스코드 실행" 2개의 과정으로 나누어 처리합니다.




소스코드 평가


  • 소스코드 평가 과정에서는 실행 컨텍스트를 생성하고 변수, 함수 등의 선언문만 먼저 실행하여 생성된 변수나 함수 식별자를 키로 실행 컨텍스트가 관리하는 스코프(렉시컬 환경의 환경 레코드)에 등록합니다.




소스코드 실행


  • 소스코드 평가 과정이 끝나면 선언문을 제외한 소스코드가 순차적으로 실행되는 런타임이 시작됩니다. 소스코드를 실행하면서 필요한 변수, 함수의 참조를 실행 컨텍스트가 관리하는 스코프에서 취득합니다. 그리고 변수 값의 변경 등 소스코드의 실행 결과는 다시 실행 컨텍스트가 관리하는 스코프(렉시컬 환경의 환경 레코드)에 등록합니다.




실행 컨텍스트의 역할


const x = 1;
const y = 2;

function test(a) {
  const x = 10;
  const y = 20;
  console.log(a + x + y);
}

sayHello(50);
console.log(x + y);

  • 자바스크립트 엔진이 아래 코드를 평가하고 실행하는 방식은 아래와 같습니다.

1. 전역 코드 평가


  • 전역 코드 평가를 통해 변수 선언문과 함수 선언문이 실행되고, 그 결과 생성된 전역 변수와 전역 함수가 실행 컨텍스트가 관리하는 전역 렉시컬 환경의 환경 레코드에 등록됩니다. 이 때 var 키워드로 선언된 전역 변수와 함수 선언문으로 정의된 전역 함수는 전역 객체의 프로퍼티와 메서드가 됩니다.



2. 전역 코드 실행


  • 전역 코드 평가 과정이 끝나면 런타임이 시작되어 전역 코드가 순차적으로 실행됩니다. 이 떄 전역 변수에 값이 할당되고 함수가 호출됩니다. 함수가 호출되면 순차적으로 실행되던 전역 코드의 실행을 일시 중단하고 코드 실행 순서를 변경하여 함수 내부로 진입합니다.



3. 함수 코드 평가


  • 함수 호출에 의해 함수 내부로 진입하면 함수 내부의 문들을 실행하기에 앞서 함수 코드 평가 과정을 거치며 함수 코드를 실행하기 위한 준비를 합니다. 이 때 지역 실행 컨텍스트가 생성되고 매개변수와 지역 변수 선언문이 실행되어 지역 실행 컨텍스트의 환경 레코드에등록됩니다. 또한 함수 내부에서 지역 변수처럼 사용할 수 있는 arguments 객체가 생성되어 지역 실행 컨텍스트의 환경 레코드에 등록되고 this 바인딩도 결정됩니다.



4. 함수 코드 실행


  • 함수 코드 평가 과정이 끝나면 런타임이 시작되어 함수 코드가 순차적으로 실행되기 시작합니다. 이 때 매개변수와 지역 변수에 값이 할당되면서 코드가 실행됩니다. console.log() 함수를 실행할 때 console은 스코프 체인을 통해서 전역 스코프에서 참조하며 .log는 프로토타입 체인으로 검색합니다. 그리고 a,x,y 식별자는 스코프 체인을 통해 검색하여 console.log() 함수를 실행 후 함수 코드를 모두 실행하였다면 다시 전역 코드로 돌아가서 이후 코드를 마저 실행합니다.



최종 분석


  • 위와 같은 메커니즘을 구성하기 위해서는 실행 컨텍스트는 실별자와 스코프를 렉시컬 환경으로 관리하고 코드 실행 순서는 실행 컨텍스트 스택으로 관리합니다.




실행 컨텍스트 스택

  • 자바스크립트 엔진은 가장 먼저 전역 코드를 평가하고 전역 실행 컨텍스트를 생성하여 실행 컨텍스트 스택에 push하고 이후 코드를 실행하면서 함수를 호출할 경우 지역 함수를 평가 및 실행 컨텍스트를 생성하고 실행 컨텍스트 스택에 push 하고 모두 실행했다면 pop을 합니다. 이러한 메커니즘을 통해서 코드의 실행 순서를 관리합니다. 실행 컨텍스트 스택에 가장 최상위에 존재하는 실행 컨텍스트를 실행 중인 실행 컨텍스트라 부릅니다.




렉시컬 환경


  • 렉시컬 환경은 식별자와 식별자에 바인딩된 값, 그리고 상위 스코프에 대한 참조를 기록하는 자료구조로 실행 컨텍스트를 구성하는 컴포넌트입니다. 실행 컨텍스트 스택이 코드의 실행 순서를 관리한다면 렉시컬 환경은 스코프와 식별자를 관리합니다.

  • 렉시컬 환경은 환경 레코드외부 렉시컬 환경에 대한 참조로 구성됩니다.




환경 레코드(Environment Record)


  • 스코프에 포함된 식별자를 등록하고 등록된 식별자에 바인딩된 값을 관리하는 저장소입니다. 환경 레코드는 소스코드의 타입에 따라 관리하는 내용의 차이가 있습니다.




외부 렉시컬 환경에 대한 참조(Outer Lexical Environment Reference)


  • 외부 렉시컬 환경에 대한 참조는 상위 스코프를 가리킵니다. 외부 렉시컬 환경에 대한 참조를 통해 단방향 링크드 리스트 자료구조의 스코프 체인을 구현합니다.




실행 컨텍스트 생성과 식별자 검색 과정


1. 전역 객체 생성


  • 전역 객체는 전역 코드가 평가되기 이전에 생성됩니다. 이때 전역 객체에는 빌트인 전역 프로퍼티와 빌트인 전역 함수, 그리고 표준 빌트인 객체가 추가됩니다.



2. 전역 코드 평가


  • 소스코드가 로드되면 자바스크립트 엔진은 전역 코드를 평가합니다. 전역 코드 평가는 다음과 같은 순서로 진행됩니다.



2.1 전역 실행 컨텍스트 생성


  • 전역 실행 컨텍스트를 생성하여 실행 컨텍스트 스택에 푸시합니다.



2.2 전역 렉시컬 환경 생성


  • 전역 렉시컬 환경을 생성하고 전역 실행 컨텍스트에 바인딩합니다.(렉시컬 환경은 환경 레코드와 외부 렉시컬 환경에 대한 참조로 구성됩니다.)

  • ES6 이후부터는 전역 환경 레코드는 기존의 전역 객체가 관리하던 var 전역 변수와 전역 함수 선언문으로 정의한 함수, 빌트인 전역 프로퍼티와 빌트인 전역 함수, 표준 빌트인 객체를 관리하는 객체 환경 레코드와 const와 let을 관리하는 선언적 환경 레코드와 this가 바인딩된 [[GlobalThisValue]] 내부슬롯으로 구성됩니다.

  • var 전역 변수와 다르게 전역 함수 선언문은 소스코드 평가 과정에서 객체 환경 레코드에 식별자가 등록되고 함수 객체가 생성되어 등록됩니다. 이러한 메커니즘으로 인해서 함수 선언문은 함수 선언문 이전에 호출할 수 있으며 변수 호이스팅과 함수 호이스팅의 차이점입니다.

  • 선언적 환경 레코드는 let과 const 변수를 관리하는 환경 레코드로서 변수 호이스팅은 발생되지만 var 키워드와 달리 undefined로 초기화하지 않고 를 할당하여 변수 할당문 이전까지는 일시적 사각지대로서 해당 변수를 참조하면 참조 에러가 발생됩니다.

  • 외부 렉시컬 환경을 통해 상위 렉시컬 환경에 접근할 수 있습니다. 전역 렉시컬 환경의 외부 렉시컬은 null이며 스코프 체인의 종점을 의미합니다.




3. 전역 코드 실행


  • 위의 과정이 끝났다면 전역 코드를 순차적으로 실행합니다. 전역 코드가 실행되어 변수에 접근할 때 전역 실행 컨텍스트의 환경 레코드를 통해 참조합니다. 실행 컨텍스트에서 접근할 식별자를 결정하는 과정을 식별자 결정이라고 합니다. 만약, 실행 컨텍스트의 환경 레코드에 변수를 찾지 못했다면 상위 스코프의 실행 컨텍스트의 환경 레코드에 접근해야 합니다. 하지만, 전역 실행 컨텍스트는 스코프 체인의 종점이기 때문에 여기서 찾지 못했다면 참조 에러가 발생됩니다.




4. 함수 코드 평가


  • 전역 코드 실행 중 함수를 실행하게 될 경우 해당 함수로 코드의 제어권이 이동합니다. 그리고 함수 코드 평가를 시작합니다.

  • 함수 코드 평가는 함수 '실행 컨텍스트 생성' -> '함수 렉시컬 환경 생성' -> '함수 환경 레코드 생성' -> 'this 바인딩' -> '외부 렉시컬 환경에 대한 참조 결정' 순으로 평가합니다.

  • 함수 실행 컨텍스트를 생성 합니다. 그리고 함수 렉시컬 환경을 생성하면 함수 실행 컨텍스트에 바인딩하여 실행 컨텍스트 스택에 push 합니다.

  • 함수 렉시컬 환경의 환경 레코드에는 매개변수, arguments 객체, 지역 변수와 중첩 함수를 등록하고 관리합니다.

  • 함수가 일반 함수로 호출되었을 때 [[ThisValue]] 내부 슬롯에 전역 객체가 바인딩되며, this를 호출하였을 때 전역 객체를 가리킵니다.

  • 전역 선언문 함수의 외부 렉시컬 환경에 대한 참조는 함수 선언문이 평가된 시점에 결정됩니다. 자바스크립트는 함수를 어디서 호출했는지가 아니라 함수를 어디에 정의했는지에 따라 상위 스코프를 결정합니다. 이러한 메커니즘은 함수 객체를 생성할 때 함수 객체의 [[Environment]] 내부 슬롯의 현재 실행 컨텍스트에 대한 참조를 저장하는 방식으로 구현됩니다. 즉, 해당 함수가 호출되어 실행 컨텍스트를 생성하고 렉시컬 환경의 외부 렉시컬 환경에 대한 참조 값을 [[Environment]] 내부 슬롯에 있는 값으로 셋팅합니다.




실행 컨텍스트와 렉시컬 환경 반환


  • 실행 컨텍스트가 실행 완료되어 실행 컨텍스트 스택에서 pop 되어도 실행 컨텍스트의 렉시컬 환경은 가비지 컬렉팅 대상이 되지 않을 수 있습니다. 렉시컬 환경은 독립적인 객체로서 참조 여부에 따라서 가비지 컬렉팅 대상이 됩니다.




실행 컨텍스트와 블록 레벨 스코프


  • var 키워드는 함수 레벨 스코프만을 따르지만, let과 const는 모든 코드 블록을 지역 스코프로 인정합니다. 만약, 코드 실행 과정에서 if문과 같은 블록을 만날 경우 선언적 환경 레코드를 가지는 렉시컬 환경을 생성합니다. 그리고 외부 렉시컬 환경에 대한 참조는 if문이 실행되기 이전의 렉시컬 환경을 참조합니다.




'Programming Language > JavaScript' 카테고리의 다른 글

자바스크립트와 클래스  (0) 2022.12.15
자바스크립트의 클로저(Closure)란?  (0) 2022.12.14
자바스크립트의 스코프란?  (5) 2022.12.12
this와 메서드 호출 관계  (0) 2022.12.10
빌트인 객체란?  (0) 2022.12.10

스코프(Scope)란?

 

  • 스코프(Scope)는 모든 식별자가 선언된 위치에 의해 해당 식별자의 유효 범위가 결정되는데 이 유효범위를 스코프(Scope)라고합니다.




식별자 결정

 

const number = 15;

function PrintNumber() {
  const number = 20;
  console.log(number);
}

console.log(number);
PrintNumber();

 

  • 자바 스크립트 엔진은 console.log(number);를 실행할 때와 PrintNumber() 함수를 실행할 때 식별자 이름이 같은 전역, 지역 number 변수중 어떤것을 참조해야 할것인지를 결정해야 합니다. 이를 식별자 결정이라고 합니다. 또한 자바스크립트는 스코프를 통해 어떤 변수를 참조해야할 것인지를 결정합니다. 따라서 스코프란 자바스크립트 엔진이 식별자를 검색할 때 사용하는 규칙이라고 할 수 있습니다.




코드 문맥과 환경

 

  • "코드가 어디서 실행되며 주변에 어떤 코드가 있는지"를 렉시컬 환경(lexical environment)이라고 부릅니다. 즉, 코드의 문맥은 렉시컬 환경으로 이뤄집니다. 이를 구현한 것이 "실행 컨텍스트(execution conext)"이며, 모든 코드는 실행 컨텍스트에서 평가되고 실행됩니다.




스코프의 종류

 

구분 설명 스코프 변수
전역 코드의 가장 바깥 영역 전역 스코프 전역 변수
지역 함수 몸체 내부 지역 스코프 지역 변수

 

  • 코드는 전역과 지역으로 구분할 수 있습니다. 변수는 선언된 위치에 의해 자신이 유효한 스코프가 결정됩니다. 즉, 전역에 선언된 변수는 전역 스코프를 갖고, 함수 몸체 내부에 선언된 변수는 지역 스코프를 갖습니다.




스코프 체인

 

let x = "global x";

function outer() {
  let x = "outer x";

  function inner() {
    let x = "inner x";
  }
}

 

  • 위 코드를 보면 함수 내부에 함수가 중첩되어 있는 구조입니다. outer 함수는 inner 함수의 상위 스코프이며, 전역은 outer 함수의 상위 스코프입니다. 이러한 계층적 구조를 통해서 자바스크립트 엔진은 현재 스코프에서 부터 참조할 변수를 상위 스코프로 이동하며 검색합니다. 이를 통해서 상위 스코프에서 선언한 변수를 하위 스코프에서 참조할 수 있습니다.

 

  • 스코프 체인은 물리적인 실체로 존재합니다. 자바스크립트 엔진은 코드를 실행하기에 앞서 렉시컹 환경을 실제로 생성합니다. 변수 선언이 실행되면 변수 식별자가 렉시켠 환경에 키로 등록되고, 변수 할당이 일어나면 이 자료구조의 변수 식별자에 해당하는 값을 변경합니다.




렉시컬 환경(Lexical Environment)

 

  • 스코프 체인은 실행 컨텍스트의 렉시컬 환경을 단방향으로 연결한 것입니다. 전역 렉시컬 환경은 코드가 로드되면 곧바로 생성되고 렉시컬 환경은 함수가 호출되면 곧바로 생성됩니다.




렉시컬 스코프(Lexical Scope)

 

  • 렉시컬 스코프는 정적 스코프라고도 불립니다. 자바스크립트를 비롯한 대부분의 프로그래밍 언어는 렉시컬 스코프라고 합니다. 렉시컬 스코프는 함수를 어디서 호출했는지가 아니라 어디서 정의했는지에 따라 결정됩니다.




'Programming Language > JavaScript' 카테고리의 다른 글

자바스크립트의 클로저(Closure)란?  (0) 2022.12.14
자바스크립트의 실행 컨텍스트란?  (0) 2022.12.13
this와 메서드 호출 관계  (0) 2022.12.10
빌트인 객체란?  (0) 2022.12.10
프로토타입이란?  (0) 2022.12.08

Node.js와 모듈

 

// var.js 파일

const odd = "홀수";
const even = "짝수";

module.exports = {
  odd,
  even,
};

 

// func.js 파일

const { odd, even } = require("./var");

function checkOddOrEven(num) {
  if (num % 2) {
    return odd;
  } else {
    return even;
  }
}

module.exports = checkOddOrEven;

 

  • 노드는 파일안의 코드를 모듈로 만들 수 있다는 점에서 브라우저의 자바스크립트와 다릅니다.( 모듈이란 특정한 기능을 하는 함수나 변수들의 집합입니다. )




ES6에서의 모듈

// var.js 파일

export const odd = "홀수";
export const even = "짝수";

 

// func.js 파일

import { odd, even } = from "./var";

function checkOddOrEven(num) {
  if (num % 2) {
    return odd;
  } else {
    return even;
  }
}

export default checkOddOrEven;

 

  • ES6 부터는 require, modules를 사용하지 않고 export, import & from, default를 사용하여 모듈을 가져오거나 내보낼 수 있습니다.

 

  • export는 내보낼 코드의 타입 앞에 적어주면되고, import, import & from은 require 대신에 사용합니다. 그리고 default는 모듈 내에서 하나의 값에 대해서 사용할 수 있으며 import 하는 쪽에서 원하는 이름으로 import 할 수 있습니다.




'Node.js' 카테고리의 다른 글

express 프레임 워크란?  (0) 2022.12.18
npm(node package manager)란?  (0) 2022.12.18
Node.js의 파일 시스템  (0) 2022.12.17
Node.js란?  (0) 2022.12.11

Node.js란?

 

  • Node.js는 Javascript 런타임( 런타임은 특정 언어로 만든 프로그램을 실행할 수 있는 환경을 뜻합니다. )입니다. 따라서 노드는 Javascript 프로그램을 브라우저가 아닌 환경에서 실행할 수 있도록 도와주는 런타임입니다.




Node.js와 V8엔진

 

  • 과거에는 브라우저에서만 Javascript를 실행할 수 있었으며, 브라우저 외의 환경에서 실행하고 싶어도 성능의 문제 때문에 어려움이 있었습니다. 하지만, 2008년 구글의 V8 엔진이 공개되고 V8 엔진의 높은 성능 덕분에 라이언 달( Ryan Dahl )이 2009년 V8 엔진 기반의 노드 프로젝트를 시작하여 현재의 Node.js를 완성할 수 있었습니다.




이벤트 기반, 논 블로킹 I/O 모델

 

  • Node.js는 V8과 더불어 libuv라는 라이브러리를 사용합니다. V8과 libuv는 C/C++로 구현되어 있으며 우리가 작성한 Javascript코드를 Node.js가 알아서 V8과 libuv에 연결해줍니다. libuv는 노드의 특성인 이벤트 기반, 논 블로킹 I/O 모델을 구현하고 있습니다.




이벤트 기반( event-driven )

 

  • 이벤트 기반이란 이벤트가 발생할 때 미리 지정해둔 작업을 수행하는 방식을 의미합니다. 즉, 클릭이 발생했을 때 이에 대응하는 콜백 함수가 실행될 수 있도록 지정하는 것을 말합니다. 이벤트 기반 모델에서는 이벤트 루프( event loop ), 백그라운드( background ), 테스크 큐( task queue )라는 개념이 있습니다.




이벤트 루프( event loop )

 

  • 이벤트 발생 시 호출할 콜백 함수들을 관리하고 호출된 콜백 함수의 실행 순서를 결정하는 역할을 담당합니다. 노드가 종료될 때까지 이벤트 처리를 위한 작업을 반복하므로 루프( loop )라고 합니다.




백그라운드( background )

 

  • setTimeout과 같은 타이머나 이벤트 리스너들이 대기하는 곳입니다. 자바스크립트가 아닌 다른 언어로 작성된 프로그램이라고 봐도 됩니다. 여러 작업이 동시에 실행될 수 있습니다.




테스크 큐( task queue )

 

  • 이벤트 발생 후 백그라운드에서는 태스크 큐로 타이머나 이벤트 리스너의 콜백 함수를 보냅니다. 테스크 큐는 여러 개로 이루어져 있으며 정해진 순서대로 콜백들이 줄을 서 있으므로 콜백 큐라고도 부릅니다. 콜백들은 보통 완료된 순서대로 줄을 서 있지만 특정한 경우에는 순서가 바뀌기도 합니다.




이벤트 처리 과정

 

function run() {
  console.log("3초 후 실행");
}

setTimeout(run, 3000);

 

  1. anonymous가 호출되고 setTimeout()을 호출 후 역순으로 호출스택에서 제거됩니다.
  2. 백그라운드에서는 3초를 센 후 run 함수를 테스크 큐로 보냅니다.
  3. 이벤트 루프가 호출스택이 비어있다는 것을 확인하고 테스크 큐에서 run 함수를 호출스택에 넣고 실행합니다.( 이벤트 루프는 호출 스택이 비어있을 때만 테스크 큐에 있는 함수를 호출 스택으로 가져옵니다. )




Node.js와 논 블로킹 I/O

 

  • Node.js는 non-blocking I/O를 이용해서 I/O 작업을 백그라운드로 넘겨 동시에 처리합니다.




Node.js와 싱글 쓰레드

 

  • 노드는 싱글 쓰레드라고 불리지만 엄밀히 따지면 싱글 쓰레드가 아닙니다. 노드를 실행하면 여러 쓰레드를 생성합니다. 다만, 노드 개발자가 제어할 수 있는 쓰레드는 하나뿐입니다.( 노드 12버전부터는 노드에서 멀티 쓰레드를 사용할 수 있습니다. )




'Node.js' 카테고리의 다른 글

express 프레임 워크란?  (0) 2022.12.18
npm(node package manager)란?  (0) 2022.12.18
Node.js의 파일 시스템  (0) 2022.12.17
Node.js와 모듈  (0) 2022.12.11

표준 빌트인 객체

 

  • 표준 빌트인 객체는 ECMAScript 사양에 정의된 객체를 말하며, 애플리케이션 전역의 공통 기능을 제공합니다. 표준 빌트인 객체는 ECMAScript 사양에 정의된 객체이므로 실행 환경과 관계없이 언제나 사용할 수 있습니다.




호스트 객체

 

  • 호스트 객체는 ECMAScript 사양에 정의되어 있지 않지만 자바스크립트 실행 환경에 따라 추가로 제공하는 객체를 말합니다. 브라우저 환경에서는 DOM, BOM, Canvas, XMLHTTPRequest, fetch와 같은 클라이언트 사이드 Web API 호스트 객체로 제공하고, Node.js 환경에서는 Node.js 고유의 API 호스트 객체로 제공합니다.




사용자 정의 객체

 

  • 사용자 정의 객체는 표준 빌트인 객체와 호스트 객체처럼 기본 제공되는 객체가 아닌 사용자가 직접 정의한 객체를 말합니다.




원시값과 래퍼 객체

 

const str = "devhun";

// 원시 타입이지만 프로퍼티와 메서드를 사용할 수 있다.
console.log(str.length);
console.log(str.toUpperCase());

 

  • 위 코드에서 원시값은 객체가 아니기 때문에 프로퍼티나 메서드를 가질 수 없는데도 원시값인 문자열이 마치 객체처럼 동작합니다. 이는 문자열, 숫자, 불리언 값의 경우 자바스크립트 엔진이 일시적으로 객체로 변환해주기 때문입니다. 그리고 접근이 끝난 후 다시 원시값으로 되돌리는데 이러한 객체를 래퍼 객체(wrapper object)라고 합니다.




래퍼 객체 생성과정

 

// 1번
const str = "Hello";

// 2번
str.name = "devhun";

// 3번
console.log(str.name); // undefined 출력

// 4번
console.log(typeof str, str); // string Hello 출력

 

  1. 식별자 str은 문자열을 값으로 가지고 있습니다.
  2. str은 암묵적으로 생성된 래퍼 객체를 가리킨다. 그리고 "Hello" 원시 값은 래퍼 객체의 [[StringData]] 내부 슬롯에 할당됩니다.
  3. str에는 [[StringData]] 내부 슬롯에 할당된 원시값으로 할당되며, 래퍼 객체를 참조하는 식별자가 없기 때문에 가비지 컬렉션의 대상이 됩니다.
  4. str은 새롭게 생성된 래퍼 객체를 가리키며, 새롭게 생성된 래퍼 객체는 name 프로퍼티를 가지고 있지 않습니다.
  5. str은 다시 래퍼 객체를 가리키며 string을 출력하고 이후 다시 가리키는 값이 원시 값으로 바뀌면서 가비지 컬렉션의 대상이 됩니다.




전역 객체

 

  • 전역 객체는 코드가 실행되기 이전 단계에 자바스크립트 엔진에 의해 어떤 객체보다도 먼저 생성되는 특수한 객체입니다. 환경에 따라 전역 객체는 다르며 브라우저 환경에서는 window, Node.js 환경에서는 global 입니다.




전역 객체의 특징

 

  • 전역 객체는 생성자가 제공되지 않으며 개발자가 의도적으로 생성할 수 없습니다.

 

  • 전역 객체의 프로퍼티를 참조할 때 window 또는 global를 생략할 수 있습니다.

 

  • 전역 객체는 Object, String, Number, Boolean, Function, Array RegExp Date, Math, Promise와 같은 표준 빌트인 객체를 프로퍼티로 가지고 있습니다.( 전역 객체가 최상위 객체라고 해서 프로토타입 상속 관계에서 최상위라는 의미가 아닙니다. 표준 빌트인 객체와 호스트 객체를 프로퍼티로 소유하고 있을 뿐입니다. )

 

  • 자바스크립트 실행 환경에 따라 추가적으로 프로퍼티와 메서드를 갖습니다.

 

  • var 키워드로 선언한 전역 변수와 선언하지 않은 변수에 값을 할당한 암묵적 전역 그리고 전역 함수는 전역 객체의 프로퍼티가 됩니다.( let, const는 전역에 선언되어도 전역 객체의 프로퍼티가 되지 않습니다. )

 

  • 여러 파일로 분리되어 있더라도 모든 파일은 동일한 하나의 전역 객체를 가리킵니다.




빌트인 전역 프로퍼티

 

  • 빌트인 전역 프로퍼티는 전역 객체의 프로퍼티를 의미합니다. 주로 어플리케이션 전역에서 사용하는 값을 제공합니다. 빌트인 전역 프로퍼티로는 Infinity, NaN, undefined 등이 있습니다.




빌트인 전역 함수

 

  • 빌트인 전역 함수는 애플리케이션 전역에서 호출할 수 있는 빌트인 함수로서 전역 객체의 메서드입니다. isFinite, isNaN, parseFloat, parseInt 등이 있습니다.




'Programming Language > JavaScript' 카테고리의 다른 글

자바스크립트의 스코프란?  (5) 2022.12.12
this와 메서드 호출 관계  (0) 2022.12.10
프로토타입이란?  (0) 2022.12.08
함수와 일급 객체  (0) 2022.12.07
Math.random() 사용법과 주의사항  (0) 2022.12.07

디지털 이미지 파일 포맷의 종류

  • 디지털 이미지 파일 포맷으로는 크게 래스터( Raster )와 벡터( Vector )가 있습니다




래스터( Raster ) 파일


  • 래스터 파일은 픽셀이 그리드 형태로 모여 만들어진 이미지입니다. 디지털 사진과 디테일한 그래픽은 모두 래스터 형식으로 제공됩니다.




래스터 파일의 특징


  • 비트맵이라고도 하는 래스터 파일은 고품질 사진을 저장하고 보여줄 때 가장 효과적이며 각각의 픽셀이 색상을 표현하여 이미지를 구성합니다. 일정한 수의 색상 픽셀로 구성되어 있기 때문에 파일 크기를 변경하면 해상도가 저하됩니다.

  • 소프트웨어를 통해 래스터 파일의 개별 픽셀을 편집하여 사진의 형태를 변경할 수 있습니다. 온라인의 대부분의 이미지가 래스터 이미지 파일이며, 프로그램 호환성이 좋습니다. 대표적인 래스터 파일 유형으로는 .jpg, .jpeg, .png가 있습니다.




래스퍼 파일의 장점


  1. 디테일한 표현

래스터 파일을 올바른 치수로 표시하면 고해상도 사진에서 볼 수 있는 복잡한 디테일과 색상을 고스란히 표현할 수 있습니다. 파일에 포함된 픽셀 수가 많을수록 이미지 품질이 높아집니다.



  1. 정밀한 편지

래스터 그래픽이나 사진을 편집할 때 각 픽셀을 개별적으로 변경할 수 있습니다. 따라서 필요에 따라 이미지를 보정하고 조정할 수 있습니다.




  1. 뛰어난 호환성

래스터 파일은 매우 다양한 프로그램과 웹 브라우저에서 열 수 있으므로 이미지를 간편하게 확인하고, 편집하고, 공유할 수 있습니다.




래스퍼 파일의 단점


  1. 제한적인 해상도

벡터 이미지와 달리 래스터 파일은 크기를 조정할 때 해상도가 유지되지 않습니다. 이미지를 확대하면 색상과 디테일이 왜곡되어 사용이 제한될 수 있습니다.




  1. 큰 파일 크기

래스터 파일에는 수백만 개의 픽셀이 포함될 수 있습니다. 이로 인해 매우 디테일한 이미지가 생성되지만 파일 크기가 커지고 로딩 속도가 느려질 수 있습니다.




  1. 패브릭 인쇄 한계

래스터 이미지를 구성하는 정사각형 픽셀은 직물이나 의류에는 잘 적용되지 않습니다. 티셔츠 로고와 프린트를 만들 때는 벡터 이미지를 선택하는 것이 좋습니다.




벡터( Vector ) 파일


  • 벡터 파일은 그리드에 점을 설정하는 수학 공식을 기반으로 만들어진 고해상도 이미지입니다.




벡터 파일의 특징


  • 벡터 파일의 이미지는 픽셀을 사용하지 않습니다. 대신에, 그리드(격자)에 고정된 점을 사용하여 수학적 공식에 기반한 직선과 곡선으로 이미지가 생성됩니다. 따라서 해상도 손실 없이 이미지를 크게 또는 작게 만들 수 있습니다.

  • 벡터 포맷은 각종 홍보 자료를 제작하기 위해 그래픽 크기를 다양하게 조정해야 하는 디지털 일러스트레이션과 기업 로고 작업에 많이 사용됩니다. 작은 명함부터 티셔츠, 포스터에 이르기까지 벡터 포맷으로 저장된 로고는 어떤 크기에서도 해상도가 그대로 유지됩니다. 대표적인 벡터 파일 유형으로는 SVG, AI, EPS가 있습니다.




벡터 파일의 장점


  1. 변함없는 해상도

벡터 이미지는 해상도를 유지하면서 거의 무한대로 확대할 수 있습니다. 반면, 래스터 파일의 이미지는 특정 크기에 맞춰진 경우에만 해상도가 유지되므로 이미지를 크게 늘릴수록 품질이 떨어집니다.




  1. 작은 파일 크기

벡터 파일은 픽셀 블록이 없으므로 픽셀로서 이미지를 표현하는 래스터 이미지보다 파일 크기가 작습니다




  1. 다양한 디자인 기능

모양, 텍스트, 다양한 색상, 필터를 추가하여 독창적인 일러스트레이션과 디자인을 만들고, 언제든지 간편하게 원하는 대로 편집할 수 있습니다.




벡터 이미지의 단점


  1. 디테일한 사진 작업에는 부적합

매우 디테일한 디지털 사진은 픽셀 하나하나를 따로 편집할 수 있는 래스터 포맷이 더 낫습니다. 벡터 파일은 사진보다 그래픽에 더 적합합니다.




  1. 호환성 문제

벡터 파일을 열고 작업하려면 벡터 기반의 디자인 프로그램이 있어야 합니다. 래스터 기반의 소프트웨어에서 벡터를 편집하기는 쉽지 않습니다.




  1. 까다로운 파일 변환

래스터 이미지를 벡터 이미지로 변환하려면 다소 높은 컴퓨팅 성능이 요구되며, 다른 파일 포맷보다 변환이 쉽지 않습니다.




.jpg와 .jpge


  • .jpg와 .jpeg는 래스터 이미지 파일의 종류 중 하나 압니다.

  • .jpg와 .jpeg는 동일한 확장자입니다. 과거 DOS시절 확장자명을 최대 3글자까지만 허용했기 때문에 .jpeg가 아닌 .jpg로 사용되었습니다.

  • .jpeg 파일은 최대 24비트 색상을 지원하며, 간편한 저장과 전송을 위해 손실 압축 방식으로 이미지를 저장합니다. 일상적인 사용에는 큰 문제가 없지만 원본 이미지의 품질이 훼손될 수 있다는 단점이 있습니다.




.jpg, .jpeg 파일의 장점


  • .jpeg 파일은 대부분의 브라우저, 소프트웨어, 앱과 호환되는 가장 보편적인 이미지 파일 포맷입니다.

  • 파일 크기가 작아서 빠르게 전송하고 액세스할 수 있습니다. .jpeg는 사람의 눈으로 볼 수 없는 모든 색상을 지능적으로 제거하여 파일 크기를 최대한 작게 유지합니다




.jpg, .jpeg 파일의 단점

  • 손실 압축은 공간을 절약할 수 있지만 압축률이 매우 높은 이미지를 처리할 때 품질이 저하됩니다. 가장자리와 선이 뚜렷한 이미지는 압축 시 선명도가 약간 손실됩니다.




.png


  • .png는 무손실 압축과 함께, 보다 광범위하고 밝은 색상의 팔레트를 제공합니다.

  • .png(Portable Network Graphic)는 래스터 이미지 파일의 일종입니다. 투명 또는 반투명 배경의 그래픽을 처리할 수 있어 웹 디자이너에게 특히 인기 있는 파일 유형입니다.




.png 파일의 장점


  • .png 파일은 수백 개가 아닌 수백만 개의 색상 옵션을 처리하므로 GIF보다 훨씬 더 디테일한 이미지를 저장할 수 있습니다.

  • .png는 개방형 포맷입니다. 라이선스 없이도 다양한 프로그램에서 열고 편집할 수 있습니다.

  • .png 이미지는 압축해도 데이터가 손실되지 않으며, 이는 압축 과정에서 일부 정보가 사라지는 손실 압축 포맷(예: .jpeg 파일)과 비교할 때 큰 장점이 될 수 있습니다.




.png 파일의 단점


  • .png 파일은 압축 시 모든 원본 데이터를 유지하므로 .gif 또는 .jpeg보다 파일 크기가 훨씬 큽니다. 따라서 컴퓨터의 하드 드라이브에서 공간을 많이 차지합니다.

  • .png는 처음부터 웹을 염두에 두고 설계되었기 때문에 CMYK 색상 모드를 지원하지 않습니다. 따라서 인쇄용으로 전송하기가 어려울 수 있습니다.

  • 압축된 .png에는 고품질의 디테일한 이미지 데이터가 포함되어 있지만, 파일 크기가 클수록 페이지 로드 시간과 응답 속도가 느려집니다.




.svg


  • .svg는 벡터 이미지 파일의 종류 중 하나 압니다.

  • SVG(Scalable Vector Graphics) 파일 포맷은 웹 사이트에 2D 그래픽, 차트, 일러스트레이션을 표현하는 데 널리 사용되는 툴입니다. 벡터 파일이므로 확대하거나 축소해도 해상도가 저하되지 않습니다.

  • SVG는 웹 친화적인 벡터 파일 포맷입니다. JPEG와 같은 픽셀 기반의 래스터 파일과 달리, 벡터 파일은 그리드 위의 점과 선을 기반으로 하는 수학 공식을 통해 이미지를 저장합니다. 따라서 SVG와 같은 벡터 파일은 품질을 그대로 유지하면서 크기를 마음대로 조정할 수 있으므로 로고와 복잡한 온라인 그래픽에 아주 적합합니다.




SVG 파일의 장점


  • 픽셀로 구성된 래스터 파일과 달리, SVG와 같은 벡터 그래픽은 크기에 상관없이 항상 해상도를 유지합니다. SVG 이미지는 특정 브라우저나 다양한 위치에 맞게 크기를 조정해도 품질이 저하되지 않습니다.

  • 기본 SVG 파일은 많은 컬러 픽셀로 생성되는 래스터 이미지보다는 크기가 작습니다.

  • SVG 파일은 텍스트를 디자인이 아닌 텍스트 그대로 처리하기 때문에 스크린 리더가 SVG 이미지에 포함된 모든 단어를 스캔할 수 있습니다. 그래서 웹 페이지를 읽어야 할 때 매우 유용합니다. 검색 엔진은 SVG 이미지 텍스트를 읽고 색인을 달 수도 있습니다.




SVG 파일의 단점


  • SVG 파일은 로고, 일러스트레이션, 차트 등 웹 그래픽에 적합합니다. 그러나 픽셀이 부족하므로 고품질 디지털 사진을 표현하기는 어렵습니다. 디테일이 풍부한 사진에는 JPEG 파일이 더 좋습니다.

  • 최신 브라우저만 SVG 이미지를 지원할 수 있으므로 Internet Explorer 8과 이전 브라우저에서는 SVG 파일을 사용하기가 어려울 수 있습니다.




자바스크립트와 객체지향 프로그래밍

 

  • 자바스크립트는 명령형, 함수형 그리고 프로토타입 기반의 객체지향 프로그래밍을 지원하는 멀티 패러다임 프로그래밍 언어입니다. 또한 자바스크립트는 원시 타입의 값을 제외한 모든 것이 객체입니다.




상속과 프로토타입

 

function Circle(radius) {
  this.radius = radius;
  this.getArea = function () {
    return Math.PI * this.radius ** 2;
  };
}

const circle1 = new Circle(1);
const circle2 = new Circle(2);

// return 값은 false
console.log(circle1.getArea === circle2.getArea);

 

  • Circle 생성자 내부에 메서드를 정의하여 인스턴스를 생성할 때마다 getArea() 메서드가 생성되어 할당됩니다. 이러한 불필요한 메모리 낭비를 해결하기 위해 상속을 통해서 하나의 메서드를 동일한 타입의 인스턴스가 공유하도록 하는 것이 바람직합니다.




프로토타입 기반 상속으로 메서드 중복 해결

 

function Circle(radius) {
  this.radius = radius;
  this.getArea = function () {
    return Math.PI * this.radius ** 2;
  };
}

Circle.prototype.getArea = function () {
  return Math.PI * this.radius ** 2;
};

const circle1 = new Circle(1);
const circle2 = new Circle(2);

// return 값은 true
console.log(circle1.getArea === circle2.getArea);

 

  • Circle 생성자 함수가 생성한 모든 인스턴스는 상위(부모) 객체 역할을 하는 Circle.prototype의 모든 프로퍼티와 메서드를 상속받습니다. 이러한 특성을 이용해서 getArea 메서드를 Circle.prototype의 메서드로 할당하여 이후 생성된 모든 Circle 인스턴스가 Circle.prototype의 getArea 메서드를 공유하여 사용할 수 있습니다. 이를 통해 getArea 메서드가 중복되어 생성되는 것을 방지할 수 있습니다.




프로토타입 객체

 

모던 자바스크립트 Deep Dive, 이웅모



  • 프로토타입 객체란 부모 객체의 역할을 하는 객체로서 자식 객체에게 자신의 프로퍼티 및 메서드를 제공합니다. 프로토타입을 상속받은 자식 객체는 프로토타입의 프로퍼티를 프로토타입 체인을 통해서 자신의 프로퍼티처럼 자유롭게 사용할 수 있습니다.

 

  • 모든 객체는 하나의 프로토타입을 갖으며 모든 프로토타입은 생성자 함수와 연결되어 있습니다. 생성자 함수의 prototype, 프로토타입의 constructor 그리고 인스턴스의 __proto__로 서로 연결되어 있습니다.




__proto__는 접근자 프로퍼티다.

 

  • 객체의 [[Prototype]] 내부 슬롯에 접근할 수 있는 방법은 __proto__ 접근자 프로퍼티를 사용하여 간접적으로 접근할 수 있습니다. __proto__는 접근자 프로퍼티로서 [[Get]], [[Set]] 프로퍼티 어트리뷰트를 가지며 이를 통해 [[Prototype]] 내부 슬롯의 값, 즉 프로토타입을 취득하거나 변경할 수 있습니다.




__proto__Object.prototype.__proto__

 

const player = {};

// false return
console.log(player.hasOwnProperty("__proto__"));

// true return
console.log({}.__proto__ === Object.prototype);

 

  • __proto__ 접근자 프로퍼티는 객체가 직접 소유하는 프로퍼티가 아니라 Object.prototype의 프로퍼티이며, Object.prototype을 프로토타입으로 가지는 모든 객체는 프로토타입 체인을 통해서 Object.prototype.__proto__ 접근자 프로퍼티를 사용할 수 있습니다.

 

  • 모든 객체는 프로토타입의 계층 구조인 프로토타입 체인에 묶여있습니다. 접근하려는 인스턴스의 프로퍼티나 메서드가 없을 때 __proto___ 접근자 프로퍼티가 가리키는 참조를 따라 자신의 부모 역할을 하는 프로토타입의 프로퍼티를 순차적으로 검색합니다.




__proto__ 보다는 getPrototypeOf, setPrototypeOf를 사용하자.

 

  • __proto__ 사용은 권장되지 않는다. const obj = Object.create(null);에서 obj는 Object 인스턴스로서 프로토타입이 존재하지 않으며 obj.__proto__ 가 undefined입니다. 이와 같이 프로토타입이 없는 인스턴스도 있기 때문에 __proto__를 사용하기 보다는 getPrototypeOf, setPrototypeOf를 사용하는 것이 바람직합니다.




함수 객체의 prototype 프로퍼티

 

// true return
(function () {}.hasOwnProperty("prototype"));

// false return
({}.hasOwnProperty("prototype"));

function Person(name) {
  this.name = name;
}

const me = new Person("devhun");

// true를 return
console.log(Person.prototype === me.__proto__);

 

  • 함수 객체만이 소유하는 prototype 프로퍼티는 생성자 함수가 생성할 인스턴스의 프로토타입을 가리킵니다.

 

  • prototype 프로퍼티는 생성자 함수일 경우에만 소유합니다.

 

  • constructor 함수의 prototype 프로퍼티와 constructor로 생성한 인스턴스가 들고 있는 __proto__는 동일한 프로토타입을 가리키고 있습니다.




프로토타입의 constructor 프로퍼티와 생성자 함수

 

  • 모든 프로토타입은 constructor 프로퍼티를 갖으며 이 constructor 프로퍼티는 자신을 prototype 프로퍼티로 참조하고 있는 생성자 함수를 가리킵니다.

 

  • 프로토타입이 constructor 프로퍼티를 가지고 있기 때문에 해당 생성자로 생성한 인스턴스는 프로토타입 체인에 의해서 constructor 프로퍼티에 접근할 수 있습니다.




리터럴 표기법으로 생성된 객체와 생성자 함수와 프로토타입

 

function foo() {}

// return true
console.log(foo.constructor === Function);

 

  • 리터럴 표기법으로 생성된 객체의 생성자 함수와 프로토타입은 자바스크립트 엔진 내부적으로 추상 연산인 OrdinaryObejctCreate를 호출하고 인스턴스를 셋팅합니다. 이를 통해 리터럴 표기법으로 생성된 객체도 프로토타입이 셋팅되며, 프로토타입의 constructor도 생성자 함수와 연결되어 있습니다.




프로토타입 생성 시점

 

  • 프로토타입은 생성자 함수가 생성되는 시점에 더불어 생성됩니다. 또한 생성자 함수와 프로토타입은 단독으로 존재할 수 없고 언제나 쌍으로 존재합니다.




사용자 정의 생성자 함수와 프로토타입

 

  • 생성자 함수로서 호출할 수 있는 함수, 즉 constructor는 함수 정의가 평가되어 함수 객체를 생성하는 시점에 프로토타입 객체도 더불어 생성됩니다.

 

  • 사용자 정의 생성자가 평가되어 생성된 프로토타입은 constructor 프로퍼티를 갖으며, 프로토타입 객체의 프로토타입은 Object.prototype입니다.




빌트인 생성자 함수와 프로토타입 생성 시점

 

  • Object, String, Number, Function, Array 등과 같은 빌트인 생성자 함수도 일반 함수와 마찬가지로 빌트인 생성자 함수가 생성되는 시점에 프로토타입이 생성됩니다. 모든 빌트인 생성자 함수는 전역 객체가 생성되는 시점에 생성됩니다.




프로토타입 체인

 

function Player(name) {
  this.name = name;
}

Player.prototype.sayHello = function () {
  console.log(`Hello ${this.name}`);
};

const me = new Player("devhun");

 

  • 위와 같은 상황에서 Player.prototype의 프로토타입은 Object.prototype입니다. Object.prototype은 언제나 프로토타입 체인의 최상위에 위치하여 체인의 종점이라 부릅니다.

 

  • Player 생성자와 Player.prototype은 prototype과 constructor 프로퍼티로 서로 연결되어 있으며 Player.prototype의 prototype은 Obejct.prototype이기 때문에 Player로 생성한 인스턴스는 Player.prototype 및 Object.prototype과 상속관계로 이루어져 있습니다.




오버라이딩과 프로퍼티 섀도잉

 

function Player(name) {
  this.name = name;
}

// 프로토타입 프로퍼티
Player.prototype.sayHello = function () {
  console.log("Hello");
};

const me = new Player("devhun");

// 프로퍼티 섀도잉
me.sayHello = function () {
  console.log(`Hello ${this.name}`);
};

// Hello devhun 출력
me.sayHello();

 

  • 위 코드를 보면 Player.prototype에 sayHello() 라는 메서드를 추가하였지만, Player 생성자를 통해 생성한 인스터스 sayHello 프로퍼티에 새로운 메서드를 할당하여 오버라이딩을 하였습니다. 이로 인해서 프로토타입의 sayHello() 메서드가 가려지는 현상을 프로퍼티 섀도잉이라 합니다.( 자바스크립트에서는 오버로딩은 지원하지 않습니다. )




프로토타입 메서드 삭제

 

// 인스턴스 프로퍼티 삭제
delete me.sayHello;

// 프로토타입 체인에 의해서 Hello 출력
me.sayHello();

delete me.sayHello;

// Hello가 그대로 출력
me.sayHello();

 

  • 자식 객체에서는 프로토타입 프로퍼티를 변경 또는 삭제하는 것은 불가능합니다. 프로토타입의 프로퍼티를 삭제하기 위해서는 프로토타입에 직접 접근하여 삭제해야 합니다.




프로토타입 교체

 

  • 프로토타입은 임의의 다른 객체로 변경할 수 있으며, 이러한 특징을 활용하여 객체 간의 상속 관계를 동적으로 변경할 수 있습니다. 프로토타입은 생성자 함수 또는 인스턴스에 의해 교체할 수 있습니다.




생성자 함수에 의한 프로토타입 교체

 

const Person = (function () {
  function Person(name) {
    this.name;
  }

  Person.prototype = {
    // 생성자와 연결을 만듬
    // constructor: Person,
    sayHello() {
      console.log(`Hello ${this.name}`);
    },
  };

  return Person;
})();

const me = new Person("devhun");

// true
console.log(Person.prototype === me.__proto__);

// false
console.log(me.constructor === Person);

// true
console.log(me.constructor === Object);

 

  • 위와 같이 생성자 함수에서 prototype을 리터럴 객체로 변경하면 이후 Person 생성자 함수로 생성된 모든 인스턴스의 프로토타입은 리터럴 객체로 셋팅됩니다. 하지만, 리터럴 객체는 constructor 프로퍼티가 없기 때문에 Person 생성자 함수는 prototype 프로퍼티를 통해서 객체 리터럴에 접근할 수 있지만, 프로토타입인 리터럴 객체는 constructor 프로퍼티가 없기 때문에 프로토타입 체인에 의해서 Object 생성자와 연결되어 있는 상태입니다. 이를 해결하기 위해서는 직접 constructor 프로퍼티로 연결해주는 작업이 필요합니다.




인스턴스에 의한 프로토타입 교체

 

function Person(name) {
  this.name;
}

const parent = {
  sayHello() {
    console.log(`Hello ${this.name}`);
  },
};

const me = new Person("devhun");

Object.setPrototypeOf(me, parent);

// false
console.log(Person.prototype === me.__proto__);

// false
console.log(me.constructor === Person);

// true
console.log(me.constructor === Object);

 

  • 인스턴스의 __proto__ 접근자 프로퍼티를 통해서 프로토타입을 교체할 수 있습니다. 인스턴스로 프로토타입을 교체할 경우 기존 생성자 함수의 prototype은 그대로이기 때문에 이후 생성될 인스턴스의 프로토타입에는 영향을 주지 않습니다.




intanceof 연산자

 

function Person(name) {
  this.name = name;
}

const me = new Person("devhun");

// true
console.log(me instanceof Person);

// true
console.log(me instanceof Object);

 

  • instanceof 연산자는 우변의 생성자 함수의 prototype에 바인딩된 객체가 좌변의 객체의 프로토타입 체인상에 존재하면 true로 평가됩니다.




직접 상속

 

const obj = Object.create(Object.prototype, {
  x: { value: 1, writable: true, enumerable: true, configurable: true },
});

console.log();

 

  • object.create() 함수를 이용해서 첫 번째 인수로는 프로토타입을 두 번째 인수로는 프로퍼티 디스크립터 배열을 전달하여 인스턴스를 생성할 수 있습니다.

 

  • object.create(null);를 통해서 인스턴스를 생성할 경우 아무런 프로퍼티를 가지지도 않고 프로토타입도 가지지 않는 인스턴스가 생성됩니다.




정적 프로퍼티/메서드

 

function Person(name) {
  this.name = name;
}

Person.prototype.sayHello = function () {
  console.log(`Hello ${this.name}`);
};

// 정적 프로퍼티
Person.staticProp = "static prop";

// 정적 메소드
Person.staticMethod = function () {
  console.log("static method");
};

 

  • 정적 프로퍼티/메서드는 생성자 함수로 인스턴스를 생성하지 않아도 참조 및 호출할 수 있는 프로퍼티/메서드를 말합니다.

 

  • 정적 프로퍼티/메서드는 인스턴스로는 호출할 수 없습니다.




프로퍼티 존재 확인

 

in 연산자 & Object.prototype.hasOwnPrototype 메서드

 

const person = {
  name: "devhun",
};

// true를 return
console.log("toString" in person);

// false를 return
console.log(person.hasOwnProperty("toString"));

 

  • in 연산자는 인스턴스의 프로퍼티가 존재하는지 확인하는데, 프로토타입 체인을 따라 확인하기 때문에 부모 프로토타입에 찾으려는 프로토타입이 있을 경우 true를 return 합니다. 그렇기 때문에 경우에 따라서 in 연산자 보다는 Object.prototype.hasOwnPrototype 메서드를 사용해야 합니다.




프로퍼티 열거

 

for...in 문

 

const person = {
  name: "devhun",
  age: 28,
};

for (const key in person) {
  console.log(`${key} : ${person[key]}`);
}

 

  • for...in 문은 인스턴스가 가지고 있는 enumerable가 true인 프로퍼티 어트리뷰트를 가진 프로퍼티를 대상으로 열거합니다. 하지만, 순회 대상 인스턴스가 상속받은 프로토타입의 프로퍼티까지 포함합니다.

 

  • 인스턴스의 고유 프로퍼티만 열거하고 싶을 경우에는 Object.keys/values/entries 메서드를 사용하면 됩니다.




'Programming Language > JavaScript' 카테고리의 다른 글

this와 메서드 호출 관계  (0) 2022.12.10
빌트인 객체란?  (0) 2022.12.10
함수와 일급 객체  (0) 2022.12.07
Math.random() 사용법과 주의사항  (0) 2022.12.07
프로퍼티 어트리뷰트란?  (0) 2022.12.06

일급 객체란?

 

  • 일급 객체란 아래와 같은 조건을 가진 객체를 말합니다.

 

  1. 무명의 리터럴로 생성할 수 있다. 즉, 런타임에 생성이 가능하다.
  2. 변수나 자료구조(객체, 배열 등)에 저장할 수 있다.
  3. 함수의 매개변수로 전달할 수 있다.
  4. 함수의 반환값으로 사용할 수 있다.




함수는 일급객체일까?

 

// 1. 런타임에 함수 리터럴이 평가되어 함수 객체가 생성되고 변수에 할당할 수 있다.
const increment = function (num) {
  return ++num;
};

// 2. 변수나 자료구조(객체, 배열 등)에 저장할 수 있다.
const numObj ={
  increment;
}

// 3. 함수의 매개변수로 전달할 수 있다.
// 4. 함수의 반환값으로 사용할 수 있다.
function makeCounter(increment){
  return increment;
}

makeCounter(numObj.increment);

 

  • 함수는 위 코드에 나온 모든 조건에 해당되기 때문에 일급 객체입니다. 일반 객체와 다른점은 호출이 가능하고 함수 객체만의 고유의 프로퍼티를 소유합니다.




함수 객체의 프로퍼티

 

  • 함수 객체는 length, name, arguments, caller, prototype의 프로퍼티를 가지고 있습니다.




arguments 프로퍼티

 

  • 함수 객체의 arguments 프로퍼티는 함수 호출 시 전달된 인수들의 정보를 담고 있는 순회 가능한 유사 배열 객체입니다. 함수 내부에서 지역 변수처럼 사용 가능합니다.

 

  • 함수를 호출할 때 매개변수의 정보는 arguments 객체에 저장되는데, 초과된 매개변수 또한 arguments 객체에 저장됩니다.

 

  • arguments 프로퍼티는 가변 인자 함수를 구현할 때 유용합니다. arguments 객체는 배열 형태로 인자 정보를 담고 있지만, 실제 배열이 아닌 유사 배열 객체입니다.( 유사 배열 객체란 배열은 아니지만, length 프로퍼티와 같이 배열처럼 사용할 수 있는 프로퍼티를 가지는 객체로 for 문으로 순회할 수 있는 객체를 말합니다. )




caller 프로퍼티

 

function foo(func) {
  return func();
}

function bar() {
  return `caller : ${bar.caller}`;
}

console.log(foo(bar)); // caller : function foo(func){...}
console.log(bar()); // caller : null

 

  • caller 프로퍼티는 ECMAScript 사양에 포함되지 않은 비표준 프로퍼티입니다.

 

  • caller는 자신을 호출한 함수를 가리킵니다.




length 프로퍼티

 

  • 함수 객체의 length 프로퍼티는 함수를 정의할 때 선언한 매개변수의 개수를 가리킵니다.




name 프로퍼티

 

  • 함수 객체의 name 프로퍼티는 함수 이름을 나타내며, name 프로퍼티는 ES6 이전까지는 비표준이었다가 ES6에서 정식 표준이 되었습니다.

 

  • 함수 이름을 가리키는 프로퍼티입니다.




prototype

 

  • prototype 프로퍼티는 생성자 함수로 호출할 수 있는 함수 객체인 constructor만이 소유하는 프로퍼티입니다. prototype은 생성자 함수가 생성할 인스턴스의 프로토타입 객체를 가리킵니다.




'Programming Language > JavaScript' 카테고리의 다른 글

빌트인 객체란?  (0) 2022.12.10
프로토타입이란?  (0) 2022.12.08
Math.random() 사용법과 주의사항  (0) 2022.12.07
프로퍼티 어트리뷰트란?  (0) 2022.12.06
암묵적 전역( implicit global )이란?  (0) 2022.11.28

+ Recent posts