블로그 이미지
윤영식
Full Stacker, Application Architecter, KnowHow Dispenser and Bike Rider

Publication

Category

Recent Post

2013. 9. 28. 17:45 Languages/JavaScript

자바스크립트의 상속과 성능향상을 위하여서는 Prototype Pattern을 갖는 Prototypal Inheritance를 이용한다.




개념

  - ECMAScript 5에서 Object.create 로 가능하다 

var myCar = { name: "Ford Escort", drive: function () { console.log( "Weeee. I'm driving!" ); }, panic: function () { console.log( "Wait. How do you stop this thing?" ); } }; // Use Object.create to instantiate a new car var yourCar = Object.create( myCar ); // Now we can see that one is a prototype of the other 

console.log( yourCar.name );

 

  - 오브젝트 확장할 때 

var vehicle = { getModel: function () { console.log( "The model of this vehicle is.." + this.model ); } };

// 두번째 인자에서 확장한다 Mixin... var car = Object.create(vehicle, { "id": { value: MY_GLOBAL.nextId(), // writable:false, configurable:false by default enumerable: true }, "model": { value: "Ford", enumerable: true } }); 


  - Object.create를 사용하지 않고 Construct Function의 prototype 프로퍼티를 이용하여 prototypal inheritance 구현 

var vehiclePrototype = { init: function ( carModel ) { this.model = carModel; }, getModel: function () { console.log( "The model of this vehicle is.." + this.model); } }; function vehicle( model ) { function F() {};  // 일급 클래스 펑션의 prototype을 vehiclePrototype객체로 정의 

// 상단 이미지에서 Foo의 프로퍼티인 propertype 변수에 Foo.prototype 객체를 참조한다

F.prototype = vehiclePrototype;

// new를 하면 vechiclePrototype객체가 f 변수에 할당 된다 var f = new F(); f.init( model ); return f; } var car = vehicle( "Ford Escort" ); 

console.log(car.getModel());

다른 방법으로 proto 전달 

var beget = (function () { function F() {} return function ( proto ) { F.prototype = proto; return new F(); }; 

})();



다양한 Object creation으로 메소드 확장하기

  - 메소드안에 정의 

    + getInfo는 Car 생성때 마다 생성되므로 메모리를 차지한다 (안좋음) 

function Car(make, model, level, color, warranty) {
    this.make     = make;
    this.model    = model;
    this.level    = level;
    this.color    = color;
    this.warranty = warranty;
    
    this.getInfo = function() {
        return this.make + ', ' + this.model + ', ' + this.level + ', '+ this.color + ', ' + this.warranty;
    };
}
 
var aCar = new Car('Acura', 'TL', 'Sport', 'blue', 5);

console.log(aCar.getInfo());  //displays Acura, TL, Sport, blue, 5 


  - 메소드를 외부에서 Prototype에 지정하기 

    + 필요한 시점에 메소드를 추가할 수 있다 예) Car.prototype.accelerate 

function Car(make, model, level, color, warranty) {
    this.make     = make;
    this.model    = model;
    this.level    = level;
    this.color    = color;
    this.warranty = warranty;
}
 
Car.prototype.getInfo = function() {
  return this.make +','+ this.model +','+ this.level +','+ this.color +','+ this.warranty;
};
 
var aCar = new Car('Acura', 'TL', 'Sport', 'blue', 5);
alert(aCar.getInfo());  //displays Acura, TL, Sport, blue, 5
 
Car.prototype.accelerate = function() {
    return 'How fast?';
};

console.log(aCar.accelerate()); //displays "How fast?"


  - Prototype Pattern을 이용

    + prototype에 필요한 메소드를 Literal 객체로 지정한다 

function Car(make, model, level, color, warranty) {
    this.make     = make;
    this.model    = model;
    this.level    = level;
    this.color    = color;
    this.warranty = warranty;
}
 
Car.prototype = {
    getInfo: function () {
      return this.make +','+ this.model +','+ this.level +','+ this.color +','+ this.warranty;
    }

};


  - Revealing Prototype Pattern을 이용

    + prototype에 객체를 생성하여 할당한다 

var UsedCar = function(mileage) {
    //Define a variable unique to each instance of UsedCar
    this.mileage = mileage;
};
 

UsedCar.prototype = new Car('Honda', 'Civic', 'LX', 'gray', 2);


// Revealing Module Pattern 형식

UsedCar.prototype = function () { }();



<참조>

  - 원문 : The Prototype Pattern

  - Some Prototype Pattern 사용하기 

  - Revealing Prototype Pattern 사용하기

  - 드미트리 소스시코브의 JavaScript Core

posted by 윤영식
2013. 9. 14. 12:18 Languages/JavaScript

Mediator 패턴은 충돌을 해결하고 협력할 수 있도록 해주는 것이다. 즉 시스템에 많은 컴포넌트들이 있다면 서로의 커뮤니케이션을 중재하고 제어한다. 예로 공항 관제실을 생각해 보자. 비행기가 컴포넌트이고 이들의 이착륙을 관리하는 관제실이 Mediator가 된다. 




개념 이해하기 

  - Mediator

    Colleague 오븐젝트와 통신하기 위한 인터페이스를 정의한다 

  - ConcreteMediator

    Colleague 클래스를 알고 Colleague 오브젝트의 레퍼런스를 가지고 있는다. 

    Colleague 끼리 메세지를 전달하고 통신토록 해준다 

  - Colleague Classes

    Mediator 오브젝트에 레퍼런스를 유지한다. Mediator를 통하여 다른 Colleague와 통신한다 



PubSub 만들기 

  - Mediator Core

  - publish() 와 subscribe() 

var mediator = (function(){

    // Storage for topics that can be broadcast or listened to

    var topics = {};


    // Subscribe to a topic, supply a callback to be executed

    // when that topic is broadcast to

    var subscribe = function( topic, fn ){

        if ( !topics[topic] ){ 

          topics[topic] = [];

        }

        topics[topic].push( { context: this, callback: fn } );

        return this;

    };


    // Publish/broadcast an event to the rest of the application

    var publish = function( topic ){

        var args;

        if ( !topics[topic] ){

          return false;

        } 


        args = Array.prototype.slice.call( arguments, 1 );

        for ( var i = 0, l = topics[topic].length; i < l; i++ ) {

            var subscription = topics[topic][i];

            subscription.callback.apply( subscription.context, args );

        }

        return this;

    };


    return {

        publish: publish,

        subscribe: subscribe,

        installTo: function( obj ){

            obj.subscribe = subscribe;

            obj.publish = publish;

        }

    };


}());

  - Jack Lawson의 Mediator.js 를 가져다 쓰면 된다 

