본문 바로가기

3.구현/HTML5&Javascript

[javascript] 자바스크립트 중급자는 이것만 알고가자 1

들어가기

중급자라면 이정도는 알고 있으면 좋다고 생각하는 부분을 모았습니다. 저도 다시 공부하는 기분으로 정리했습니다.

작성자: ospace114@empal.com, http://ospace.tistory.com/

함수

함수 호이스팅

호이스팅(hoisting, 끌어올림)은 코드 실행 전에 함수, 변수, 클래스, 선언문 들이 맨위로 이동하는 과정이다. 예를 들어 함수 선언 전에 함수를 호출할 수 있는 것이다.

f(); // 선언 전에 호출 가능
function f() { ... }

함수 스코프

let은 정의 전까지 존재하지 않지만, var은 스코프(Scope) 안에 어디든지 사용 가능하다.
var로 선언한 변수는 호이스팅(hoisting, 끌어올림)이라는 메커니즘을 따른다. 이는 선언만 가져올뿐 할당은 가져오지 않는다.

console.log(x); // referece 에러 발생
let x = 3;
console.log(x); // undefined
var x = 3;

이로 인해 var보다 let이 더 안전한 코딩이 될 수 있다.

클로저

클로저(Closure)는 주변 상태에 대한 참조와 함수에 대한 조합이다. 즉, 외부 함수의 데이터를 그 데이터를 조작하는 내부 함수를 연관시켜서 실행하게 해준다. 그렇기 때문에 함수가 선언되는 시점에 데이터가 어떤 범위에 있는지 신중하게 확인해야 한다.

let globalFunc;
{
    let blockVar = 'a';
    globalFunc = function() {
        console.log(blockVar);
    }
}
globalFunc(); // 'a'

const f = (function() {
    let count = 0;
    return function() {
        return `called ${++count} time(s)`;
    }
})();

f(); // called 1 time(s)
f(); // called 2 time(s)

배열

임의 위치에 추가/제거

let arr = [1, 2, 3];
arr.splice(1, 0, 5, 6]; // arr = [1, 5, 6, 2, 3]
arr.splice(1, 2); //arr = [1, 2, 3]

배열 안에 요소 교체

let arr = [1, 2, 3, 4];
arr.copyWithin(1, 2); // arr = [1, 3, 4, 4]
arr.copyWithin(2, 0, 2); // arr = [1, 3, 1, 3]
arr.copyWithin(0, -3, -1) // arr = [3, 1, 1, 3]

특정 값으로 채우기

const arr = new Array(5).fill(1); // arr = [1, 1, 1, 1, 1]
arr.fill('a'); // arr = ['a', 'a', 'a', 'a', 'a']
arr.fill('b', 1); // arr = ['a', 'b', 'b', 'b', 'b']
arr.fill('c', 2, 4); // arr = ['a', 'b', 'c', 'c', 'b']

Symbol

심볼은 많이 사용하지 않은 기능 중에 하나이지만, 잘 사용하면 훌륭한 키로 사용할 수 있다.

const SYM = Symbol();

const o = { a:1, b:2, c:3, [SYM]:4 };
for(let prop in o) {
    if(!o.hasOwnProperty(prop)) continue;
    console.log(`${prop}:${o[prop]}`);
}

Object.keys(o).forEach(prop => console.log(`${prop}: ${o[prop]}`));

클래스

클래스는 함수 확장에 대한 syntax sugar이다. 일반적인 OOP의 클래스와는 차이가 있다. 그러나 알고 있으면 유용하기에 간단하게 정리해보았다.

간단한 클래스 생성

class Car {
    constructor() {
    }
}

const car1 = new Car();
const car2 = new Car();

console.log(car1 instanceof Car); // true
console.log(car1 instanceof Array) // false

클래스에 속성과 메소드 추가

class Car {
    constructor(make, model) {
        this.make = make;
        this.model = model;
        this.useGears = ['P', 'N', 'R', 'D'];
        this.useGear = this.useGears[0];
    }
    shift(gear) {
        if(!~this.useGears.indexOf(gear))
            throw new Error(`Invalid gear: ${gear}`);
        this.useGear = gear;
    }
}

