2015년 2월 26일 목요일

[JavaScript] Hoisting(호이스팅) 이란?

부제 : 함수 선언과 함수 표현의 차이
Function Declaration vs Function Expression


함수 선언과 함수 표현의 가장 큰 차이는 바로 Hoisting이다.
이걸 제대로 이해하기 위해서는 Execution Context에 대한 이해가 필수이니 이 글(클릭하기)를 읽고 와주길 바란다.

우선 Hoisting에 대해 간단히 말하자면, 후선언된 변수나, 함수들이 해당 Scope에서 최상위에 위치하는걸 뜻한다.
자바스크립트엔진은 해당 Execution Context의 생성시, 즉 Runtime 시점에서 변수선언문이나 함수선언문을 읽기 전에 선언된 변수와 함수들을 다른 무엇보다도 먼져 읽어 Scope의 최상위에 위치시킨다. 이 덕에, 훨씬 뒤에서 선언된 함수들과 변수들을 그 전에 사용이 가능하다.

*참고로 변수의 경우에는 변수 선언(Variable Declaration)만 Hoisting 된다, 즉 Variable Initialization이 있다면 변수가 선언은 되나, 변수에 어떤 값도 들어가지 않는다는 뜻.

*또한 함수의 Execution Context의 생성은 함수 호출시 이뤄지므로, 함수 내부에서 선언된 변수들은 함수 호출시에서야 Hoisting 된다.


console.log(internalFunction);// 함수가 Hoisting 됐을뿐, 아직 Execution Context는 생성되지 않았으므로, 에러가 발생한다.

functionDeclaration();// 함수가 성공적으로 호출되며 Function Declaration가 프린트 된다.

console.log();// Undefined 가 프린트 된다. 변수에 "vart"라는 값이 어싸인되는것은 아래 코드가 읽히는 시점에서기 때문.

function functionDeclaration() {
    console.log("Function Declaration");
    
    function internalFunction(){
    }
}

var t = "vart";

functionExpression(); // 에러가 발생

console.log(novalue); // Context에서 선언된적 없는 변수인 novalue를 호출할 수가 없어 에러가 발생

var functionExpression = function() {
    console.log("Function Expression");
}


그럼 이제 Hoisting을 제대로 이해했는지 확인도 할겸 아래 코드의 결과를 알아 맞춰보자.


function test1() {
    function internalFunction() {
 return true;
    }

    return internalFunction();

    function internalFunction() {
 return false;
    }
}

console.log(test1());

function test2() {
    var internalFunction = function() {
 return true;
    }

    return internalFunction();

    var internalFunction = function() {
 return false;
    }
}

console.log(test2());

function test3() {
    return internalFunction();

    var internalFunction = function() {
 return true;
    }

    var internalFunction = function() {
 return false;
    }
}

console.log(test3());

function test4() {
    var internalFunction = function() {
 return true;
    }

    return internalFunction();

    function internalFunction() {
 return false;
    }
}

console.log(test4());






정답은


  • test1 = false
  • test2 = true
  • test3 = TypeError: undefined is not a function
  • test4 = true
    • 참고로 test4는 hoisting 되었던 함수가 변수 표현문으로 다시 정의되어 true값을 반환하게 된다.
    • 기억하자, 자바스크립트에서는 함수 또한 객체에 불과하다. 그러니 재정의될 수 있다.

2015년 2월 23일 월요일

[JavaScript] 자바스크립트의 Function(함수)

타 프로그래밍 언어와 같이 자바스크립트(ECMAScript)의 함수(Function)는 Statement들을 언제 어디서든 실행할 수 있게 그 Statement들을 캡슐화(Encapsulation)하기 때문에 매우 중요한 기능을 하는 요소 중 하나 이다. 참고로 Function도 자바스크립트의 Object(객체) 자료형에 포함된다.
즉, 자바스크립트의 모든 함수(Function)들은 Object(객체)의 하위 개체인 Function 타입에 속하고, 우리가 프로그래밍시 함수(Function)에 부여하는 함수 이름은 다른 reference type의 경우처럼, 메모리에 저장된 함수 객체를 가리킬 뿐이다. 그렇기 때문에 함수(Function)은 일반 변수처럼(function expression) 선언이 가능하며, 코드상에 이름이 같은 함수 2개가 존재해도 Exception이 발생하지 않으며, 객체(Object)가 자신 내부에 또 다른 객체(Object)를 저장하는 것 처럼, 함수(Function)안에도 또 다른 함수를 선언할 수 있고, 하나의 함수를 여러가지 변수로 호출 할 수도 있다.

  • 참고로 Strict Mode에서 함수 선언시 함수 이름이나 함수 파라미터에 "eval" 또는 "arguments"라는 단어를 쓰지 못한다, 이는 좀 이따가 다루도록 하겠다
  • 자바스크립트(ECMAScript)에서 함수로 전달되는 Arguments는 언제나 Pass By Value이다. 즉 함수에서 전달된 Argument의 값을 변경해도 원래의 Argument 변수에는 영향이 가지 않는다.