/*! * Mediator.js Library v0.9.5 * https://github.com/ajacksified/Mediator.js * * Copyright 2013, Jack Lawson * MIT Licensed (http://www.opensource.org/licenses/mit-license.php) * * For more information: http://thejacklawson.com/2011/06/mediators-for-modularized-asynchronous-programming-in-javascript/index.html * Project on GitHub: https://github.com/ajacksified/Mediator.js * * Last update: June 13 2013 */ (function(global, factory) { 'use strict';

// 다양한 환경에서 적용할 수 있도록 모듈에 대한 export if(typeof exports !== 'undefined') { // Node/CommonJS exports.Mediator = factory(); } else if(typeof define === 'function' && define.amd) { // AMD define('mediator-js', [], function() { global.Mediator = factory(); return global.Mediator(); }); } else { // Browser global global.Mediator = factory(); } }(this, function() { 'use strict'; // We'll generate guids for class instances for easy referencing later on. // Subscriber instances will have an id that can be refernced for quick // lookups.

// 글로벌 유니크 아이디를 생성하는 로직이다. 등록된 Colleague 오브젝트에 (즉, Subscriber Instances)

// 아이디를 부여해 준다 function guidGenerator() { var S4 = function() { return (((1+Math.random())*0x10000)|0).toString(16).substring(1); }; return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4()); } // Subscribers are instances of Mediator Channel registrations. We generate // an object instance so that it can be updated later on without having to // unregister and re-register. Subscribers are constructed with a function // to be called, options object, and context. // Colleague인 Subscriber 펑션 정의 function Subscriber(fn, options, context){ if(!(this instanceof Subscriber)) { return new Subscriber(fn, options, context); } this.id = guidGenerator(); this.fn = fn; this.options = options; this.context = context; this.channel = null; }

// Subscriber Prototype 객체 정의

// Subscriber에 등록한 fn, context, options를 변경할 수 있다 Subscriber.prototype = { // Mediator.update on a subscriber instance can update its function,context, // or options object. It takes in an object and looks for fn, context, or // options keys. update: function(options){ if(options){ this.fn = options.fn || this.fn; this.context = options.context || this.context; this.options = options.options || this.options; if(this.channel && this.options && this.options.priority !== undefined) { this.channel.setPriority(this.id, this.options.priority); } } } }; // 채널을 통하여 Subscriber 의 등록/제거 관리 

// Subscriber에 정보 publish function Channel(namespace, parent){ if(!(this instanceof Channel)) { return new Channel(namespace); } this.namespace = namespace || ""; this._subscribers = []; this._channels = []; this._parent = parent; this.stopped = false; } // A Mediator channel holds a list of sub-channels and subscribers to be fired // when Mediator.publish is called on the Mediator instance. It also contains // some methods to manipulate its lists of data; only setPriority and // StopPropagation are meant to be used. The other methods should be accessed // through the Mediator instance. // Mediator 채널이 sub-channel 목록을 가지고 있다 

// Channel Prototype 객체 정의 Channel.prototype = { addSubscriber: function(fn, options, context){ var subscriber = new Subscriber(fn, options, context); if(options && options.priority !== undefined){ // Cheap hack to either parse as an int or turn it into 0. Runs faster // in many browsers than parseInt with the benefit that it won't // return a NaN. options.priority = options.priority >> 0; if(options.priority < 0){ options.priority = 0; } if(options.priority >= this._subscribers.length){ options.priority = this._subscribers.length-1; } this._subscribers.splice(options.priority, 0, subscriber); }else{ this._subscribers.push(subscriber); } subscriber.channel = this; return subscriber; }, // The channel instance is passed as an argument to the mediator subscriber, // and further subscriber propagation can be called with // channel.StopPropagation().

// Mediator에 등록한 Subscriber에 아규먼트 전달 멈춤 stopPropagation: function(){ this.stopped = true; },


getSubscriber: function(identifier){ var x = 0, y = this._subscribers.length; for(x, y; x < y; x++){ if(this._subscribers[x].id === identifier || this._subscribers[x].fn === identifier){ return this._subscribers[x]; } } }, // Channel.setPriority is useful in updating the order in which Subscribers // are called, and takes an identifier (subscriber id or named function) and // an array index. It will not search recursively through subchannels. // subchannel을 돌면서 우선순위를 업데이트 한다 setPriority: function(identifier, priority){ var oldIndex = 0, x = 0, sub, firstHalf, lastHalf, y; for(x = 0, y = this._subscribers.length; x < y; x++){ if(this._subscribers[x].id === identifier || this._subscribers[x].fn === identifier){ break; } oldIndex ++; } sub = this._subscribers[oldIndex]; firstHalf = this._subscribers.slice(0, oldIndex); lastHalf = this._subscribers.slice(oldIndex+1); this._subscribers = firstHalf.concat(lastHalf); this._subscribers.splice(priority, 0, sub); },

// sub channel 등록/삭제 addChannel: function(channel){ this._channels[channel] = new Channel((this.namespace ? this.namespace + ':' : '') + channel, this); }, hasChannel: function(channel){ return this._channels.hasOwnProperty(channel); }, returnChannel: function(channel){ return this._channels[channel]; }, removeSubscriber: function(identifier){ var x = this._subscribers.length - 1; // If we don't pass in an id, we're clearing all if(!identifier){ this._subscribers = []; return; } // Going backwards makes splicing a whole lot easier. for(x; x >= 0; x--) { if(this._subscribers[x].fn === identifier || this._subscribers[x].id === identifier){ this._subscribers[x].channel = null; this._subscribers.splice(x,1); } } }, // This will publish arbitrary arguments to a subscriber and then to parent // channels. // 퍼블리싱 : subscriber에 아규먼트 전달 publish: function(data){ var x = 0, y = this._subscribers.length, called = false, subscriber, l, subsBefore,subsAfter; // Priority is preserved in the _subscribers index. for(x, y; x < y; x++) { if(!this.stopped){ subscriber = this._subscribers[x]; if(subscriber.options !== undefined && typeof subscriber.options.predicate === "function"){ if(subscriber.options.predicate.apply(subscriber.context, data)){ subscriber.fn.apply(subscriber.context, data); called = true; } }else{ subsBefore = this._subscribers.length; subscriber.fn.apply(subscriber.context, data); subsAfter = this._subscribers.length; y = subsAfter; if (subsAfter === subsBefore - 1){ x--; } called = true; } } if(called && subscriber.options && subscriber.options !== undefined){ subscriber.options.calls--; if(subscriber.options.calls < 1){ this.removeSubscriber(subscriber.id); y--; x--; } } } if(this._parent){ this._parent.publish(data); } this.stopped = false; } };

// Mediator 펑션 function Mediator() { if(!(this instanceof Mediator)) { return new Mediator(); } this._channels = new Channel(''); } // A Mediator instance is the interface through which events are registered // and removed from publish channels. Mediator.prototype = { // Returns a channel instance based on namespace, for example // application:chat:message:received // sub channel은 namespace 기반으로 만들어 진다 getChannel: function(namespace){ var channel = this._channels, namespaceHierarchy = namespace.split(':'), x = 0, y = namespaceHierarchy.length; if(namespace === ''){ return channel; } if(namespaceHierarchy.length > 0){ for(x, y; x < y; x++){ if(!channel.hasChannel(namespaceHierarchy[x])){ channel.addChannel(namespaceHierarchy[x]); } channel = channel.returnChannel(namespaceHierarchy[x]); } } return channel; }, // Pass in a channel namespace, function to be called, options, and context // to call the function in to Subscribe. It will create a channel if one // does not exist. Options can include a predicate to determine if it // should be called (based on the data published to it) and a priority // index. // sub channel에 namespace 기반으로 Subscriber 등록하기 subscribe: function(channelName, fn, options, context){ var channel = this.getChannel(channelName); options = options || {}; context = context || {}; return channel.addSubscriber(fn, options, context); }, // Pass in a channel namespace, function to be called, options, and context // to call the function in to Subscribe. It will create a channel if one // does not exist. Options can include a predicate to determine if it // should be called (based on the data published to it) and a priority // index. // 한번 호출하고 끝내기 once: function(channelName, fn, options, context){ options = options || {}; options.calls = 1; return this.subscribe(channelName, fn, options, context); }, // Returns a subscriber for a given subscriber id / named function and // channel namespace // 아이디와 명칭으로 Subscriber 얻어오기 (identifier === GUID) getSubscriber: function(identifier, channel){ return this.getChannel(channel || "").getSubscriber(identifier); }, // Remove a subscriber from a given channel namespace recursively based on // a passed-in subscriber id or named function. remove: function(channelName, identifier){ this.getChannel(channelName).removeSubscriber(identifier); }, // Publishes arbitrary data to a given channel namespace. Channels are // called recursively downwards; a post to application:chat will post to // application:chat:receive and application:chat:derp:test:beta:bananas. // Called using Mediator.publish("application:chat", [ args ]); // 퍼블리싱 publish: function(channelName){ var args = Array.prototype.slice.call(arguments, 1), channel = this.getChannel(channelName); args.push(channel); this.getChannel(channelName).publish(args); } };

// 일반적으로 사용하는 이름으로도 Aliasing // Alias some common names for easy interop Mediator.prototype.on = Mediator.prototype.subscribe; Mediator.prototype.bind = Mediator.prototype.subscribe; Mediator.prototype.emit = Mediator.prototype.publish; Mediator.prototype.trigger = Mediator.prototype.publish; Mediator.prototype.off = Mediator.prototype.remove; // Finally, expose it all. Mediator.Channel = Channel; Mediator.Subscriber = Subscriber; Mediator.version = "0.9.5"; return Mediator; })); 



사용해 보기 

  - Plunker에서 수행한 내역 보기 : http://plnkr.co/edit/k9dT9Ms7xzbx77GhVraA?p=preview

  - HTML

<!DOCTYPE html>

<html>


  <head>

    <script data-require="jquery@*" data-semver="2.0.3" src="http://code.jquery.com/jquery-2.0.3.min.js"></script>

    <link data-require="bootstrap-css@*" data-semver="3.0.0" rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" />

    <link data-require="bootstrap@*" data-semver="3.0.0" rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" />

    <script data-require="bootstrap@*" data-semver="3.0.0" src="//netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script>

    <link rel="stylesheet" href="style.css" />

    <script src="mediator.js"></script>

    <script src="script.js"></script>

  </head>


  <body>

    <h1>Chat</h1>

    <form id="chatForm">

      <label for="fromBox">Your Name:</label>

      <input id="fromBox" type="text" />

      <br />

      <label for="toBox">Send to:</label>

      <input id="toBox" type="text" />

      <br />

      <label for="chatBox">Message:</label>

      <input id="chatBox" type="text" />

      <button type="submit">Chat</button>

    </form>

    <div id="chatResult"></div>

  </body>


</html>


  - JavaScript

// create Mediator.js

var mediator = new Mediator();


$("#chatForm").submit(function(){

    // Collect the details of the chat from our UI

    var text = $( "#chatBox" ).val(),

        from = $( "#fromBox" ).val(),

        to = $( "#toBox" ).val();

        

    // newMessage 채널로 퍼블리싱 하기 

    // Publish data from the chat to the newMessage topic

    mediator.publish( "newMessage" , { message: text, from: from, to: to } );

    return false;

});


// Append new messages as they come through

function displayChat( data ) {

    var date = new Date(),

        msg = data.from + " said \"" + data.message + "\" to " + data.to;

  

    $( "#chatResult" ).prepend("<p>" + msg + " (" + date.toLocaleTimeString() + ")</p>");

}


// Log messages

function logChat( data ) {

    if ( window.console ) {

        console.log( data );

    }

}


// newMessage 명칭으로 Subscriber 등록하기 

// Subscribe to new chat messages being submitted

// via the mediator

mediator.subscribe( "newMessage", displayChat );

mediator.subscribe( "newMessage", logChat );


  - 수행내역



<참조>

  - 원문 : Mediator Pattern

  - OODesign : Mediator Pattern

  - http://thejacklawson.com/Mediator.js/

  - 오늘의 명언 : 내가 감동하면 남도 감동할 수 있다. 그것이 SNS정신이다 

posted by 윤영식
2013. 9. 7. 17:32 Languages/JavaScript

Publish/Subscribe 를 위한 Observer 패턴을 알아보자. JavaScript은 Web의 Virtual Machine이다. JavaScript는 웹앱이다. 




개념

  - Observable

     Client에 observers를 붙였다 띄었다 하는 동작을 가진 추상 클래스 또는 인터페이스  (Publisher)

    ConcreteObservable

     오브젝트의 상태정보를 가지고 있다가 상태가 변경되면 등록된 Observers 에게 변경 정보를 알려준다 

    Observer

     정보를 알려줄 Operation을 정의한 추상 클래스 또는 인테페이스 (Subscriber)


  - 어떤 오브젝트가 오브젝트 목록을 관리하다가 어떤 상태의 변화가 감지되면 해당 오브젝트 목록들에 변경값을 Notify한다 

  - 상태 정보 변경에 관심을 갖는 오브젝트를 Observer Object라 하고 Observer Object를 register/remove 한다 

  - GoF의 정의 

"One or more observers are interested in the state of a subject and register their interest with the subject by attaching themselves. When something changes in our subject that the observer may be interested in, a notify message is sent which calls the update method in each observer. When the observer is no longer interested in the subject's state, they can simply detach themselves."



구현 

  - Observer Pattern 구현을 위해 필요한 컴포넌트 

    > Subject : observers 목록을 제어(register/remove)한다 

    > Observer : Subject로부터 상태변화를 받을 오브젝트 인터페이스를 제공한다 

    > ConcreteSubject : 상태 변경을 observers 에게 notify를 수행한다 

    > ConcreteObserver : ConcreteSubject에 Observer 인터페이스를 생성하여 등록한다 

// Object 목록 관리 Constructor Function

function ObserverList(){

this.observerList = []; } ObserverList.prototype.Add = function( obj ){ return this.observerList.push( obj ); }; ObserverList.prototype.Empty = function(){ this.observerList = []; }; ObserverList.prototype.Count = function(){ return this.observerList.length; }; ObserverList.prototype.Get = function( index ){ if( index > -1 && index < this.observerList.length ){ return this.observerList[ index ]; } }; ObserverList.prototype.Insert = function( obj, index ){ var pointer = -1; if( index === 0 ){ this.observerList.unshift( obj ); pointer = index; }else if( index === this.observerList.length ){ this.observerList.push( obj ); pointer = index; } return pointer; }; ObserverList.prototype.IndexOf = function( obj, startIndex ){ var i = startIndex, pointer = -1; while( i < this.observerList.length ){ if( this.observerList[i] === obj ){ pointer = i; } i++; } return pointer; }; ObserverList.prototype.RemoveAt = function( index ){ if( index === 0 ){ this.observerList.shift(); }else if( index === this.observerList.length -1 ){ this.observerList.pop(); } }; // Extend an object with an extension

function extend( extension, obj ){ for ( var key in extension ){ obj[key] = extension[key]; } }


  - Subject 구현, Observer에는 Update메소드를 구현하면 된다

// 1. Subject Constructor Function을 정의한다

// (Constructor Pattern의 prototype 방식 사용)

function Subject(){ this.observers = new ObserverList(); } Subject.prototype.AddObserver = function( observer ){ this.observers.Add( observer ); }; Subject.prototype.RemoveObserver = function( observer ){ this.observers.RemoveAt( this.observers.IndexOf( observer, 0 ) ); };

// Observer가 상태 정보를 받는다 
Subject.prototype.Notify = function( context ){
  var observerCount = this.observers.Count();
  for(var i=0; i < observerCount; i++){
    this.observers.Get(i).Update( context );

};



// 2. Observer 구현 

// The Observer
function Observer(){
  this.Update = function(){
    // ...
  };
}


  - ConcreteSubject, ConcreteObserver

// HTML

1) button을 클릭하면 observable checkbox를 페이지에 추가

2) checkbox가 subject로 동작한다. check가 되면 모든 checkbox에 상태변경을 알려준다(notify) 

3) 새로운 checkbox를 container에 추가한다 

