자바스크립트 Deep Dive - week 5

하머·2022년 12월 14일
0
post-thumbnail

Prototype

1. 프로토타입 이란?

  • 자바스크립트의 모든 객체는 자신의 부모 역할을 담당하는 객체와 연결되어 있다.
    ⇒ 객체 지향의 상속 개념 같이 부모 객체의 프로퍼티 혹은 메소드에 프로토타입 체인을 통해 접근 가능하다.
  • 자바스크립트의 모든 객체는 [[Prototype]]이라는 인터널 슬롯(internal slot)를 가진다. [[Prototype]]의 값은 null 또는 객체이며 상속을 구현하는데 사용된다. ****
  • [[Prototype]] 객체의 데이터 프로퍼티는 get 액세스를 위해 상속되어 자식 객체의 프로퍼티처럼 사용할 수 있다.
    proto 프로퍼티로 접근 가능하나 프로토타입 체인 구조 상 단방향 링크드 리스트로 구현되어야 하는 조건 때문에 잘못 수정 시 순환참조의 경우가 생길 수 있으므로 직접적으로 접근해 수정해선 안된다. (Object.getPrototypeOf() (ES5), Object.setPrototypeOf (ES6) 메소드를 사용해 접근)

2. **[[Prototype]] vs prototype 프로퍼티**

  • **[[Prototype]] =** proto or null (프로토타입 체인의 종결점인 Object.prototype의 **[[Prototype]]** 경우)

    • 객체의 입장에서 자신의 부모역할 하는 프로토타입 객체를 가리킨다. 함수의 경우에는 Function.prototype 을 가리킨다.
      • 함수 리터럴을 사용한 함수들은 Function() 생성자 함수로 인스턴스를 생성하는 것을 단순화 한것이기 때문에 Function.prototype 이 된다.
  • prototype 프로퍼티

    • 생성자 함수는 본인이 생성하는 인스턴스들의 [[Prototype]]인 prototype 프로퍼티를 갖는다.

    • 다만 축약형 함수나 화살표 함수의 경우 생성자 함수로 사용할 수 없으므로(constructor 가 없음) prototype 프로퍼티를 갖지 않는다.

      function Person(name, gender) {
        this.name = name;
        this.gender = gender;
        this.sayHello = function () {
          console.log("Hi! my name is " + this.name);
        };
      }
      var arrowFunc = () => {
        return "arrow";
      };
      
      var foo = new Person("Lee", "male");
      
      console.dir(Person);
      console.dir(foo);
      
      console.log(foo.__proto__ === Person.prototype); // ① true
      console.log(Person.prototype.__proto__ === Object.prototype); // ② true
      console.log(Person.prototype.constructor === Person); // ③ true
      console.log(Person.__proto__ === Function.prototype); // ④ true
      console.log(Function.prototype.__proto__ === Object.prototype); // ⑤ true
      console.log(arrowFunc.prototype); // undefined
    • 함수 리터럴이나 객체 리터럴(function)으로 생성되는 객체들 역시 모두 Object(), Function() 생성자로 생성되는 객체이기 때문에 Object.prototype, Function.prototype 을 [[Prototype]]으로 둔다.

      var person = {
        name: "Lee",
        gender: "male",
        sayHello: function () {
          console.log("Hi! my name is " + this.name);
        },
      };
      
      console.dir(person);
      
      console.log(person.__proto__ === Object.prototype); // ① true
      console.log(Object.prototype.constructor === Object); // ② true
      console.log(Object.__proto__ === Function.prototype); // ③ true
      console.log(Function.prototype.__proto__ === Object.prototype); // ④ true

3. constructor 프로퍼티

  • prototype 객체는 constructor 프로퍼티를 갖는다. 이 constructor 프로퍼티는 객체의 입장에서 자신을 생성한 객체(생성자 함수)를 가리킨다.

    function Person(name) {
      this.name = name;
    }
    
    var foo = new Person("Lee");
    
    // Person() 생성자 함수에 의해 생성된 객체를 생성한 객체는 Person() 생성자 함수이다.
    console.log(Person.prototype.constructor === Person);
    
    // foo 객체를 생성한 객체는 Person() 생성자 함수이다.
    console.log(foo.constructor === Person);
    
    // Person() 생성자 함수를 생성한 객체는 Function() 생성자 함수이다.
    console.log(Person.constructor === Function);