함수(Function)의 선언


함수(Function)는 아래와 같이 정의가 가능하다.
정확히 말하자면 함수 정의는 2가지 방법으로 가능하다.
Function Declaration(함수 선언)Function Expression(함수 표현).
이 두가지 방법의 차이에 대해서는 다른 글(클릭하기)에서 좀 더 자세히 다뤄보도록 하겠다.


// Function Declaration
function functionIdentifier(arg1, arg2, arg3){
console.log("Demo Function Argument 1" + arg1);
console.log("Demo Function Argument 2" + arg2);
console.log("Demo Function Argument 3" + arg3);
// Statement를 여기에 적으면 된다.
}
// functionIdentifier 자리에는 함수의 이름이 오게되고
// 그 옆의 괄호안에는 함수 파라미터(Formal Parameter)가 오면 된다.

// 또는 아래와 같이 var 키워드를 이용 할 수도 있다. -Function Expression
// 알아야 할것은, 함수 표현식에서 정의되는 함수들 중, 
// function identifier가 정의되지 않은 함수들은
// 모두 anonymous function 또는 lambda function으로 불리며, 
// 이런 함수들은 이름(Function Identifier)를 가지지 않는다.
// 이 때문에 함수 객체의 "name" 프로퍼티는 공백의 문자열(empty string)이 된다.
// 즉, 해당 변수가 정의된 함수 객체를 참조할 뿐인것이다.
var functionExpressionExample1 = function functionIdentifier(arg1,arg2){
console.log("This is Function Expression1");
};

var functionExpressionExample2 = function(arg1,arg2){
console.log("This is Function Expression2");
};

// Self executing function이라 불리는 기법
// 함수 호출이 없이 함수가 실행된다.
(function(arg1,arg2){
console.log("This is Self executing function");
})();

//여러 변수들이 한개의 함수를 가리키게 할 수도 있다.
var func0 = functionName(arg1, arg2, arg3);
var func1 = functionName(arg1, arg2, arg3);
var func2 = functionExpressionExample1;


함수(Function)의 호출


함수(Function)은 언제 어디서든 함수 이름과, 아규먼트(Argument)를 괄호()에 포함해 호출이 가능하다.

//파라미터로 주어진 두 변수의 합(두 변수가 숫자일 경우), 또는 문자열의 합을 반환하는 함수
function addition(arg1, arg2){
return arg1 + arg2;
}

console.log(addition(3,4));//7


타 프로그래밍 언어와 달리, ECMAScript 함수는 주어지는 Argument의 갯수가 Formal Parameter의 갯수와 일치하지 않아도 함수 호출이 가능하다. 말인즉, 이름이 같은 함수 A가 Formal Parameter로 2개의 변수를 받을 때, 3개 또는 0개의 Argument로도 함수 A를 호출 할 수 있다는 뜻 이다. 이는 자바스크립트가 함수(Function)에 Argument 를 전달할 때 Array 객체로 전달하기 때문이다. 다른 프로그래밍 언어(특히 Java)와는 궤를 달리하는 방식인데, 이러한 이유 때문에, Java 또는 C#에서 가능한 method polymorphism이 Javascript(ECMAScript)에서는 구현이 불가능 하다.
참고로 Argument 값이 주어지지 않는 Parameter는 언제나 Undefined 값을 가지게 된다.


//파라미터로 주어진 두 변수의 합(두 변수가 숫자일 경우), 또는다 문자열의 합을 반환하는 함수
function addition(arg1, arg2){
return arg1 + arg2;
}

console.log(addition(3,4));//7
console.log(addition(3));//NaN 이 경우에는 arg2에 undefined 값이 어싸인 된다.
console.log(addition(3,4,6));//7 이 경우에는 세번째 아규먼트는 사용되지 않는다.