<button id="addNewObserver">Add New Observer checkbox</button>
<input id="mainCheckbox" type="checkbox"/>
<div id="observersContainer"></div>

// Script 

// References to our DOM elements var controlCheckbox = document.getElementById( "mainCheckbox" ), addBtn = document.getElementById( "addNewObserver" ), container = document.getElementById( "observersContainer" ); // 3. Concrete Subject

// Subject의 notify 메소드를 호출하는 구문이 존재 // Extend the controlling checkbox with the Subject class extend( new Subject(), controlCheckbox ); // Clicking the checkbox will trigger notifications to its observers controlCheckbox["onclick"] = new Function( "controlCheckbox.Notify(controlCheckbox.checked)" ); addBtn["onclick"] = AddNewObserver;

// 4. Concrete Observer function AddNewObserver(){ // Create a new checkbox to be added var check = document.createElement( "input" ); check.type = "checkbox"; // Extend the checkbox with the Observer class extend( new Observer(), check ); // Override with custom update behaviour check.Update = function( value ){ this.checked = value; }; // Add the new observer to our list of observers // for our main subject controlCheckbox.AddObserver( check ); // Append the item to the container container.appendChild( check ); }



Publish/Subscribe

  - Publish/Subscriber와 유사하다. Observer나 Pub/Sub 사용이유는 틀린 계층간의 결합시 Loose Coupling으로 관계설정에 유용한다 

var pubsub = {};

(function(q) {

    var topics = {},
        subUid = -1;

    // Publish or broadcast events of interest
    // with a specific topic name and arguments
    // such as the data to pass along
    q.publish = function( topic, args ) {

        if ( !topics[topic] ) {
            return false;
        }

        var subscribers = topics[topic],
            len = subscribers ? subscribers.length : 0;

        while (len--) {
            subscribers[len].func( topic, args );
        }

        return this;
    };

    // Subscribe to events of interest
    // with a specific topic name and a
    // callback function, to be executed
    // when the topic/event is observed
    q.subscribe = function( topic, func ) {

        if (!topics[topic]) {
            topics[topic] = [];
        }

        var token = ( ++subUid ).toString();
        topics[topic].push({
            token: token,
            func: func
        });
        return token;
    };

    // Unsubscribe from a specific
    // topic, based on a tokenized reference
    // to the subscription
    q.unsubscribe = function( token ) {
        for ( var m in topics ) {
            if ( topics[m] ) {
                for ( var i = 0, j = topics[m].length; i < j; i++ ) {
                    if ( topics[m][i].token === token) {
                        topics[m].splice( i, 1 );
                        return token;
                    }
                }
            }
        }
        return this;
    }; 

}( pubsub ));



<참조>

  - 원문 : Observer Pattern

  - OODesign : Observer Pattern

  - PubSubJS for JavaScript

  - RadioJS for Pub/Sub

posted by 윤영식
2013. 9. 7. 17:11 Languages/JavaScript

클래스의 인스턴스를 오직 하나만 생성하는 Singleton Pattern을 알아보자. 기술은 향기와 같다. 막아도 냄새를 맡을 수 있는 것이다. 옳바른 향기를 피워보자. 




개념

  - Java에서의 싱글톤 정의 패턴 

    1) 자신을 private 멤버변수로 선언

    2) private 생성자 정의

    3) public static synchronized 형태 getInstance() 메소드 정의

    4) 자신의 변수가 null인지 아닌지 체크 

class Singleton

{

    private static Singleton instance; // 1) 

    private Singleton() // 2) 

    {

        ...

    }


    public static synchronized Singleton getInstance() // 3)

    {

        if (instance == null) // 4) 

            instance = new Singleton();

        return instance;

    }

    ...

    public void doSomething()

    {

        ... 

    }

}


  - Global Scope에 격리되고 내부 변수를 공유한다

    > Constructor Function을 정의한다 

    > Closure 객체안에서 Constructor Function을 한번만 new하는 로직을 넣고 Closure객체를 return한다 

var mySingleton = (function () {

// Instance stores a reference to the Singleton var instance; function Init() { // define Constructor Function // Singleton // Private methods and variables function privateMethod(){ console.log( "I am private" ); } var privateVariable = "Im also private"; var privateRandomNumber = Math.random(); return { // Public methods and variables publicMethod: function () { console.log( "The public can see me!" ); }, publicProperty: "I am also public", getRandomNumber: function() { return privateRandomNumber; } }; }; return { // return the Closure Object // Get the Singleton instance if one exists // or create one if it doesn't getInstance: function () {

// 생성이 안되었으면 최초에 한번만 init 한다 if ( !instance ) { instance = Init(); } return instance; } }; })();


// 잘 못된 예제 var myBadSingleton = (function () { // Instance stores a reference to the Singleton var instance; function init() { // Singleton var privateRandomNumber = Math.random(); return { getRandomNumber: function() { return privateRandomNumber; } }; }; return { // Always create a new Singleton instance getInstance: function () {

instance = init(); return instance; } }; })(); var singleA = mySingleton.getInstance(); var singleB = mySingleton.getInstance(); console.log( singleA.getRandomNumber() === singleB.getRandomNumber() ); // true var badSingleA = myBadSingleton.getInstance(); var badSingleB = myBadSingleton.getInstance(); console.log( badSingleA.getRandomNumber() !== badSingleB.getRandomNumber() ); // true



<참조>

  - 원문 : Singleton Pattern

  - OODesign : Singleton Pattern

 - 널리 Singleton을 사용해 보자

posted by 윤영식
2013. 9. 7. 16:53 Languages/JavaScript

SNS의 정신은 감동, 배려, 책임이다. 기술이 아니다, 자바스크립트를 행복하게 사용하려면 모듈단위의 개발이 되어야 한다. 모듈 패턴을 알아보자.



종류

  - 견고한 애플리케이션을 개발하기 위하여 모듈단위 개발이 필요하다 

  - 모듈을 구현하기 위한 방법

    > The Module pattern

    > Object literal notation

    > AMD modules

    > CommonJS modules

    > ECMAScript Harmony modules



Object Literals

  - curly braces {} 를 사용한다 

  - new 키워드가 필요없다 

  - 오브젝트에 프로퍼티의 동적 추가가 가능하다. 예) myObjectLiteral.sencondKey = value; 

var myObjectLiteral = {

  variableKey: variableValue,

  functionKey: function() {...},

  someObject: {...}

}



Module Pattern

  - class처럼 private, public를 캡슐화 하여 정의하는 방법이다 

  - 별도 name space 사용으로 global scope의 오염을 방지한다 

  - ClosureIIFE (Immediately Invoked Function Expression) 을 사용한다 

var testModule = (function() {

  var counter = 0;

  return {  // Closure

    incrementCounter: function() {

      return counter++;

    },

    resetCounter: function() {

      console.log('counter is', counter);

      counter = 0;

    }

  };

})(); // IIFE


testModule.incrementCounter();

testModule.resetCounter();

// output : counter is 1

  - 예제에서 counter는 global scope에서 숨겨져이다. 즉, private variable같다, counter 접근은 두개의 method로만 가능하다. 물론 function도 private 으로 정의가능하다 

var myNamespace = (function () {

  var myPrivateVar, myPrivateMethod;

  // A private counter variable
  myPrivateVar = 0;

  // A private function which logs any arguments
  myPrivateMethod = function( foo ) {
      console.log( foo );
  };

  return {

    // A public variable
    myPublicVar: "foo",

    // A public function utilizing privates
    myPublicFunction: function( bar ) {

      // Increment our private counter
      myPrivateVar++;

      // Call our private method using bar
      myPrivateMethod( bar );

    }
  };
 

})();



Module Pattern 변형 

  - Import mixins : global scope의 변수를 module에 import 하는 방법

    jQuery 와 Underscore 객체를 익명함수의 파라미터로 받아서 import 한다

// Global module
var myModule = (function ( jQ, _ ) {
  
    function privateMethod1(){
        jQ(".container").html("test");
    }

    function privateMethod2(){
      console.log( _.min([10, 5, 100, 2, 1000]) );
    }
    
    return{
        publicMethod: function(){
            privateMethod1();                
        }            
    };
   
// Pull in jQuery and Underscore
}( jQuery, _ ));
 

myModule.publicMethod();  

  

  - Export : global module 만들기 

    var module = {} 객체를 만들어서 return 하여 export 한다 

