ECMAScript 5 Features

이미 우리 바로 옆에 있는 ECMAScript5

Created by 이항희 / blog.javarouka.me

현재 거의 모든 브라우저가 지원하고 있는 것은 3판

그 다음 많이 대중화된 5판(?)

핫이슈가 된 6판

오늘 다룰 것은 5판

음...그럼 4판은???

ECMA International 에서 비공개 결정됨

Why?

4판이 제안한 새로운 특성들

  • 패키지와 네임스페이스
  • 클래스와 인터페이스
  • 제네릭스 및 타입 애노테이션
  • 엄격한 검증 (Strict verification)
  • 리플렉션
  • 동적/가상 프로퍼티
  • 이터레이터와 제네레이터
  • 옵티마이저
  • ....기타등등...

언어 명세가 기존에 비해 복잡하고 많다

너무 급진적이고 격렬한 변화

douglas crockford
이거 복잡해서 쓰겠나...실무자 이외의 그룹은 언어 개선안도 내지 마라

By Douglas Crockford

(JSON DataFormat Popularised, JSLint Contributor)
Jeremy Ashkenas
JavaScript는 전문가에게 맡기기엔 너무나 소중하다

By Jeremy Ashkenas

(Backbone.js, Underscore, CoffeeScript Contributor)

결국 4판은 폐기되고
"ECMAScript Harmony" (ECMAScript 6판)
으로 일부 기능만 흡수되어 공개.

그 뒤 2009년 12월 5판이 공개.
편의성 위주와 그동안 부실했던 것을 보충하려는 것들 위주

이제 그 5판의 기능을 알아보는 시간

모두 볼 순 없고 유용한 것들에 대해서

이런 것들!

  • JSON Object Native Support
  • Strict Mode
  • New Object Programing
  • New Array Collection Method
  • etc...

이제 알아봅시다

JSON

JSON 변환을 Native로 지원

그전에는 eval 등의 평가 함수나 별도의 서드파티 라이브러리를 사용해야만...

ECMA Script 3nd

(function() {

    var jsonString = "{ \"hello\": \"world\" }";

    // eval 사용
    // eval(json 문자열);
    var jsn = eval("("+jsonString+")");
    alert(JSON.stringify(jsn, null, '    '));

})();
RUN
혹은, Function 생성자로
(function() {

    var jsonString = "{ \"hello\": \"world\" }";

    // 동적 함수 사용
    // new Function(json 문자열을 리턴하는 소스 문자열);
    var jsn = new Function("return " + jsonString)();
    alert(JSON.stringify(jsn, null, '    '));

})();
RUN

ECMA Script 5th

!function() {

  var jsonString = "{ \"hello\": \"world\" }";

  // Native JSON Object
  var jsn = JSON.parse(jsonString)
  alert(JSON.stringify(jsn, null, '    '));

}();                   
RUN

Strict Mode

엄격한 코드 검사 및 수행 모드

여러 언어적 결함을 전부 오류 처리.

보다 견고한 프로그래밍을 가능하게 해줍니다.

사용법은 두개

// 전역에 적용
"use strict"

// 함수 스코프에만 적용
function iamStrict() {
  "use strict"
};                  

눈에 띄는 몇가지만 소개해 보겠습니다.

식별 이름으로 eval, arguments 불가

// All Syntax Error!
var eval = 17;
var arguments = "hello";
++eval;
arguments++;
function x(eval) { }
function arguments() { }

중복되는 이름의 객체 속성, 함수 인자 불가

// x 속성이 중복
var obj = { x: 1, x: 2 }; // Syntax Error!

// 인자 이름 중복 불가
function sum(a, a, b) { // Syntax Error!
    return a + a + b;
}                       

언어적 보안 강화

암묵적 전역 변수 불가


implictVar = 10 // Syntax Error!
function fn() {
    "use strict"
    hello = "world"; // Syntax Error
}

with 문 사용불가

// Syntax Error
with({ hello: "world" }) {
    console.log(hello);
}

언어적으로 삭제 불가능인 속성 삭제 불가


delete Object // Syntax Error
delete String // Syntax Error
delete Object.prototype // Syntax Error
delete Element // Syntax Error
delete undefined // Syntax Error

객체 불변, get/set, 리플렉션, prototype ...

Prototype

객체의 원본

JavaScript 객체는 전부 Object 생성자의 prorotype이 원본

생성된 객체에서 Prototype 객체를 알아내는 방법

Object.getPrototypeOf(obj)

!function() {
    var today = new Date();

    // ECMA Script 3nd -
    alert("__proto__ # " + (today.__proto__ === Date.prototype));
    alert("constructor.prototype # " + (today.constructor.prototype === Date.prototype));

    // ECMA Script 5th +
    alert("Object.getPrototypeOf # " + (Object.getPrototypeOf(today) === Date.prototype));
}();
RUN