이게 가능한 이유는 Array(배열)형식으로 전달된 Arguments들과, 함수의 실행시 전달된 Argument Array의 사이즈를 확인하지 않는 자바스크립트(ECMAScript)의 특성 때문이다. 이러한 특수한 이유 때문에 자바스크립트(ECMAScript)의 함수에서 배열에 접근하듯 Array Bracket Notation([] 사각 괄호안에 인덱스를 넣어 배열에 접근하는 방법)을 이용해서 함수에 주어진 Argument에 접근할 수 있다.
  • 참고로 배열의 이름은 "Arguments" 이다, 이 때문에 Strict 모드에서 "Arguments"라는 단어를 함수 이름이나 함수 파라미터의 이름으로 사용할 수가 없다.


//파라미터로 주어진 두 변수의 합(두 변수가 숫자일 경우), 또는 문자열의 합을 반환하는 함수
function addition(arg1, arg2) {
 if (arguments[2] == undefined) {
  return arg1 + arg2;
 } else {
  return arg1 + arg2 + arguments[2]
 }
}
console.log(addition(3,4));//7
console.log(addition(3));//NaN
console.log(addition(3,4,6));// 13


위의 코드에서 arg1, arg2는 각각 arguments 배열의 인덱스 0과 1에 위치하고, 3번째 Argument는 인덱스 2에 위치하게 된다.
그렇기 때문에 3번째 Argument, 즉 인덱스 2에 위치한 변수가 undefined 값을 가진 경우에는 첫번째 두번째 변수의 합을 반환하지만, 반대의 경우에는 주어진 3개의 변수들 모두의 합을 반환하게 된다. 또는 아래와 같은 사용도 가능하다.


function demoConcatString() {
    var t = "";
    for (var i = 0; i < arguments.length; i++) {
 t = t + arguments[i] + " ";
    }
    return t;
}

console.log(demoConcatString("This", "is", "demo"))// This is demo;


함수의 Arguments 배열은 매게 변수들을 제외하고도 callee 라는 프로퍼티를 가지고 있는데, Arguments 배열과 관계된 함수를 가리킨다.


function demo() {
    console.log(arguments.callee);
}

demo();// [Function: demo]


arguments.callee 가 호출 된 함수를 가리킨다면, function.caller 는 호출 한 함수를 가리킨다. 만약 global context에서 호출됬다면 null 값을 가진다.


function demo() {
    console.log(demo.caller);
}

function caller() {
    demo();
}

demo();// null, global 컨텍스트에서 호출 됬을 때는 null

caller();// [Function: caller]


함수 오버로딩(Function Overloading)


위에서 잠깐 이야기 했듯, 자바스크립트의 함수는 객체에 불과하며 Arguments 들이 배열(Array) 형식으로 전달되기 때문에 사실상 Function Signature를 가지지 않는다. 이러한 이유 때문에 자바(Java)나 C# 등에서 사용되는 Method Overloading은 구현이 불가능하다. 만약 같은 이름의 함수를 여러개 선언했다면, 가장 마지막에 선언된 함수가 실행될 것이니 이러한 실수는 피하도록 하자.


function demoConcatString(arg1) {
    var t = "";
    for (var i = 0; i < arguments.length; i++) {
 t = t + arguments[i] + " ";
    }
    return t + "1";
}
function demoConcatString(arg1, arg2) {
    var t = "";
    for (var i = 0; i < arguments.length; i++) {
 t = t + arguments[i] + " ";
    }
    return t + "2";
}
function demoConcatString(arg1, arg2, arg3) {
    var t = "";
    for (var i = 0; i < arguments.length; i++) {
 t = t + arguments[i] + " ";
    }
    return t + "3";
}

console.log(demoConcatString("This", "is", "demo")); //This is demo 3, 가장 마지막의 함수가 호출된다.

2015년 2월 17일 화요일

[JavaScript] 자바스크립트 Object(객체)

자바스크립트의 Object 데이터 타입

자바스크립트(ECMAScript)의 Object 데이터 타입은 다른 프로그래밍 언어의 Object 와 같이 여러개의 자료(Data)들과 함수(Function)들의 집합이다(자바스크립트도 객체지향프로그래밍언어(Object Oriented Programming language)이다). 주로 객체라 불리며 자바스크립트의 유일한 Composite/Reference Data Type 이다(다른 reference data type은 모두 object 타입의 하위 타입).

참고로 자바스크립트에서 Primitive Data Type들을 제외한 모든것들이 다, 심지어는 함수까지 모든게 Object 타입이다. 또한 Primitive Data Type 마져 접근될시에는 자바스크립트 VM에의해서 Object 타입으로 변환되어 사용된다. 즉 모든게 Object 타입이라 할 수는 없지만 모든게 Object 타입처럼 작동한다고 할 수 있다.
이러한 이유로 Object(객체) 자료형은 모든 자료형의 최상위 타입이라고 할 수 있다. 객체 자료형을 포함 하위 타입의 자료형들은 총 7개의 함수들과 속성들을 가진다.