// Global module
var myModule = (function () {

    // Module object 
  var module = {},
    privateVariable = "Hello World";
  
  function privateMethod() {
    // ...
  }

  module.publicProperty = "Foobar";
  module.publicMethod = function () {
    console.log( privateVariable );
  };
  
  return module;
 

}());



<참조>

  - 원문 : 모듈패턴

posted by 윤영식
2013. 9. 7. 16:07 Languages/JavaScript

구글러 오스마니님의 JavaScript Design Pattern 글에서 패턴을 요약해 본다. 생성자 패턴에 대해 알아보자 



개념

  - 자바스크립트는 모든것이 오브젝트이다 

  - Object constructor가 지정한 타입의 오브젝트를 생성하는데 사용한다 

 


Object Creation

  - 오브젝트를 만드는 3가지 기본 방법 

    비어있는 오브젝트를 만들어 리턴하는 것이다 

var newObject = {};


var newObject = Object.create( null );


var newObject = new Object();


  - 오브젝트에 Key, Value 할당하는 4가지 방법

// 1. Dot syntax

newObject.someKey = "hi dowon";

var key = newObject.someKey;


// 2. Square bracket syntax

newObject["someKey"] = "hi youngsik";

var key = newObject["someKey"];


// ECMAScript 5 olny

// 3. Object.defineProperty

Object.defineProperty( newObject, "someKey", {

  value: "hi dowon",

  writable: true,

  enumerable: true,

  configurable: true

});


var defineProp = function( obj, key, value ) {

  config.value = value;

  Object.defineProperty( obj, key, config);

};


var person = Object.create( null );

defineProp( person, "car", "Seoul");

defineProp( person, "house", "apt");


// 4. Object.defineProperties

// set properties

Object.defineProperties( newObject, {

   "someKey": {

     value: "hi dowon",

     writable: true

   }, 

   "anotherKey": {

      value: "youngsik",

      writable: false

    }

});



Basic Constructors

  - Constructor Function에 new 키워들 사용하여 신규 오브젝트를 생성한다

  - Constructor Function안의 this는 새로 생성된 오브젝트의 레퍼런스이다 

// define constructor function

function Car(model, price) {

  this.model = model;

  this.price = price;

  

  this.toString = function() {

    return this.model + ', ' + this.price;

  }

}


// new로 생성하면 Car.prototype을 통하여 신규 오브젝트를 만들어 주소를 넘겨주면 hd가 주소 레퍼런스를 가진다

var hd = new Car('santafe', 1000);


// 찍어보자

Car {model: "santafe", price: 1000, toString: function}


  - Function은 prototype 프로퍼티를 가지고 있다. prototype은 객체를 레퍼런스를 하는데 별도로 지정하지 않으면 자신의 property 객체를 레퍼런스 한다. 여기서 Car.property 는 Car.property 객체를 레퍼런스 한다  

function Car(model, price) {

  this.model = model;

  this.price = price;

}


Car.prototype.toString = function() {

  return this.model + ', ' + this.price;

}


console.log(hd.toString());

kia, 5000


  Car Constructor Function은 {this.model: <value>, this.price: <value>, prototype: Car.prototype}  가 되고, Car.prototype 객체는 {constructor: Car, toString: function() {...}} 이다. Car Constructor Function의 prototype 프로퍼티는 Car.prototype 객체를 레퍼런싱하고, Car.prototype객체의 constructor 프로퍼티를 통하여 Car Constructor Function을 레퍼런싱하여 상호 참조한다. prototype와 constructor 프로퍼티는 writable이다. 이를 이용해 JavaScript의 Prototypal Inheritance 방식을 사용할 수 있다.



<참조>

  - 오스마니 생성자패턴

  - JavaScript Prototype

posted by 윤영식
2013. 8. 30. 17:24 Languages/JavaScript
자바스크립트 개발시 피해야하는 중요한 3가지 사용패턴에 대해서 알아보자 


1. Object 상속에 의한 오염

  - Object의 prototype을 통하여 확장을 하지마라

  - 예를 보자 

    두번째 Object.prototype.e 를 확장하면 모든 오브젝트에 e="E"가 불필요하게 반영된다. 즉 모든 오브젝트에 상속된다 

var obj = {a: "A", b: "B", c: "C", d: "D"};
for (var key in obj) {
   alert(key +': '+obj[key]); //displays "a: A", "b: B", "c: C", "d: D"
}
 
// Anti-Pattern
Object.prototype.e = "E";
for (var key in obj) {
   alert(key +': '+obj[key]); //displays "a: A", "b: B", "c: C", "d: D", "e: E"
}
 
var obj2 = {a2: "A2", b2: "B2", c2: "C2", d2: "D2"};
for (var key in obj2) {
   alert(key +': '+obj2[key]); //displays "a2: A2", "b2: B2", "2c: C2", "d2: D2", "e: E"

}

  - 상속의 방법

function Person(name, sex) { 

  Person.prototype.populationCount++; 
  Person.prototype.getName=function(){ return name }; 
  Person.prototype.getSex=function(){ return sex }; 
  Person.prototype.setSex=function(newSex){ sex = newSex; }; 
  Person.prototype.die=function(){ Person.prototype.populationCount -- ; };
}
Person.prototype.populationCount=0;
 
var rob = new Person('Rob','male');
var jeanie = new Person('Jeanie','female');
alert(rob.populationCount);  // displays 2
 
//the following creates a new public property for rob and sets it to 12
rob.populationCount+=10;
alert(rob.populationCount); //displays 12
alert(jeanie.populationCount); //still displays 2
 
Child.prototype = Object.create(Person.prototype);
Child.prototype.constructor = function Child(name, sex, age) {
    //call the parent constructor
    Person(name, sex);
    Child.prototype.getAge = function() { return age; };
}
var child = new Child('Ralph', 'male', 3);
 
alert(child.getName()); //displays "Ralph"
alert(child.getAge());  //displays 3



2. Global Namespace에 의한 오염

  - 보통 var를 사용하지 않고 x=2 처럼 하면 window.x=2가 되어 전역영역을 오염시킨다. 따라서 변수선언시 반드시 var 를 사용한다 

  - Group 변수를 통해서 오염을 방지하자 

   var Finance = {};

Finance.INTEREST_RATE = 2.5;
Finance.calcAnnualizedInterest(startVal, endVal) {
  //...
}

  - 또는 ECMAScrpt 5 "use strict"를 script 맨위체 포함시킨다. 또는 function 안에 놓고 제한된 scope에서 strict mode를 사용할 수도 있다

  function strictFunc(){

  "use strict";
  // code ...
}

  - 예를 보자 

    i 의 결과값으로 5가 나온다. Global Namespace를 오염시키는 function - foo, bar -, variable - i - 같은 것은 절대 사용하지 말자 

function foo() { return 1; }

function bar() { for (i=0; i<5; i++) {} }

 

i = foo();

bar();



3. True, False에 대한 부적절한 사용에 의한 오염 

  - 자바스크립트에서 zero (0), empty string (""), null, undefined, NaN 은 모둔 false 값이다 

  - 비교문이나 루프문에서 조건 사용시 축약한다  

// Anti-Pattern

if (testString != undefined && testString != '') {

  //do something

  }


// 변경 : 위 조건절과 동일 

if (!testString) {

  //do something
}

  - ===, ==! 를 항상 사용한다 

// 주의

   var zero = 0;

if (zero === false) {
  // doesn't execute because zero is 0, not false
}



<참조>

  - 원문 : Three JavaScript Anti-Patterns and How to avoid them

  - Another Anti-Patterns 

  - Anti-Patterns를 피하고 성능을 향상시키는 방법들 : Slide 및 동영상

posted by 윤영식
2013. 6. 28. 21:33 Languages/JavaScript

오늘은 강남토즈에서 김영보선생님의 자바스크립트 객체지향 세미나를 듣고 있다. 간단히 요약해 본다 




1. OOP

  - object.method() : 전형적인 OOP 형태 

  - Object is "{}" 즉, JSON 포멧

     object is "new Function"

  - 자바스크립트에서 function은 함수가 아니라 키워드일 뿐이다 

    ex) function sales() {}  -->  { "sales" : function(){} } 으로 대치가능하다

  - 수행 순서

     + 소스

       function A() {

   function B() {}

       } 

     + A() 호출하면 A안의 내용만 { "A" : function(){} } 만 만든다 { "B": function(){} } 은 안만든다.

       즉 수행하는 시점에 해석되고 객체로 적재되어 수행한다 

     + 초기 랜더링에 필요한 부분만 해석되어 수행하면 성능 향상을 할 수 있다

  - oop의 information hiding을 위하여 closure를 사용한다 



2. 메커니즘

  - 메모리 stack 생성 -> Execution Context 저장하고 EC단위로 수행한다 

  - Executable Code type : global code, function code, eval code (eval 코드는 쓰지말라! 5차부터 에러남)

  - Execution Context : Executable code의 실행 단위.  Context : 문맥, 처리단위, 묶음 

  - Stack안에 EC가 들어가는데 가장 먼저 만들어진것이 stack의 바닥부터 위로 채워넣는다. 수행은 위에서 빼서 수행 그때 GC 발생

  - object를 EC에 바인딩 : EC = { VO : { key: value } }   *VO = Variable Object

     모든 EC는 VO와 연동한다 

  - VO 구성요소 : variable, function, function parameter 

    예) 펑션을 만나면 Variable Object를 만든다. { key: value } 이런 형태임



3. VO 메커니즘

  - Global Context, function context로 분리  

  - Global Context : 하나만 존재, Global object도 VO와 연동. 즉, Global object와 VO는 같음

  - 코드가 Hoisting되어서 function -> variable 순으로 호출됨. 즉, function 호출

  - VO의 function context는 AO와 같음 *AO = Activation Object : function 호출시 AO를 만들어 수행하는 것이다 

  - function 실행되면

    + AO 생성 : function & AO 바인딩

    + function에 아규먼트 갯수와 상관이 없으므로 arguments 객체를 만들어줌 (배열아님)

    + 내부 function을 설정, 실행하는 것이 아니다 { key: value } 로 설정

  - scope chain은 scope binding의 추상개념이다



4. Property 탐색 & 클로져

  - property = { key: value } 형태 추상개념

  - Global Context -> Inner Function AO에서 탐색 

  - 클로져 (참조)

    + 내부변수를 참조하는 함수를 리턴함 == function code + [[Scope]]의 조합

    + function 생성 시점에 클로져 설정 



