본문으로 바로가기

shorthand property, concise method

category JavaScript/ES6+ 2021. 2. 26. 18:36

이 글은 [인프런] Javascript ES6+ 제대로 알아보기 - 초급(정재남)를 토대로 작성되었음을 알려드립니다.


es2015 로 넘어오면서 객체를 사용함에 있어 편리성, 간결성과 더불어 다양하게 향상된 기능들을 제공해 주고 있어 이에 대해 알아보고자 합니다.

 

 

프로퍼티 축약, 간결한 메소드

먼저 객체의 프로퍼티를 간결하게 작성할 수 있는 방법에 대해 알아보겠습니다.

 

shorthand properties (프로퍼티 축약)

먼저 다음의  ES5  vs  ES6 코드를 비교하며 살펴보자.

ES5 JS
var x = 10;
var y = 20;
var obj = {
    x: x,
    y: y
};
ES6(es2015) JS
var x = 10;
var y = 20;
var obj = {
    x,
    y
};

위 예제는 각각 똑같은 프로퍼티키와 선언된 변수명이 같도록 하여 변수명을 할당하고자 하는 경우에 사용할 수 있고, 이는  key 가 곧  value 를 의미하게 됩니다.   즉, 프로퍼티의 key 와 value 에 할당할 변수명이 동일한 경우 value 를 생략 가능합니다.

 

프로퍼티 축약 활용

1)   함수에서 객체를 리턴할 때

js
const convertExtension = function (fullFileName) {
    const fullFileNameArr = fullFileName.split('.');
    const filename = fullFileNameArr[0];
    const ext = fullFileNameArr[1] && fullFileNameArr[1] === 'png' ? 'jpg' : 'gif';
    return {
        filename,
        ext
    }
};

const convertResult = convertExtension( 'abc.png' );
console.log( convertResult );
console.log( convertResult.filename );
console.log( convertResult.ext );

 

2)   destructuring assignment(해체 할당)

destructuring assignment 은 이후 자세히 살펴보겠지만 여기선 프로퍼티 축약에 중점을 두고 살펴보도록 하자.

js
const {
    name: name,
    age: age
} = {
    name: '재희',
    age: 30
};
console.log(name, age);

앞 부분이  변수명 선언부이고  = 의 뒷 부분이  값으로 할당하는 형태로 이루어져 있습니다.
const 에 바로 객체가 와서 낯설겠지만 이것은 객체의 형태를 뒷 부분과 매칭시켜서 동일하게 매칭되어 있는  key 마다 똑같이 할당해 주는 것입니다.   이 코드상에서는  name : name, 의 프로퍼티의 값에  '재희'를 넣고,  age 에는  30을 넣겠다라는 코드입니다.

위 코드도 프로퍼티 축약형으로 다음과 같이 할 수 있습니다.

js
const {
    name,
    age
} = {
    name: '재희',
    age: 30
};
console.log(name, age);

 

 

 

concise methods (간결한 메소드)

간결한 메소드는 앞서 살펴본 프로퍼티 축약형과 비슷한 느낌의 문법 형태를 띠고 있습니다.

먼저 다음의  ES5  vs  ES6 코드를 비교하며 살펴보자.

ES5 JS
var obj = {
    name: 'foo',
    getName: function () { return this.name }
};
ES6(es2015) JS
var obj = {
    name: 'foo',
    getName () { return this.name }
};

ES5 코드에서  getName 은 메소드 부분으로 익명함수를 프로퍼티에 할당한 것입니다.
이렇게  ES6에서는 이러한 할당 자체를 콜론과 함수 키워드를 생략함으로서 간결한 문법을 제공해 주고 있습니다. 즉, 간결한  메소드 축약형입니다.

 

그렇다면 기존  ES5의 메소드와  ES6의 메소드 축약형은 어떤 차이점을 가지고 있을까요?

다음의 코드를 통해 차이점을 살펴보도록 하자.

js
var obj = {
    name: 'foo',
    // ES6
    getName () { return this.name; },
    // ES5
    getName2 : function () { return this.name; }
};

console.dir( obj.getName );
console.dir( obj.getName2 );

로그를 확인해 보면  ES6의  getName 의 경우  arguments,  caller 는 호출(invoke)을 해야만 이에 대한 정보, 즉 값을 알 수 있는 것입니다.
이유는 실행 환경 당시에 값에 접근하는 것이 아니라 외부 스코프에서 이미 함수가 종료된 이후에 접근(이 코드상에서)하려고 한 것이기 때문에 명시적인 에러를 나타내고 있는 것입니다.
하지만  ES5에서  arguments,  caller 는 실행이 종료된 이후에 접근하려고 해도 접근이 되고 다만 값만  null 일 뿐인 것입니다.
정리하면, 대부분의 기능들이  ES6부터는 에러를 명시적으로 보여주면서 디버깅하기 용이하도록 제공해 주고 있기 때문에 위와 같은 에러가 표시되어 보여주고 있는 것입니다.
즉,  obj.getName 을 호출하고 난 후  console.dir 로 보여주려고 한 상태는 함수가 이미 종료된 상태이고 이렇게 접근할 수 없는 상태임을 명시적으로 나타내 주고 있는 것입니다.

 

그리고 또 하나 다른 점은  getName2 에는  prototype 프로퍼티 가 존재하지만 ES6의  getName 에는  prototype 프로퍼티가 없습니다.

 

prototype 프로퍼티가 있고 없고의 차이점은 무엇일까?

js
var obj = {
    name: 'foo',
    getName () { return this.name; },
    getName2 : function () { return this.name; }
};

const a = new obj.getName2();
console.log( a ); // 빈 객체