4. 프로토타입 체인

  • 자바스크립트는 특정 객체의 프로퍼티나 메소드에 접근하려고 할 때 해당 객체에 접근하려는 프로퍼티 또는 메소드가 없다면 [[Prototype]]이 가리키는 링크를 따라 자신의 부모 역할을 하는 프로토타입 객체의 프로퍼티나 메소드를 차례대로 검색한다. 이것을 프로토타입 체인이라 한다.

  • 객체의 프로퍼티를 참조하는 경우, 해당 객체에 프로퍼티가 없을 경우에 프로토타입 체인이 동작하지만, 객체의 프로퍼티에 값을 할당하는 경우에는 프로토타입 체인이 동작하지 않는다.

    • 이는 객체에 해당 프로퍼티가 있을 경우 동적으로 값을 재할당하고 없는 경우는 해당 객체에 프로퍼티를 동적으로 추가하기 때문이다.
    function Person(name) {
      this.name = name;
    }
    
    Person.prototype.gender = "male"; // ①
    
    var foo = new Person("Lee");
    var bar = new Person("Kim");
    
    console.log(foo.gender); // ① 'male'
    console.log(bar.gender); // ① 'male'
    
    // 1. foo 객체에 gender 프로퍼티가 없으면 프로퍼티 동적 추가
    // 2. foo 객체에 gender 프로퍼티가 있으면 해당 프로퍼티에 값 할당
    foo.gender = "female"; // ②
    
    console.log(foo.gender); // ② 'female'
    console.log(bar.gender); // ① 'male'

5. prototype 프로퍼티 객체의 확장

  • 생성자 함수로 생성할 인스턴스들의 부모가 될 prototype 프로퍼티에 모든 인스턴스가 사용할 공통적인 프로퍼티 또는 메서드를 설정해 줄 수 있다.

    function Person(name) {
      this.name = name;
    }
    
    var foo = new Person("Lee");
    var bar = new Person("Kim");
    
    Person.prototype.sayHello = function () {
      console.log("Hi! my name is " + this.name);
    };
    
    foo.sayHello(); // Hi! my name is lee
    bar.sayHello(); // Hi! my name is kim

6. 원시 타입의 확장

  • 자바스크립트에서 원시 타입들은 객체와 유사하게 동작할 때가 있다. (예: str.length)

  • 빌트인 객체인 String, Number, Array 객체 등이 가지고 있는 표준 메소드는 프로토타입 객체인 String.prototype, Number.prototype, Array.prototype 등에 정의되어 있다.

    • 이들 프로토타입 객체들도 Object.prototype을 [[Prototype]]으로 연결한다.
  • 원시 타입으로 프로퍼티나 메소드를 호출할 때 원시 타입의 빌트인 객체의 prototype으로 일시적 변환되어 프로토타입 객체를 공유하게 된다.

  • 빌트인 객체의 prototype 프로퍼티에 추가한다면 원시 타입의 확장이 가능해진다.

    var str = "test";
    
    String.prototype.myMethod = function () {
      return "myMethod";
    };
    
    console.log(str.myMethod());
    console.dir(String.prototype);
    
    console.log(str.__proto__ === String.prototype); // ① true
    console.log(String.prototype.__proto__ === Object.prototype); // ② true
    console.log(String.prototype.constructor === String); // ③ true
    console.log(String.__proto__ === Function.prototype); // ④ true
    console.log(Function.prototype.__proto__ === Object.prototype); // ⑤ true

7. 프로토타입 객체의 변경

  • prototype 프로퍼티를 동적으로 바꿈으로써 객체의 상속을 구현할 수 있다.

  • 이 때 주의할 점은 프로토타입 변경 시점과 인스턴스 생성 시점에 따라 각각의 [[Prototype]]이 다르게 바인딩 된다.

    function Person(name) {
      this.name = name;
    }
    
    var foo = new Person("Lee");
    
    // 프로토타입 객체의 변경
    Person.prototype = { gender: "male" };
    
    var bar = new Person("Kim");
    
    console.log(foo.gender); // undefined
    console.log(bar.gender); // 'male'
    
    console.log(foo.constructor); // ① Person(name)
    console.log(bar.constructor); // ② Object() (Object.prototype.constructor)