5. This

  - 모든 EC와 this와 관련이 있다

  - function단위로 this 오브젝트를 만든다 (this value)

  - this는 AO에 생긴다. AO는 function이다. 따라서 this는 function안에 존재한다 

  - EC = {VO/AO, value, this: value} 형태  

  - null : 프로세싱 한것. 값이 undefine에서 null이 된것이다 

    undefine : 프로세싱 안한것

  - this.a 했을 때 a가 undefine이면 this생성이 안된 경우일 것이다 (참조)



6. Prototype 

  - object는 prototype을 갖음 

  - [[function]].prototype.property_name 형식

  - this.property_name 으로 접근가능

  - prototype chain이 된다 : 상속개념 사용가능 -> 사용자제 

  - 렌더링시에 생성되어 수행되게 할려면 new 사용을 자제한다?

  - 사용이유 : prototype을 사용하면 new이후 this로 정의한 것을 접근할 수 있기 때문이다 

  - 객체를 1차레벨로 수평화 하라 -> 오브젝트를 불러서 사용한다 (Node.js의 require와 비슷하겠다) -> 접근성이 용이해 진다 


다음 강좌가 있으면 또 교육을 받아봐야 겠다. 정말 설명을 명확하게 잘 해주신다.



<참조>

  - MSDN 자바스크립트 객체지향 이야기

  - NHN이 말하는 자바스크립트의 클로져 이야기

posted by 윤영식
2013. 2. 20. 16:27 Languages/JavaScript

자바스크립트 개발자가 되고 싶다면 이렇게 배우자. 


1) 이럴때 자바스크립트를 배우자

  - 모던 웹 애플리케이션을 만들고자 할때

  - Backbone.js, Node.js, MongoDB와 같은 프레임워크나 자바스크립트 인터프리터를 탑재한 곳에서 개발 할때


2) 이렇게 배우진 말자

  - 온라인에서 튜토리얼이나 동영상 보고 배우지 말자

  - 초보자에게 더글라스 클락포드의 "JavaScript: The Good Parts" 책은 적합하지 않다 


3) 1~ 3주간 이렇게 배우자 

  - 공부할 책들

    + Professional JavaScript for Web Developers

    + JavaScript: The Definition Guid 6th (번역서 : 자바스크립트 완벽 가이드)

    + 코드아카데미 강좌 이용

  - 코드아카데미 Web Fundamentals 공부

  - Professional JavaScript 책 1장에서 3장까지 공부

  - 코드아카데미 JavaScript Fundamentals 공부

  - Professional JavaScript 책 4장에서 5장까지 쭉 공부

  - Definition Guide는 6장까지 쭉 공부 (번역본 읽어보니 주옥같은 내용이다. 강추)

  - Professional JavaScript 책 18장까지 공부 (원문에 챕터를 너무 분류했다)

  - Definition Guide를 다시 12장까지 공부 (서버사이드 자바스크립트 마스터)


4) 4~5주간 이렇게 배우자 

  - 코드아카데미 jQuery 배우기

  - Professional JavaScript 책 18장부터 끝까지 공부

  - Definition Guide 13장부터 끝까지 공부 (클라이언트 사이드 자바스크립트 마스터)

  - 이후엔 애플리케이션을 만들어 본다 


5) 6주차

  - Node.js를 이용하여 서버 코딩을 한다

  - Backboen.js를 이용하여 클라이언트 코딩을 한다 


6) 이후 좋은 참조 사이트들 공부하기 

  - 자바스크립트 디자인 패턴

  - 자바스크립트 Garden (번역)



<참조>

  - 원문 : http://javascriptissexy.com/how-to-learn-javascript-properly/

posted by 윤영식
2013. 1. 27. 20:10 Languages/JavaScript

this를 자바스크립트에서 만나게 되면 이게 어느 인스턴스의 this인지 파악하기가 난해하다. this는 context(Execute Context)와 Scope(유효범위)에 대한 부분을 잘 알아야 한다. 

 

1) function안에서 변수 접근 권한

function someFunc() {
    var _this = this;
    something.on("click", function() {
        console.log(_this);
    });
};

  - var _this = this; 문구는 왜 사용하는 것일까?

  - 첫번재 유효범위(First Scope)는 Global Scope 이다

    + 모든 변수(Variable)과 함수(Function)은 global이다. 

    + Global Scope는 window 객체이다 

    + window.x = 9; 라고 property를 설정하면 어디서나 접근이 가능하다 

 

function myFunc() {
    var x = 5;
});
console.log(x); // undefined

  - x 는 myFunc()안에서 초기화 되었고, myFunc()안에서만 접근이 가능하다 

  - var 키워드를 사용하지 않으면 자동으로 global 유효범위로 만들어진다 

  - var 키워드를 사용하면 function이 호출 될때 생성되는 context에 지역변수로(local variable) 포함된다

function myFunc() {
    x = 5;
});
console.log(x); // 5

 

  - Outer Function에 정의된 모든 변수는 다른 Inner Function에서 접근을 할 수 있다

function outer() {
   var x = 5;
   function inner() {
      console.log(x); // 5
   }
   inner();
}

 

  - Outer Function에서는 Inner Function의 변수를 접근 할 수 없다 

function outer() {
    var x = 5;
    function inner() {
        console.log(x); //5 
        var y = 10;
    }
    inner();
    console.log(y); //undefined
}

 

2) this의 유효범위 

  - this는 function이 호출될 때 자동으로 설정되는 변수이다 

  - 변수의 값은 function이 어떻게 호출되냐에 따라 틀려진다

  - 자바스크립트에서 함수를 호출하는 주된 몇가지 방법을 가지고 있다. (주로 사용하는 3가지 방법)

    + function이 method처럼 호출하기 : when a function is called as a method === 오브젝트.function은 메소드라 칭함

    + 자기자신위에 호출하기  : when a function is called on it's own === function 자체가 호출 오브젝트.function이 아닐 경우

    + event handler로서 호출하기 : when a function is called as an event handler == 이벤트 핸들러 수행시 호출되는 function

function foo() {
   console.log(this); //global object
};

myapp = {};
myapp.foo = function() {
   console.log(this); //points to myapp object
또는 (같은 내용)
myapp = {
   foo: function() {
     console.log(this); //points to myapp object
   }
}

var link = document.getElmentById("myId");
link.addEventListener("click", function() {
   console.log(this); //points to link
}, false);

  - addEventLister를 사용하여 function을 붙이면 this의 값이 변경된다 : this값은 caller로 부터 function에 전달된다 

 

 

$("myLink").on("click", function() {
    console.log(this); //points to myLink (as expected)
    var _this = this;  //store reference
    $.ajax({
        //ajax set up
        success: function() {
            console.log(this); //points to the global object. Huh?
            console.log(_this); //better!
        }
    });
});

  - myLink을 click하면 function이 수행된다. 이때 이벤트 핸들러로서 펑션의 this는 myLink DOM element의 참조로 설정된다

  - 그러나 ajax 정규호출의 this는 global object로 this 값을 설정한다. 이는 "event handler"도 "오브젝트 method"도 아닌 function이기 때문이다

  - 위와 같이 하는 것이 function을 호출하는 곳에서 this에 대한 값을 정확히 사용하기 위한 방법이다

 

 

3) Crockford on JavaScrpt

  - 백발의 중년 신사분이 JavaScript 언어에 대해 설명을 하고 있다. 우리나라에선 상상할 수 없는 일이겠지, 큰 세미나에 가봐야 젊은 친구들의 열정만을 엿볼 수 있는 부분을 생각해 보면 그들의 환경이 참 부럽다.

  - 15분경의 동영상에서 this의 호출에 대해서 보자 

  - 31분경에서 정확한 Closure 사용법 설명

 

 

<참조> 

  - 원문 : Scope and this in JavaScript

  - Scope 한글 블로깅 연재

  - Naver Dev JavaScript 클로져, 유효범위

posted by 윤영식
2012. 12. 23. 22:02 Languages/JavaScript

Java의 Spring과 같이 확장성이 높은 JavaScript 프레임워크를 만들기 위한 아키텍쳐에 대하여 알아보자 


  - Module Layer를 두어 모듈 단위로 모든것을 개발한다.

  - SandBox Layer 를 두어 Module이 상호 통신하는 인터페이스와 보안, 가이드 역할을 한다.

  - Core Layer 를 두어 Module의 LifeCycle을 책임진다. 모듈간 통신과 에러 핸들링, 확장을 수행. 

  - Base Library Layer 를 두어 기본 함수와 유틸을 제공한다. : JQuery, YUI 같은 것


> 유지보수 가능한 JavaScript 전략


> 고성능을 내기위한 JavaScript 전략 (Responsive JavaScript 코딩 전략)


  - UI Thread 작업시에는 Queue를 이용하라

  - JavaScript Execution 시간을 50ms 이상으로 하지 마라

  - JavaScript 다운로드하고 파싱하고 실행할때까지 HTML Rendering이 멈춘다. JavaScript은 무조건 HTML 맨 밑에 배치함

  - JavaScript를 쪼개 놓지 말고 합쳐서 다운로드 타임으로 조금이라도 줄여라 

  - 필요한 시점에 동적으로 스크립트 파일을 로딩한다

  - Reflow와 Repaint 횟수를 줄이는 것이 성능 최적화에 중요함

posted by 윤영식
2012. 12. 23. 21:51 Languages/JavaScript

Java에서 할 수 있다면 ECMAScript에서도 할 수 있지 않을까? 테스트를 어떻게 하는지 알아보자 


  - named function을 사용하라 (가급적 anonymous function 사용 배제)

  - namespace를 사용한다

  - 속성, 펑션은 return {key:value} 클로저 객체로 리턴한다

  - 펑션 내부 계산로직에 대해 return 하여 검증할 수 있게 한다

  - Functional Testing

    + gebish.org

    + zombie.labnotes.org

    + phantomjs.org


  - 단위 테스트 

    + QUnit ( docs.jquery.com/QUnit )

    + sinonjs.org

    + mocha

    + JsTestDriver

    + YUITest


> JsTestDriver를 통한 효율적인 JavaScript 테스팅하기



  - JsTestDriver와 Maven 연결 테스트하기

  - JsTestDriver 설정을 통하여 Jenkins에 xUnit Plugin 연동하여 테스트

  - 여러 브라우저에 대하여 테스트하기 

  - http://tddjs.com 참조