Create Object # ES3

!function() {

    var javacafe = {};

    var javacafe = new Object();

    var JavacafeConstructor = function() {};
    JavacafeConstructor.prototype = {
        constructor: JavacafeConstructor
        // etc ...
    };
    javacafe = new JavacafeConstructor();

}();

Create Object # ES5

Object.create(prototype)!

!function() {

    var step = Object.create({
        "hello": "world",
        "hi": "안녕"
    });
    alert(step.hi);
    alert(step.hello);

}();
RUN

Bonus

!function() {
    var obj1 = {}, obj2 = new Object(), obj3 = Object.create(null);

    var byLiteral = Object.getPrototypeOf(obj1),
        byConst = Object.getPrototypeOf(obj2),
        byCreate = Object.getPrototypeOf(obj3);

    alert("byLiteral # " + byLiteral);
    alert("byConst # " + byConst);
    alert("byCreate # " + byCreate);

}();
RUN

Object Freeze

불변 객체 생성

(function() {

    // Object.freeze
    var java = { study: "재미있어요" };
    Object.freeze(java); // 객체 불변으로 만들기

    java.study = "하기싫어요";
    alert("java.study : " + java.study); // 변하지 않음

    java.play = "놀러갑시다";
    alert("java.play : " + java.play); // 새 속성 추가 불가

    delete java.study;
    alert("java.study : " + java.study); // 삭제도 불가

})();               
RUN

Object Key

객체의 모든 속성명을 얻고싶은데...

// 객체의 속성명 singer, leader, video 를 가져오고 싶다.
var PinkLUV = {
    singer: "Apink",
    leader: "Chorong, Park",
    title: "LUV",
    video: "www.youtube.com/watch?v=8dVjSvLzD1I"
}

for - in

!function() {
        var PinkLUV = {
            singer: "Apink",
            leader: "Chorong, Park",
            title: "LUV",
            video: "www.youtube.com/watch?v=8dVjSvLzD1I"
        },
        keys = [];
    for(var k in PinkLUV) {
        if(!PinkLUV.hasOwnProperty(k)) continue;
        keys.push(k);
    }
    alert(keys);
}();                   
RUN

Object.keys

!function() {
    var PinkLUV = {
        singer: "Apink",
        leader: "Chorong, Park",
        title: "LUV",
        video: "www.youtube.com/watch?v=8dVjSvLzD1I"
    };

    alert(Object.keys(PinkLUV));
}();
RUN

Object get/set

(function() {

    var Browser = {
        get name() { return this.defaultBrowser; },
        set name(v) { this.defaultBrowser = v + " 를 사용중입니다."; }
    };

    Browser.name = "Internet Explorer"; // set.
    alert(Browser.name); // get.

    Browser.name = "Chrome"; // set.
    alert(Browser.name); // get.

})();               
RUN

Object.defineProperty

(function() {
    Object.defineProperty(객체, 키이름, 디스크립터);
})();

Object.defineProperty

!function() {
    var Coupang = {
        name: "쿠팡"
    };
    Object.defineProperty(Coupang, 'age', {
        value: 4
    });

    alert(Coupang.name);
    alert(Coupang.age);

    // !!!!????
    alert(JSON.stringify(Coupang));
    alert(Object.keys(Coupang));

}();
RUN

Ddescriptor properties!

  • value
    값.
  • writable
    값을 덮어쓸 수 있는지 여부. 기본값은 false
!function() {
    var Coupang = {};
    Object.defineProperty(Coupang, 'name', {
        value: "쿠팡",
        writable: false
    });

    Coupang.name = "티켓몬스터";

    alert(Coupang.name);

}();
RUN
  • configurable
    객체 재정의 허용여부. 기본값은 false
  • enumerable
    객체 프로퍼티를 외부 노출 기능 (keys, for-in, JSON.stringify...)에 노출할지 여부. 기본값은 false
try {
    var Coupang = {};
    Object.defineProperty(Coupang, 'name', {
        value: "쿠팡", configurable: false
    });
    alert("keys > [ " + Object.keys(Coupang) + " ]");

    Object.defineProperty(Coupang, 'name', {
        value: "티켓몬스터", enumerable: true
    });
    alert(Object.keys(Coupang));
}
catch(ex) {
    alert(ex);
}
RUN
try {
    var Coupang = {};
        Object.defineProperty(Coupang, 'name', {
        value: "쿠팡", configurable: true, enumerable: true
    });
    alert("keys > [ " + Object.keys(Coupang) + " ]");

    Object.defineProperty(Coupang, 'name', {
        value: "티켓몬스터", enumerable: true
    });

    alert("Coupang.name > " + Coupang.name);
}
catch(ex) {
    alert(ex);
}
RUN
  • get
    필드의 값을 참조할 경우 실행되는 getter 함수를 지정
  • set
    필드의 값을 할당할 경우 실행되는 setter 함수를 지정