const b = new obj.getName();

getName2 라는 이름으로 하는 생성자 함수로 인스턴스를 만든 것으로 즉, 생성자 함수로서 동작을 한 것입니다.
기존의 함수는 생성자라는 것 자체가 암묵적인 약속된 대,소문자 구별로만 사용했을 뿐  new 연산자만 사용하면 인스턴스를 생성할 수 있었기 때문에 getName2 : function () { return this.name; } 이 코드도 생성자 함수로 사용할 수 있는 것입니다.
그리고 이때 생성자 함수가 하는 역할 중에는 이 함수가 들고 있던 프로토타입에 대한 내용을 인스턴스에  __proto__ 로 연결해 주는 역할도 하게 됩니다.   그렇기 때문에 기존 메소드는 생성자 함수로도 사용이 가능했으나 새로운 메소드 축약형에서는 명시적으로 메소드 역할에 중점을 두어 명시적인 에러를 나타내게 됩니다.
이 의미는 여태 함수는 함수로서... 생성자 함수로서... 두 가지로 사용이 가능했었지만 ES6에 와서는 생성자 함수로서의 기능을 제약시킨 것입니다. 즉, 메소드는 본연의 메소드로서의 역할만 하라는 뜻이고  이 메소드로 딴 짓하면 친절하게 내가 에러를 내주도록 해줄게...라는 뜻입니다.
그리고 이러한 본래의 역할 구분으로 인해 함수가 가벼워진 측면도 있습니다.

 

메소드 축약형을 사용하면 생성자 함수로 사용할 수 없고  prototype 프로퍼티가 존재하지 않으므로 오로지 함수 본연의 기능만 할 수 있도록 변경된 것입니다.

 

 

Detail Info

1)   :function 키워드 제거

2)   super 명렁어로 상위 클래스에 접근 가능

super 명령어는 클래스(Class) 파트에서 자세히 다룰 예정으로 간단히 개념만 짚고 넘어가보도록 하겠습니다.

super 는 사전적 의미로 "상위의" 그리고  sub는 "하위의"란 뜻을 가지고 있습니다.
그래서 super Class(상위 클래스),  sub Class(하위 클래스) 가  ES6에 새롭게 등장했습니다.

  • super() :  상위에 있는 내용을 호출해라란 명령어로서 프로토타입 체이닝상에 있는 상위에 있는 내용을 가지고 오는 것입니다.
  • sub() :
js
const Person = {
    greeting: function () { return 'hello'; }
};

const friend = {
    greeting: function () {
        return 'hi, ' + super.greeting();
    }
};

// ES5 문법
Object.setPrototypeOf(friend, Person); // friend 의 프로토타입을 Person 으로 해라

Object.setPrototypeOf(friend, Person); 는 내부적으로 다음과 같은 코드로 동작합니다.

js
Object.setPrototypeOf(friend, Person);
// friend.__proto__ = { greeting: function() {} }

// 위 setPrototypeOf 은 아래 내용과 같다.
const Person = function () { };
Person.prototype.greeting = function () { return 'hello'; };

const friend = new Person();
friend.greeting = function () {
    return 'hi, ' + this.__proto__.greeting();
}

위 코드상에서  Object.setPrototypeOf(friend, Person);의  setPrototypeOf 는  friend라는 녀석을 인스턴스로 하고  Person이라는 녀석을
생성자 함수로 지정해라라는 내용입니다.
그렇게 되면  friend 의 프로토타입상의 생성자 함수가  Person 이 되고  Person 에 있는 객체가 프로토타입이 되게 됩니다.

 

위에서 언급한 내용을 생각하면서 예제를 다시 분석해보자.

js
const Person = {
    greeting () { return 'hello'; }
};

const friend = {
    greeting () {
        return 'hi, ' + super.greeting();
    }
};

Object.setPrototypeOf(friend, Person);
console.log( Object.setPrototypeOf( friend, Person ) );
console.log( friend );

friend.greeting();

기존 메소드 표기법으로 작성하면 동작하지 않기 때문에 위 예제를 메소드 축양형으로 변경한 코드이며,  super 라는 접근자로 상위의 내용을 가져다 사용할 수 있게 됩니다.   즉, 자신의 프로토타입 체이닝상에 있는 상위에 있는 메소드를 호출할 수 있습니다.
위 예제에서 friend 는 greeting 이라는 메소드를 자신이 가지고 있는 형태이고, 프로토타입 체이닝상에도 greeting 이라는 메소드도 있는 형태입니다.

friend.greeting(); 이와 같이 호출할 때 프로토타입 체이닝을 생각해 보면 가장 가까운 메소드부터 찾고, 여기서는 자신에 있는 메소드인  greeting 부터 실행할 것입니다.   그런데 상위에 있는 것을 실행한다고 할 때  friend.__proto__.greeting(); 하면  'hello' 만 나올 것입니다.

본래 기존  ES5 방식에서는 상위에 있는 것을 호출할 방법이 마땅치 않았지만  super 접근자를 이용하면 바로 상위에 있는 것까지 접근할 수 있는 길이 열린 것입니다.

 

3)   prototype 프로퍼티가 없고 이는 곧 생성자함수로 사용 불가하다.

js
const Person = {
  greeting () { return 'hello' }
}

const p = new Person.greeting();

 

4)   그 밖에는 모두 기존 함수/메소드와 동일하게 동작한다.

js
const obj = {
  a () { console.log('obj log') },
  log () { console.log(this) }
};

console.log(obj.a.name);
setTimeout(obj.a, 1000);
obj.log();
obj.log.call([]);
setTimeout(obj.log.bind('haha'), 2000);

 

 

Jaehee's WebClub