> BDD(Behavior Driven Development) JavaScript 


posted by 윤영식
2012. 12. 21. 16:18 Languages/JavaScript

렌더링 과정에 대하여 알아보고, 렌더링시 성능 최적화할 수 있는 방법들을 살펴보자 (책. 4장 렌더링)



▶ 전체 브라우저 렌더링 처리 과정


  - 전체 브라우저 처리과정 : HTML DOM 트리 + CSS 스타일 구조체 = 렌더 트리 생성 (레이아웃 처리) -> 페인트 (화면에 표현)

  - DOM 트리 생성 : HTML을 DOM 요소 노드 트리 형태로 구성.

  - 스타일 구조체 생성 : CSS 정보를 스타일 구조체(Style Struct)로 생성한다. 

  - 렌더 트리 생성 : DOM 트리와 각 노드에 스타일 정보를 설정하여 화면에 표현할 노드로 구성한다. DOM 트리와 렌더 트리는 1:1 대응하지 않는다.

  - 레이아웃 처리 : 렌더 트리의 각 노드 크기가 계산되어 위치계산한다.

  - 페인트(Paint) : 렌더 트리를 순회하면서 페인트 함수를 호출해 노드를 화면에 표현한다.



▶ 리플로와 페인트


  - 리플로(Reflow) : Ajax와 같은 데이터를 가져와서 화면에 뿌릴라 치면 변경이 필요한 렌더 트리에 대한 유효성 확인 작업과 함께 노드의 크기와 위치를 다시 계산한다. 이과정을 리플로 = 레이아웃(Layout) = 레이아웃팅(Layouting)  이라 한다. (부모 노드의 변경은 자식 노드 변경 리플로도 야기한다)

  - 리페인트(Repaint) : 변경 영역의 결과를 표현하기 위해 화면이 업데이트 되는 것을 의미한다. 리페인트 = 리드로(Redraw) 

  - 리플로 와 리페인트는 비용이 높다. 발생하는 원인을 살펴보자 

    + DOM 노드의 변경 : 추가, 삭제

    + DOM 노드 노출 속성을 통한 변경 : 추가, 삭제, 특성 변경

    + 스크립트 애니메이션 : 애니메이션은 DOM, 스타일 등이 짧은 시간에 수차례 변경되는 것임

    + 스타일 : 추가, 변경

    + 사용자 액션 : 브라우저 크기 변경, 글꼴 크기 변경등 



▶ 리플로 & 페인트 최소화 방법


  - 작업 그룹핑 : DOM 요소 변경시 같은 형태끼리 그룹으로 묶어서 실행한다. 

  - 실행 사이클 : 루핑 구문 즉 setTimeOut같은 구문에서 DOM 요소 핸들링을 배제한다.

  - display 속성 : 루핑이 어쩔 수 없다면 

element.style.display="none"; 

<looping구문> 

element.style.display="block";  


  - 이렇게 하면 루핑구문에서 "리플로+페인트"가 계속 발생하지 않고, none과 block 할때 딱 두번만 발생한다.

  - 노드 복제 : 루핑도는 구문에서 노드 핸들링시 원복 노드를 복제하여 사용함 

var clone = element.cloneNode(true);

<looping 구문>

parentNode.replaceChild(clone, element);


  - createDocumentFragment() 사용 : DOM 객체와 별개의 새로운 DOM 객체를 생성하여 사용하면 렌더링 성능 향상 가능함.

var fragment = document.createDocumentFragment(); 

<looping 구문안에서 fragment.appendChile(elems[e]); 호출 > 


  - 캐싱 : 별도의 변수에 자주 사용하는 속성값 또는 메소드값을 저장하여 사용한다. 

  - 하드웨어 가속 렌더링 : GPU를 이용한다. 브라우져 마다 설정값이 틀림. (크롬 :  chrome://flags 로 GPU 설정 확인 가능)


posted by 윤영식
2012. 12. 13. 16:48 Languages/JavaScript

NHN의 자바스크립트 성능 이야기 Chapter 3을 간략히 정리한다. jsMatch 도구를 통한 성능 측정 수치를 제공하여 신뢰를 준다.



▶ 성능우위 문법


  - 배열 생성시 : var arr = new Array(); 보다 var arr = []; 를 사용한다

  - 배열 접근시 : arr.push(i) 보다 arr[i] = value 를 사용한다

  - 객체 생성시 : var obj = new Object(); 보다 var obj = {}; 를 사용한다

  - 객체 접근시 : obj["a"] = 1 보다 obj.a = 1; 를 사용한다 

  - 문자열 생성시 : var str = new String("aaa"); 보다 var str = "aaa"; 를 사용한다 

  - 문자열 연산시 : loop문에서 문자열 조작시에 str += "test"; 보다 arr=[]; loop{ arr[i]="test"; } arr.join(""); 을 사용한다 (String과 StringBuffer개념과 유사)

  - 정규표현식 : 탐색 대상을 축소한다. loop 문 안에 정규표현식 넣지 말고 밖에 놓아 한번만 컴파일 처리되게 한다. loop문에 있으면 계속 컴파일됨 



▶ 스코프 체인 탐색 줄이기

  - 스코프 체인 : 탐색 성능을 높이는 것이 본질 (자바스크립트 실행성능 저하는 변수, 객체, 함수의 메모리상 위치 탐색 작업임)

  - 스코프 체인 구성 = 활성화 객체(Activate Object) + 전역 객체(Global Object)

  - 활성화 객체 : 함수 내부 접근시 생성 (지역변수, this, arguments 객체) -> 함수 빠져 나오면 활성화 객체 제거 됨 

  - 실행 문맥(Execution Context) -> 스코프 체인 (1, 2) -> 활성화 객체 (1) -> 전역 객체 (2) 로 프로그램 수행됨 

  - 함수가 전역 속성 참조 순서 : 실행문맥 > 스코프 체인 > 활성화 객체 (함수) > 스코프 체인 > 전역 객체의 속성 참조 

  - 활성화 객체에서 전역 변수 사용시 : 함수 내부에 var LocalVariable = GlobalVariable;  식을 첨부 (전역속성 탐색을 제거함)



▶ 프로토타입 체인 탐색 줄이기

  - new 연산자로 생성된 인스턴스 객체는 생성자의 프로토타입을 참조하게 된다

  - var obj = new Object(); obj는 Object의 프로토타입 속성에 접근 할 수 있다

  - 자신의 객체만 접근하고 할 경우는 hasOwnProperty를 이요한다 (추가)



▶ 반복문 & 조건문


  - for~in 문은 가급적 쓰지 말라

  - for(~) 문안에 Array.length 구하는 함수등을 호출하지 말고, 외부에서 var length = Array.length를 설정한다. 스코프체인의 활성화 객체를 찾아가는 탐색 경로를 거치게 되므로 응답성능이 느려진다. 

  - 조건절을 빠른 탐색을 위한 알고리즘을 적절히 적용한다 : quick-sort, merge-sort, breadth first search, depth first search등 

posted by 윤영식
2012. 12. 12. 18:01 Languages/JavaScript

웹에플리케이션을 개발할 때 JavaScript 와 CSS 사용이 많다. 특히 요즘음 JavaScript Module화 패턴을 이용하여 JS파일이 기하 급수적으로 늘어나고 있으므로 네트워크 비용을 줄여야하는 문제에 놓인다. 성능을 향상 시킬 수 있는 NHN의 비법을 요약한다. (Chapter 2. 기본적인 웹사이트 최적화 방법)


  - 이미지 파일이 많을 경우 -> CSS Sprite를 이용한다 (N-MET OSS 이용)

    + 성능에 전송 파일의 사이즈보다 개수가 중요하다. 이는 크기보다 네트워크 전송 비용이 더 크기때문이다 

  - HTTP 헤더에 정적 파일 만료날짜를 추가 -> Client PC에 Caching토록 한다 

    + 캐싱여부 판단 기준은 "이름"과 "인터넷 주소" : src="aa_20111231.js" or src="aa.js?20111231" 로 표현 

    + 만료날짜를 서버에 설정 : Apache의 mod_expires 모듈 설정

  - JavaScript 파일 통합 -> 파일의 개수를 최소화하여 네트워크 비용을 줄임

  - 파일 크기 최소화 -> Gzip압축을 사용한다 (파일크기 1~2KB 이상일 때 압축 권장. 이보다 크기가 작으면 압축해제시 CPU 비용이 더 큼)

  - 쿠키 크기 최소화 -> 헤더 정보에 쿠키가 들어가면 데이터 크기가 커진다

    + 사용하지 않는 쿠키 삭제

    + 최상위 도메인 설정 되도록 사용하지 말자

    + 만료날짜 최대한 짧게 한다

    + 쿠키 정보 필요없는 정적 파일(이미지, js, css)는 별도 도메인으로 서비스한다

  - 렌더링 성능 향상 -> DOM Tree - Render Tree - 좌표 배치 - 브라우져 화면 그리기 시작

    + CSS는 <head> </head> 사이에 넣는다 

    + JavaScript는 </body> 위에 넣는다 : js 수행시 렌더링이 멈추어 하얀 페이지 보일 수 있음

    + 최초 페이지에서 AJAX 통신하면 화면 렌더링이 두번 되므로 최초 호출시 AJAX 배제하자 

    + 태그 중첩을 최소화 한다 : <table> 되도록 쓰지 말고, <div>와 CSS로 Layout을 잡는다 (중첩이 많으면 렌더링 느려짐)

posted by 윤영식
2012. 11. 26. 18:07 Languages/JavaScript

서버단에서 페이지별로 사용하는 템플릿 엔진들(FreeMaker, Velocity) 같은것이 있다면 클라이언트 화면단에서 AJAX를 통하여 받는 데이터를 통해 화면을 변경시켜주는 템플릿 엔진도 있다. JQuery에 jquery template 엔진(소개)이 있지만, 다른 것도 검토해 볼 필요가 있겠다. 


  - 추천 javascript template engine 

    + Pure : loop 단점 존재 

    + TrimPath : google code project (강력 추천)

  - 더 극적으로 socketstream을 이용하여 HTML rendering 방법 : Node.js에서 구동되는 bi-direction 통신가능 (GitHub 설명


Single Page Application(wikipedia)을 만들기 위해서는 javascript template engine이 필수 일듯 하다. 


posted by 윤영식
2012. 11. 12. 16:26 Languages/JavaScript

JavaScript를 하면서 어려웠던 새로운 개념이 Closure이다. 이참에 조금 정리해 보도록 하자 


▶ Closure 개념

  - 로컬 변수를 참조하고 있는 함수 내의 함수 (JavaScript 마스터북, Jpub출판, p180)

  - 즉, 클로저는 자신의 범위(Scope) 밖에 있는 변수들에 접근할 수 있는 함수를 의미한다 

  - inner function을 return 할때 closure가 된다 (원문)

function outerFn() {

var count=1;

return function (cnt) {

      count += cnt;

console.log(count);

}

}


var func = outerFn();

func(10); // 결과 값 11

func(10); // 결과 값 21

  1) func가 closure가 됨 : 내부 변수들 closure, private 변수 생성됨

  2) func(parameter) 호출해 줘도 내부 변수들은 다시 생성되는 것이 아니라 상태를 유지시켜서 참조 됨 

  3) 즉, 클로저가 만들어 지면서 내부 변수들은 별도로 유지되면서 상태값을 유지함

  4) 클로저의 참조를 제거하고 GC할려면 명시적으로 func=null; 함