strict mode

  1. strict mode란?
  • 자바스크립트의 엄격한 문법을 사용하도록 하는 모드
  1. strict mode의 장점
  • 변수의 선언을 누락하는 실수를 방지
    • 변수의 선언을 누락하면 전역 객체의 프로퍼티가 되는데, 이를 에러를 통해 방지
  • 변수의 스코프를 실수로 변경하는 것을 방지
    • var 키워드를 사용하지 않고 변수를 선언하는 것을 방지
  • this가 전역 객체를 가리키는 것을 방지
    • this가 undefined를 가리키도록 한다.
  • 함수의 인자에 동일한 이름의 인자를 사용하는 것을 방지
  • delete 연산자로 변수를 삭제하는 것을 방지
  • with 문을 사용하는 것을 방지
  • eval 함수를 사용하는 것을 방지
  1. strict mode의 사용
  • 전역에 적용
    • 전역에 적용하면 스크립트 전체에 적용된다.
    • 스크립트의 맨 앞에 'use strict'를 작성한다.
    • 전역에 사용하는것은 피하자.
      • 전역에 적용하면 스크립트 전체에 적용되기 때문에 다른 스크립트에 영향을 줄 수 있다.
      • strict mode를 사용하지않는 라이브러리를 사용할 때 문제가 발생할 수 있다.
  • 함수에 적용
    • 함수 내부에 적용하면 함수 내부에 적용된다.
    • 함수의 맨 앞에 'use strict'를 작성한다.
    • 함수에 사용하지 않는 것이 좋다.
      • 이 또한 각각의 함수들에 적용하기가 번거롭고, 함수 외부 컨텍스트에서 'use strict'를 사용하지 않으면 문제가 발생할 수 있다.
  • 즉시 실행 함수에서 적용 하는 것이 가장 좋다고 한다.

빌트인 객체

  1. 빌트인 객체란?
  • 자바스크립트 엔진에 내장되어 있는 객체
  • 빌트인 객체는 개발자가 직접 생성하지 않고, 자바스크립트 엔진이 암묵적으로 생성하고 초기화한다.
  • 표준 빌트인 객체
    • ECMAScript 사양에 정의된 빌트인 객체
    • Object, Function, Array, String, Boolean, Number, Math, Date, RegExp, Promise, Symbol, Map, Set, WeakMap, WeakSet, Reflect, Proxy
    • 전역 객체의 프로퍼티로 제공된다.
    • Math, Reflect, JSON을 제외한 나머지는 생성자 함수로 사용할 수 있다.
  1. 빌트인 객체의 사용
  • 문자열, 숫자, 불리언 값에 대해 객체처럼 사용한다면 자바스크립트 엔진은 원시 값을 객체로 감싸는 래퍼 객체를 생성한다.
  • 래퍼 객체는 임시로 생성되는 객체로, 래퍼 객체가 생성되고 즉시 파괴된다.
  • 래퍼 객체는 원시 값을 객체처럼 사용할 수 있게 해주지만, 원시 값을 변경할 수는 없다.
  • 이 때 래퍼 객체는 각 빌트인 객체의 프로토타입을 상속받아 사용한다.
  1. 전역 객체
  • 브라우저 환경에서는 전역 객체가 window이다.
  • 노드 환경에서는 전역 객체가 global이다.
  • 전역 객체는 몇가지 프로퍼티와 메소드를 제공한다.
    • Infinity, NaN, undefined, eval, parseInt, parseFloat, isNaN, isFinite, decodeURI, decodeURIComponent, encodeURI, encodeURIComponent, escape, unescape, Object, Function, Array, String, Boolean, Number, Math, Date, RegExp, Promise, Symbol, Map, Set, WeakMap, WeakSet, Reflect, Proxy, JSON, Intl, require, module, exports, console, process, Buffer, setImmediate, clearImmediate, setTimeout, clearTimeout, setInterval, clearInterval, queueMicrotask, clear, alert, confirm, prompt, print
    • 더 이상 권장되지 않는 프로퍼티와 메소드도 있다.
      • escape, unescape, eval, parseInt, parseFloat, isNaN, isFinite, decodeURI, decodeURIComponent, encodeURI, encodeURIComponent, alert, confirm, prompt, print
  • 전역 객체는 전역 스코프를 갖는다.
  1. 암묵적 전역
  • 암묵적 전역은 변수 선언문으로 선언한 변수가 전역 객체의 프로퍼티가 되는 특성을 말한다.
var x = 1; // 전역 변수 x
console.log(window.x); // 1

y = 2; // 암묵적 전역 변수 y
console.log(window.y); // 2

0개의 댓글