function ECommerce() {
    var name = "쿠팡";

    Object.defineProperty(this, 'descriptionText', {
        set: function(_name) { name = _name },
        get: function() { return name + ", 한국의 E-커머스 회사입니다." }
    });

}
var eCommerce = new ECommerce();
alert(eCommerce.descriptionText);
eCommerce.descriptionText = "티켓몬스터";
alert(eCommerce.descriptionText);
RUN

Object.create + descriptor

var javarouka = Object.create({}, {
    name: {
        value: "이항희",
        enumerable: true
    },
    age: {
        value: 36,
        writable: true,
        configurable: true
    }
});
alert(javarouka.name + " / " + javarouka.age);

// try modify properties
javarouka.name = "JavaRouka";
javarouka.age = 29;
alert(javarouka.name + " / " + javarouka.age);
RUN

Array

다양한 메서드 추가

Array.indexOf

인자로 준 요소가 해당 배열의 어느 인덱스인지 반환.
없다면 -1.

!function() {
    var javarouka = { name: "이항희", job: "programer", gender: "male" },
        sora = { name: "강소라", job: "actress", gender: "female" },
        suzi = { name: "수지", job: "singer", gender: "female" };

    var groups = [ javarouka, sora, suzi ];

    var index = groups.indexOf(suzi);
    alert("suzi's index " + index);
}();
RUN

Array.some

하나라도 true를 반환하면 true.

!function() {
    var groups = [
        { name: "이항희", job: "programer", gender: "male" },
        { name: "강소라", job: "actress", gender: "female" },
        { name: "수지", job: "singer", gender: "female" }
    ];

    // 남자가 한명이라도 있는지?
    var r = groups.some(function(v){ return v.gender === 'female'; });
    alert("남자가 있나? " + r);

    // 프로그래머가 한명이라도 있는지?
    r = groups.some(function(v){ return v.job === 'programer';  });
    alert("프로그래머가 있나? " + r);
}();
RUN

Array.every

some 과 비슷하나 전부 true를 반환해야 true.

(function() {
    var groups = [
        { name: "이항희", job: "programer", gender: "male" },
        { name: "강소라", job: "actress", gender: "female" },
        { name: "수지", job: "singer", gender: "female" }
    ];

    // 전부 남자...?
    var r = groups.every(function(v){ return v.gender === "male" });
    alert("전부 남자? " + r);
})();               
RUN

Array.map

배열 요소를 순회하며 반환값으로 구성되는 새 배열을 반환.

(function() {
    var groups = [
        { name: "이항희", job: "programer", gender: "male" },
        { name: "강소라", job: "actress", gender: "female" },
        { name: "수지", job: "singer", gender: "female" }
    ];

    var r = groups.map(function(v){
        return v.name + " : " + v.job;
    });
    alert(JSON.stringify(r, null, '    '));
})();               
RUN

Array.reduce

배열의 모든 요소에 대해 지정된 콜백을 호출.
콜백 함수의 반환 값은 다음 콜백 함수 호출시 인수로 제공된다.

!function() {

    // 초기누적값은 옵션.
    // 초기누적값이 없다면 첫 콜백 실행시 첫번째 요소를 사용하며 콜백 호출은 두번째부터.
    arrayObj.reduce(callback[, 초기누적값])

    // 각 콜백에 전달되는 인자
    // 0. 전 콜백에서 반환된, 누적값 혹은 실행 시 준 초기값
    // 1. 순회하는 현재 요소
    // 2. 요소 인덱스
    function callback(누적값, 현재값, 인덱스) { /* ... */ }

}();

Array.reduce

!function() {
    var groups = [
        { name: "이항희", job: "programer", gender: "male" },
        { name: "강소라", job: "actress", gender: "female" },
        { name: "수지", job: "singer", gender: "female" }
    ];

    var genderCount = groups.reduce(function(accumulated, current) {
        if(!( current.gender in  accumulated )) accumulated[ current.gender ] = 0;
        accumulated[ current.gender ] += 1;
        return accumulated;
    }, {});

    alert(JSON.stringify(genderCount, null, '    '));
}();
RUN

기타

String.trim Native Support

Immutable 'undefined' type

currentTimeMillis

(function() {
    var requireTrim = "   이제 섹션 다 끝나갑니다~             ";
    alert("["+requireTrim.trim()+"]");  // 아주 빠르다.

    // 전 버전에서는 undefined 값을 마음대로 바꿀 수 있었지만
    // 이제는 불가능
    undefined = 1000;
    alert(typeof undefined);

    // 현재 타임스탬프 값 반환
    alert(Date.now());
})();               
RUN

ECMA Script 5th 디바이스 호환 테이블

ECMAScript 5 compatibility table BY @Kangax

THE END

Thank You!