다른 예제를 보자 
예) closure가 아님 
function foo(x) {
  var tmp = 3;
  function bar(y) {
    console.log(x + y + (++tmp));
  }
  bar(10);
}
foo(2); // 결과 값 16
foo(2); // 결과 값 16
foo(2); // 결과 값 16

예) closure 임
function foo(x) {
  var tmp = 3;
  return function (y) {
    console.log(x + y + (++tmp));
  }
}
var bar = foo(2); // bar is now a closure.
bar(10); // 결과 값 16
bar(10); // 결과 값 17
bar(10); // 결과 값 18

var bar2 = foo(2);
bar2(10); // 결과 값 16
bar2(10); // 결과 값 17

  - 로컬 변수를 계속 생성하지 않고 상태를 유지하면 사용할 수 있다 (마치 객체처럼 var bar2 = foo(2); 하면 또 다른 closure가 생성되면서 bar와 별도의 상태공유 변수 x와 tmp가 생성된다. 마치 클래스에서 객체생성하는 것 처럼 된다. closure 생성시에 arguement와 var로 선언된 variable 에 대한 참조가 가능해 진다. 결국 참조자들-x, tmp-가 일정 메모리에 저장되어 상태를 유지한다)


  - 위의 예에서 var bar = foo(2);를 하는 순간 foo function 객체의 x, tmp 변수의 레퍼런스를 closure 가 가진다 (즉, x, tmp 에 대한 변수 상태값이 유지되고, y 값은 bar(10)을 호출할 때 넘겨주는 값이 된다)


  - 클로저는 간단한 객체이다 (엄밀히 따져서 객체는 아니지만 객체식으로 치환해서 살펴보면 다음과 같다)

    + 클로저 : 객체 = 클로저를 감싸고 있는 부모 함수 foo : 생성자

    + 클로저 : 객체 = 클로저로 부터 참조되는 로컬 변수 tmp :  프로퍼티

    + 클로저 : 객체 = 클로저 자신 bar, bar2 : 메소드

    + 클로저 : 객체 = var bar = foo(2) 함수 호출 : 인스턴스화

    + 클로저 : 객체 = 클로저를 대입하는 변수 bar(10), bar2(10) : 인스턴스 <-- 요거 애매함


  - function 내부에 for문을 돌면서 function을 호출하여 callback 을 셋팅하는 오류 제거 (원문1, 원문2)


실제 사용해 보면서 좀 더 이해를 해나가야 겠다. JavaScript Master북의 글이 좀 더 쉽게 이해가 가니, 햇갈릴 때 책을 다시 정독해 보자. 


<참조>

  - 소스니 코브 : Closure 이해하기

  - Closure 정리 : 한글이라 이해하기 쉬울것임

  - Inside.JS : 한글 정리 예제 포함

posted by 윤영식
2012. 11. 12. 11:34 Languages/JavaScript

자바스크립트의 Function에는 Expression (FE) 과 Declaration (FD) 가 있다고 한다. 이에 대한 차이를 연구해 보자. 우선 존경해 맞이하는 소스니코브의 정리 내역과 구글링한 사이트를 읽고 정리한다 



▶ Function Declaration


  - 변수 할당 없는 이름있는 함수를 정의

  - "var"로 시작하지 않고 "function"으로 시작해야 한다(must)

- 명확한 이름을 가져야 한다
- 소스코드중에 오고 function body를 가지면 다른 function 안에도 위치한다
- entering the context stage에서 생성된다 (즉, function 안에 FD 존재않음)
- variable object(VO)에 영향을 준다 
function bar() {
return 3;

}


or


// 1) directly int the global context

function globalFD() {

// inside the body of another function

function innerFD() {}

}




▶ Function Expression


  - 변수 할당이 있는 표현식으로서 함수를 정의

  - 이름있는 또는 익명(anonymous) 함수 가능

  - FE는 "function"으로 시작하지 않아야 한다(must)

- 소스중에 expression position에 정의된다 
- 함수명은 optional 이다 
- variable object(VO)에 저장되지 않는다 
- code execution stage에서 생성된다 
// anonymous function expression
var a = function() {
return 3;
}

// named function expression
var a = function bar() {
return 3;
}

//self invoking function expression or immediately invoked function expression (IIFE, 즉시실행함수)
(function sayHello() {
alert("dowon hi");
})();

// in parentheses (grouping operator) can be only an expression
(function foo() {});

// in the array initialiser - also only expressions
[function bar() {}];

// comma also operators with expressions
1, function baz() {};


// FE is not available neither before the defintion

// because it is created at code execution phase.

alert(foo); // foo is not defined

(function foo() {});

// nor after, because it is not in the VO

alert(foo); // foo is not defined


// parentheses로 grouping 하기 

(function {}) ();

(function {}());


* Name Fuction Expression (NFE)

  - 재귀 호출을 위해 이름으로 자기자신을 호출할 수 있다 (이때 함수를 Variable Object에 저장할 필요가 있을까? 없다!)

  - 부모 context에서 생성할까? 노우!

  - FE로 하면 VO에 영향을 미치지 않는다 

(function foo(bar) {
if(bar) {
return;
}
foo(true); //foo name is available
})();

// but from the outside, correctly, is not  
// 즉, foo는 부모 scope에 없고 function의 [[Scope]]안에 specialObject에 저장한다 (하기 설명 참조)
foo(); // foo is not defined


위 소스 해석 (원문)

  - Interpreter가 code execution stage에서 FE를 만나면 FE를 생성하기 전에 special object를 만들고 현재 scope chain 앞에 놓는다 

  - FE 자신을 생성하고, 자신의 scope 프로퍼티에 scope chain 을 한다 (referencing)

  - special object에 unique 프로퍼티로 FE를 추가한다

  - 마지막에 parent scope chain으로 부터 special object를 제거 한다 

specialObject = {};

Scope = specialObject + Scope;

foo = new FunctionExpression;
foo.__scope__ = Scope;  // or foo.[[Scope]] = Scope;
specialObject.foo = foo; // [DontDelete], {ReadOnly}

delete Scope[0]; // remove specialObject from the front of scope chain



* FE의 특징

  - Variable Object (VO)에 속하지 않다 (스크립트 수행시 Execution Context-EC- 에 한 부분 = VO)

  - 따라서 FE는 Hoist안되고 FD만 Hoist 된다 (원문)

  - 그리고 FE는 자신의 내부 Scope에만 의미가 있지 외부(Outside)에는 의미가 전혀 없다 

  - VO = Function Declaration + Variable Declaration(var) + Arguments 들이 속한다 (FD가 최우선 순위 -> VD 순서로 Hoist됨)

  - FE로 하면 VO를 오염시키지 않기에 사용이 늘어나고 있다

예1)

bar(); // hoist 되어 정상 처리됨 

foo(); // error 발생 


function bar() { .... } // FD 

var foo = function() { .... }; // FE


예2)

var myFunction = function sameFunction() {

return myFunction === sameFunction; // true

};

console.log(myFunction()); // true 

console.log(sameFunction());  // [ReferenceError: contents is not defined] 에러 발생 



* FE에 사용하는 Grouping Operator 와 Immediately Invoked Fuction Expression (IIFE) 특징 

  - Grouping Operator인 () 은 variable과 function을 global scope와 격리된 private scope로 만드는 방법을 제공한다

  - IIFE는 jQuery, Underscore, Prototype, Dojo 등에서 global  scope을 깔끔하게 유지키 위해 private scope로 캡슐화, 모듈화에 중요하게 사용된다

  - 하기 예제는 외부세계로 부터 숨겨진 코드를 정의할 수 있고, private scope에서 동작하는 library variable을 만드는 것이다.

var Box = (function () {
    var contents = [];
    return {
        insert: function (thing) {
            contents.push(thing);
            return this;
        },
        count: function () {
            return contents.length;
        }
    };
})();

console.log(Box.insert('abc').insert(123).insert({
    width: 640, height: 360
}).insert(function () {
    return 'Hello!';
}).count()); // 4 를 출력