const car1 = new Car('Tesla', 'Model s');
car1.shift('D');
console.log(car1.useGear);

메소드 수정 방지

class Car {
    constructor(make, model) {
        this.make = make;
        this.model = model;
        this._useGears = ['P', 'N', 'R', 'D'];
        this._useGear = this._useGears[0];
    }
    get useGear() { return this._useGear; }
    set useGear(value) {
        if(!~this._useGears.indexOf(value))
            throw new Error(`Invalid gear: ${value}`);
        this._useGear = value;
    }

    shift(gear) { this.useGear = gear; }
}

아직까지는 _useGear로 접근가능한 상태이다. 프로퍼티를 완벽하게 보호하려면,

const Car = (function() {
    const carProps = new WeakMap();
    class Car {
        constructor(make, model) {
            this.make = make;
            this.model = model;
            this._useGears = ['P', 'N', 'R', 'D'];
            carProps.set(this, { useGear: this._useGears[0] });
        }
        get useGear() { return carProps.get(this).useGear; }
        set useGear(value) {
            if(!~this._useGears.indexOf(value))
                throw new Error(`Invalid gear: ${value}`);
            carProps.get(this).useGear = value;
        }

        shift(gear) { this.useGear = gear; }
    }
    return Car;
})();

클래스는 함수이다

function Car(make, model) {
    this.make = make;
    this.model = model;
    this.useGears = ['P', 'N', 'R', 'D'];
    this.useGear = this.useGears[0];
}

정적 메소드

class Car {
    static getNextVin() {
        return Car.nextVin++;
    }
    constructor(make, model) {
        this.make = make;
        this.model = model;
        this.vin = Car.getNextVin();
    }
}

Car.nextVin = 0;

상속

class Vehicle {
    constructor() {
        this.passengers = [];
    }
    addPassenger(p) {
        this.passengers.push(p);
    }
}

class Car extends Vehicle {
    constructor() {
        super();
    }
    deployAirbags() {
        console.log("BWOOSH!");
    }
    toString() {
        return 'in string';
    }
}

const v = new Vehicle();
v.addPassenger("Frank");
v.passengers;
const c = new Car();
c.addPassenger("Alice");
// v.deployAirbags(); // Error: deployAirbags is not function
c.deployAirbags();

믹스인

상속과는 다른 기능을 확장하는 방법으로 실행시점에 필요할 때에 확장가능하다.

function makeInsurable(o) {
    o.addInsurancePolicy = function(p) { this.insurancePolicy = p }
    o.getInsurancePolicy = function()  { return this.insurancePolicy }
    o.isInsured = function() { return !!this.insurancePolicy }
}

class InsurancePolicy{};

const car1 = new  Car();
makeInsurable(car1);
car1.addInsurancePolicy(new InsurancePolicy());

또는

makeInsurable(Car.prototype);
const car1 = new  Car();
car1.addInsurancePolicy(new InsurancePolicy());

함수 이름 충돌 가능성이 있기에 Symbol을 사용한 믹스인이다.

const ADD_POLICY = Symbol();
const GET_POLICY = Symbol();
const IS_INSURED = Symbol();

function makeInsurable(o) {
    o[ADD_POLICY] = function(p) { this[ADD_POLICY] = p };
    o[GET_POLICY] = function()  { return this[GET_POLICY] };
    o[IS_INSURED]  = function() { return !!this[IS_INSURED] };
}

참고

  1. 모질라 MDN
  2. Mark Pilgrim, Dive Into HTML5
  3. ES6은 ECMA-262 ECMAScript 2015 언어 명세
  4. JavaScript Weekly, http://javascriptweekly.com
  5. Node Weekly, http://nodeweekly.com
  6. HTML5 Weekln, http://html5weekly.com
  7. 악셀 라우슈마이어(es6관련), http://www.2ality.com
  8. Nolan Lawson(자바스크립트개발자), http://nolanlawson.com
  9. David Walsh(es6 generator), https://davidwalsh.name
  10. @kangax, http://perfectionkills.com
반응형