자바스크립트(ECMAScript)의 Object 타입은 모든 다른 객체들이 파생된 Base Type 이다. Object 타입은 기본적으로 총 7개의 프로퍼티(Property)들과 메소드(Method)들을 가진다.
  • constructor
    • Object를 생성하는 생성자
  • hasOwnProperty(속성이름)
    • 객체에 주어진 속성이 있는지를 확인하는 메소드
  • isPrototypeof(Object)
    • 객체가 파라미터로 주어진 Object 의 Prototype 인지를 확인하는 메소드
  • propertyIsEnumerable( 속성이름)
    • 객체의 속성들이 for-in 구문으로 Enumerated(열거) 될 수 있는지
  • toLocaleString()
    • 객체를 실행 환경에 알맞은 형태의 String으로 표현한다
  • toString()
    • 객체를 String으로 표현한다
  • valueOf()
    • object 의 primitive value(원시 값)을 반환한다. 
    • 즉 객체에 가장 알맞는 표현을 string, number, boolean 값 중 1개로 반환한다.

    자바스크립트(ECMAScript)에서의 Object(객체)는 자바처럼 new 연산자를 이용해서 선언거나 Object Literal Notation을 이용해서 선언 될 수 있다. 그리고 개발자들은 새로운 객체를 선언하면서 또는 선언한 후, 속성과 기능(메소드)들을 추가함으로써 자신만의 객체를 만들 수가 있다.

    
    //New 연산자를 사용해서 선언
    var objectVariable = new Object();//이게 표준
    var improperObjectDeclaration = new Object;//괄호가 없어도 되지만 되도록이면 괄호를 붙이도록 하자.
    var demoObject1 = new Object();
    demoObject1.name = "Demo Object 1";//속성(Property) 부여
    demoObject1.message = "This is Demo Object";//속성(Property) 부여
    
    //Object Literal Notation을 이용해서 선언
    var demoObject2 = {
     name : "Demo Object 2",
     message : "This is Demo Object 2"
    };
    
    var demoObject3 = {
     "name" : "Demo Object 2",
     "message" : "This is Demo Object 3",
     3 : "Three", //숫자를 속성 이름에 사용할 수도 있다. 이 경우에는 이름이 자동으로 String 형으로 변환된 후 저장된다
            "This is" : 7798 //속성 이름에 스페이스가 들어갈 수도 있다.
    };
    
    var demoObject4 = {}; // new Object() 와 같다
    demoObject4.name = "Demo Object 4";
    demoObject4.message = "This is Demo Object 4";
    

    위와 같이 선언된 객체의 속성들은 Dot Notation 을 통하거나 Bracket Notation을 이용해서 접근이 가능하다
    단 Dot Notation을 이용할 경우에는 숫자로 이름 지어진 속성이나 이름에 스페이스가 들어가는 속성에는 접근이 불가능하다. 이는 JavaScript의 문법(Syntax)적 한계이니, 이러한 경우에는 Bracket Notation을 이용하면 된다

    
    // Dot Notation
    console.log(demoObject3.name);
    console.log(demoObject3.message);
    console.log(demoObject3.3); // Syntax 에러
    console.log(demoObject3.This is); // Syntax 에러
    demoObject3.message = "New Message";
    
    // Bracket Notation
    console.log(demoObject3["name"]);
    console.log(demoObject3["message"]);
    console.log(demoObject3["3"]);
    console.log(demoObject3["This is"]); 
    // Bracket Notation의 경우에는 이런식의 접근도 가능하다
    var propertyName = "name";
    console.log(demoObject3[propertyName]);
    demoObject3[propertyName] = "new Demo Object";
    


    2015년 2월 7일 토요일

    [Eclipse] 이클립스 Papyrus(파피루스) UML 클래스 다이어그램을 자바(Java) & C++ 코드로 변환하기

    papyrus 로고
    papyrus 로고

    지난번에 설명했던 Eclipse 개발팀에서 만든 모델링 툴인 Papyrus(파피루스), 파피루스는 추가 확장 소프트웨어로써 프로그램 모델링 툴을 지원한다. 보통 UML 다이어그램을 그릴 때 사용하며, UML 클래스다이어그램을 코드로 변환시키거나 코드를 UML 다이어그램으로 변환하는 기능도 제공한다. 언어는 현재 C++와 J링ava만을 지원한다.

    Eclipse 파피루스 간단 사용법은 다른글을 참고하도록 하자, 이 글은 파피루스로 그린 클래스다이어그램을 코드로 변환시키는 기능에 대한 글이다.

    우선 추가확장 프로그램을 설치하자. 새로운 소프트웨서 설치창에 Papyrus 업데이트 사이트를 등록해준다. 참고로 파피루스 버젼별 업데이트 사이트는 여기서 확인이 가능하다.

    현재 최신 버젼 이클립스인 Luna 용은 주소가 요렇다.
    http://download.eclipse.org/modeling/mdt/papyrus/updates/releases/luna
    저 주소를 등록해주도록 하자.

    파피루스 확장 repository 추가

    등록을 끝마친 후 사이트를 골라 아래 체크박스 옵션에서 Group items by category 체크를 해제해주면 아래와 같은 추가 확장프로그램 리스트들을 보게 될것이다. 




    자바(Java) 프로그래머들은 필요에 따라 아래 2개를 선택한 후 설치하고
    • Papyrus Java class Generator (Incubation)
      • 다이어그램 -> 자바코드로
    • Papyrus Java Reverse (Incubation)
      • 자바 코드 -> 다이어그램으로
    C++ 프로그래머들은 
    •   Papyrus C++ profile, view and code generation (Incubation)
    를 설치하도록 하자.




    아직 설치가 끝난게 아니다. Papyrus Java classes Generator & Reverse 는 QVT Operational SDK 라는 추가 라이브러리를 요구한다. 만약 이걸 설치하지 않을시에는, 코드 변환을 하려고 버튼을 누른 순간 아래와 같은 에러 메시지를 보게될것이다.

    No classes or packages produced from the transformation. Nothing is generated.
    and exception "org.eclipse.papyrus.java.generator.metamodel.jdt.jdtmm.visitor.JDTVisitorException"

    그렇니 다시 Install New Software 페이지에 들어가서, 이번에는 본인의 이클립스 업데이트 사이트를 선택한 후에 Modelling 카테고리 아래에서 QVT Operational SDK를 찾아 설치해주자자자




    이 설치를 완료했다면, 이클립스 재시작후 UML 클래스 다이어그램 페이지로 가서




    코드로 변환하기 원하는 클래스들을 선택한 후에, 우클릭 Java -> Generate Java Code를 선택하면 자동으로 자바클래스 파일들이 생성된다.

    2015년 2월 6일 금요일

    [Eclipse] 이클립스 Papyrus(파피루스)를 이용해서 UML 다이어그램 그리기

    papyrus 로고

    UML 다이어그램을 그리는 모델링 툴, Eclipse 확장 프로그램 Papyrus, Eclipse 개발팀에서 직접 개발했다. 이 툴을 이용해 클래스 다이어그램(Class Diagram), 엑티비티 다이어그램(Activity Diagram), 시퀀스 다이어그램(Sequence Diagram) 등 각종 UML 다이어그램을 그릴 수가 있다

    우선 Eclipse 를 켜 본인 Eclipse 버젼에 알맞는 Papyrus 를 다운로드 하자.
    Modelling 카테고리에 있다.

    이클립스 파피루스 소프트웨어 설치 페이지
    이클립스 소프트웨어 설치 페이지


    Papyrus UML 소프트웨어를 골라 설치하면



    새로 만들기 창


    새로 만들기 창에 이런 문서들을 보게 될 것이다. 파피루스 모델을 생성하자


    파피루스 모델 언어 설정 페이지



    언어는 UML로 고르고 완료를 누르게 되면 아래와 같은 창을 보게될 것이다.


    아직은 텅 빈 파피루스 모델

    아직은 텅 비었다. 다이어그램을 생성해야지 그림을 그릴수가 있다

    Perspective 왼쪽 아래 Model Explorer에 model 을 우클릭 하면


    새 다이어그램 생성 메뉴


    이런 메뉴를 보게되는데, 여기서 필요한 다이어그램을 클릭하면(나는 Class Diagram 을 클릭했다),
    아래와 같은 팔레트가 오른쪽에 생성될 것이다, 거기서 필요한 다이어 그램을 끌어서 놓은 다음 속성을 수정하면 쉽게 UML 다이어그램을 그릴 수가 있다.



    파피루스를 이용해 생성된 UML 클래스 다이어그램


    혹시 저런 화면 구성이 보이지 않는다면
    이클립스 Perspective를 Papyrus 용 Perspective로 바꿔주자

    이클립스 Perspective 설정 페이지