// This throws a ReferenceError since the JavaScript engine can't
// resolve the `contents` identifier.
// [ReferenceError: contents is not defined] 를 출력 
try {
    console.log(contents);  
} catch(err) { 
    console.log(err); 


  - IIFE는 function 호출처럼 arguements를 받고, 실행시간에 in-scope에 있는 모든 데이터에 대한 access가 가능한 Closure를 반환할 수 있다 (variable 상태 저장 가능)

var d, Saved, testSubject, i, l, x;

// Capture a start time.
d = new Date().getTime();

// `Saved` exposes a single API method `get` which returns that
// start time: the variable `d` is supplied as the argument
// identified within the function definition as `start`.
// 최기의  시작시간값을 저장하고 있다 
Saved = (function (start) {
    return {
        get: function () {
            return start;
        }
    };
})(d);

testSubject = {
    level1: {
        level2: {
            level3: {
                level4: {
                    val: 'value'
                }
            }
        }
    }
};

for (i = 0, l = 1000000; i < l; i++) {
    x = testSubject.level1.level2.level3.level4.val;
}

// Capture the time after diving five levels into the `testSubject`
// object literal one million times.
// 현재 시간을 구한다 
d = new Date().getTime();

// Output the current value of `d`.
// 현재 시간이 찍힘 
console.log(d); 

// Output the original value of `d`.
// 초기 IIFE의 시간이 찍힘
console.log(Saved.get());

// Output the difference which indicates how many seconds it took
// to do that object crawl.
console.log(d - Saved.get());

//------ 결과 
1352699019341 // d 현재 시간 
1352699019322 // Saved.get() IIFE 시간 
19  // 차이 값 



<참조>

  - javascript 블로그 : 간단하고 명확하게 정의내림 (표현형식만 설명)

  - 소스니 코브 : 너무 심도 있게 설명해서 집중하지 않으면 놓치기 쉬움 (왜 구별해서 사용하는지 설명)

  - 예제로 보는 javascript : Function Expression을 사용하는 이유를 가장 잘 설명함

'Languages > JavaScript' 카테고리의 다른 글

[JavaScript 성능향상] 웹사이트 최적화 방법  (0) 2012.12.12
[JavaScript] Template Engine  (0) 2012.11.26
[JavaScript] Closure 알아가기  (0) 2012.11.12
[JavaScript] Core 요약  (0) 2012.10.12
[JavaScript] Design Pattern  (0) 2012.09.10
posted by 윤영식
2012. 10. 12. 17:45 Languages/JavaScript

드미트리 소스니코브의 JavaScript Core에 대한 내용을 간략히 요약해 보자. 




▶ 오브젝트 (객체, An Object)

  - 오브젝트는 한개의 프로토타입 오브젝트를 가지고 있으면서 프로퍼티들의 집합이다. 

  - 오브젝트의 프로토타입은 내부 [[Prototype]] 프로퍼티에 의해 참조된다.

  - [[Prototype]] 보다는 __<internal-property>__ 를 사용하나, 특별히 프로토타입 오브젝트는 __proto__ 프로퍼티 변수로 표시한다.


예)

  var foo={

          x: 10,

          y: 20

   }

   해당 객체에는 3개의 프로퍼티가 존재하게 된다.

 



▶ 프로토타입 체인


  - 프로토타입 체인은 상속/공유되는 프로퍼티들의 구현에 사용되는 객체들의 유한 연결이다. 

  - ECMAScript는 자바와 같은 일반적인 class-based 상속이 아니라 prototype chain을 통하여 상속은 한다. 이를 위임형 상속(delegation based inheritance) 또는 프로토타입형 상속(prototype based inheritance)이라 한다. 

  - 이는 ECMAScript에는 class라는 개념이 없기 때문이다. 속성(프로퍼티)/메소드등을 동적으로 할당할 수 있기 때문에 class정의 자체가 필요없음

  - 예로 b, c의 공통영역에 a 객체를 저장하면, b, c는 자신에게 추가적인 프로퍼티와 메소드를 저장할 수 있는 것이다.  


  - b가 calculate 메소드를 찾아가는 방법 : b 객체안에 메소드가 없을 경우, prototype chain을 거쳐서 찾는다. 메소드를 찾는다. 찾지 못할 경우 undefined 값이 리턴된다.  (최상위 오브젝트 Object.prototype 값은 null 이다.) 

  - 주의 할것중 상속하여 사용시 this는 원래 객체를 가르킨다. 

  - 예로 this가 가르키는 객체로 this.y 는 b, c 객체이고, this.x는 a 객체를 가르킨다.  



▶ 생성자 (Constructor)


  - 생성자를 통해 객체를 생성시 유용한 것들이 있다. 

생성자를 사용하면 펑션의 prototype 프로퍼티가 <생성자 함수명칭>.prototype 객체를 가르킨다.  (그림 3.A)

예로 constructor function을 사용하여 b, c에 생성한 객체의 주소를 할당하면 b, c의 __proto__는 Foo.prototype 객체를 참조하게 된다. Foo.prototype 객체의 constructor는 원형의 constructor function 인 Foo를 가르키게 된다.

* 참조 : 자바스크립트의 new 의미 (http://zero.diebuster.com/zero/?p=36)



그림으로 그리면 다음과 같다 


  - Foo 자신의 __proto__는 Function.prototype 을 갖는다. 

  - Foo.prototype 오브젝트의 constructor는 자동 생성되어 Foo를 참조한다

  - Foo.prototype은 b, c 오브젝트의 prototype에 참조되는 Foo의 프로퍼티이다. (프로퍼티에 .x .calculate가 붙으면서 별도 객체가 되었다)


▶ Execution Context Stack

  - runtime program execution에 대해서 알려면 ECMAScript의 실행컨텍스트스택의 동작방식을 알아야 한다 

  - 3가지 타입 존재 : global code, function code, eval code 

  - 3가지 code는 execution context 인스턴스안에서 검증되어 진다. 

  - global context는 1개만 있고, function과 eval execution context들이 여러개 있게 된다. 만일 function이 호출되면 function execution context안으로 들어가 function code가 검증되어 진다. eval function은 특별히 eval execution context안에 들어서 eval code를 검증한다.  

  - Execution Context(EC)는 Stack에 담겨서 EC1 -> EC2 로 호출할 때마다 자기자신은 caller와 callee가 되어 코드 끝까지 수행이 되는 것이다. 

  글로벌 EC가 최소 caller가 되어 다음 foo(10) EC callee를 활성화 하고, foo(10) EC가 caller 되어 foo(20) EC callee를 활성화 하는 식으로 옮겨가는 구조를 Stack안에 구현되어 수행되는 것이다. 이른 execution context stack이라 한다. 


  - ECMAScript의 코드 동작방식에서 Global EC는 하나가 생성되어 있고, 펑션 EC는 Stack이 넣어져서 수행되고 완료되면 다시 Stack에서 빠지게 된다. 

  - 모든 EC를 Object로 간주되고, EC안의 code를 수행하는 주체가 된다. 



▶ Execution Context


  - EC는 context상태를 저장하기 위한 프로퍼티와 관련 코드 수행 경로 추적을 위한 프로퍼티등을 가지고 있는 단순 객체(오브젝트)이다.  

  - Variable Object (VO)는 EC와 관련된 scope of data 이다. (A variable object is a scope of data related with the execution context.)

  - 변수(variable)과 펑션선언(function declaration)을 저장한다. function expression은 저장하지 않는다. 



baz는 function expression이어서 Global Variable Object 에 저장되지 않는다. 


  - Activation Object (AO)는 Function context에서의 Variable Object이다. 

  - caller에 의해 function context가 활성화 되면 activation object가 생성되고 function이 호출된다. 

  - 호출시에 arguments객체-indexed map-에 파라미터 값를 담아서 넘겨준다.

  - activation object도 variable object이므로 variable, function declaration, 일반 파라미터, arguments 객체를 저장한다. 




▶ Scope Chain


  - 스코프 체인은 컨텍스트의 코드를 표현하는 식별자(identifiers)를 찾기위한 오브젝트 목록이다. (A scope chain is a list of objects that are searched for identifiers appear in the code of the context)

  - 규칙인 prototype chain과 동일하다. 

  - 즉, 자신의 스코프에서 variable/activation object를 못찾으면 부모의 VO/AO를 찾는다.

  - 스코프 체인은 일반적으로 부모 VO들의 목록(list)이다. 

  - context입장에서 식별자는 variable, function declaration, formal parameter들의 명칭이다. 


  - scope chain은 __parent__ 내장 프로퍼티로 연결된다. 또는 [[Scope]]로 나타내기도 한다.

이를 통해서 내부의 펑션이 외부의 변수 값을 참조할 수 있게 된다. 

반대로 만일 내부 펑션이 외부로 리턴되어 부모가 해당 펑션을 활성화하여 VO를 사용한다면 어떻게 될까? 요건 Closure 에서...




▶ Closure


  - ECMAScript에서 펑션은 일급 객체이다. (function is the first-class object)

  - 이말은 function을 다른 function에 argument로 전달 할수 있다는 것이다. - funargs- functional arguments라고 함

  - 또한 function을 리턴 할 수도 있다. functional value 라고 한다. 

  - funarg 문제는 closure 개념으로 풀어간다. 

  - Closure는 좀 더 상세하게 다른 쳅터에서 다룬다. 


▶ This 

  - This 값은 execution context와 관련을 맺는 특별한 오브젝트이다. 그래서 context object라고 불릴 수도 있다. 

  - 즉, an object which context the execution context is activated. 

  - this 값은 execution context의 프로퍼티이지 VO의 프로퍼티가 아니다. ( a this value is a property of the execution context, but not a property of the variable object.). 즉, this는 scope chain에 참여하지 않는다. look up 대상이 아니라는 것이다.

  - this 값은 caller에 의하여 제공받는다. 즉 EC1 -> EC2를 호출할때 EC1이 caller가 도고 EC2안에 this가 있다면 EC1의 오브젝트가 되는 것이다. 

  - 좀 더 자세한 사항은 다른 쳅터에서 다룬다. 


posted by 윤영식
2012. 9. 10. 22:56 Languages/JavaScript



지난달 KOSTA 교육기관에서 "자바스크립트 디자인 패턴" 이라는 강의를 들었다. 하이브리드 웹앱을 배우면서 자바스크립트를 다시 들여다 보아야 겠다 생각하고 듣게 되었는데 자바스크립트가 이렇게 신선한 놈인줄 첨 깨달았다. 

예전엔 그냥 화면에 애니메이션 효과주는 그저그런 스크립트어로만 생각했는데 파면 팔수록 새로운 개념과 접근법으로 OOP를 실현하고 있음에 감탄을 유발시킨다.

 


1) 자바스크립트 기본을 알고 가자

  - 지돌스타님의 블로그 :  http://blog.jidolstar.com/790  연재글이 꽤 많다. 초보 OOP 개발자가 보기에 만만찮다. 

  - 드미트리소스니코브의 블로그 :  http://dmitrysoshnikov.com/ecmascript/javascript-the-core/#an-object 현재 페이스북 엔지니어 같다. 이 아저씨 고수다. 그래픽적으로 개념을 잘 설명해 주고 있다. 이 아저씨껀 다 읽어 보자. 쥑인다.


2) 자바스크립트 디자인 패턴을 본격적으로 보자

  - HTML 문서 :  http://addyosmani.com/resources/essentialjsdesignpatterns/book/

  - Design Patterns for Scalable JavaScript Application (PDF)

  - Pro JavaScript Design Patterns (PDF)


짬짬이 자바스크립트 디자인 패턴 블로그를 써야 겠다. 


posted by 윤영식
prev 1 next