클로저(Closure)



클로저란 Javascript의 유효범위, Scope를 이용하여 생명 주기가 끝난 외부함수의 변수를 참조하는 방법이다.

외부함수가 종료되더라도 내부함수가 실행되는 상태이면 내부함수에서 참조하는 외부 함수는 닫히지 못한다.

따라서 외부 함수가 종료되기 위해서는 내부함수가 종료되어야하기 때문에 클로저(Closure)라고 불리운다.




1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function outFunc(){  
    var num= 0;
    return {
        func1 : function(){
            num+=1;
            console.log("num :"+num);
        },
        func2 : function(){
            num+=2;
            console.log("num :"+num);
        }
    };
}
var out = outFunc();  
var out2 = outFunc();  
out.func1();  // 1
out.func2();  // 3
out2.func1(); // 1
out2.func2(); // 3
cs


클로저를 사용하는 이유는 전역 변수의 오/남용이 없는 깔끔한 코드를 작성할 수 있기 때문이다.

앞서 언급했던대로 자바스크립트는 계속해서 발생한 전역 변수에 대한 해답을 찾기위해 노력했다.

ES6에서 제공되는 let과 const 키워드도 이에 대한 해답중 하나이다.


그러나 let과 const가 없을 때는 디자인 패턴으로 극복하는 경우가 있었는데

네임스페이스나 생성자, 모듈패턴의 그 예 이다.

이중 모듈 패턴과 생성자 패턴은 클로저로 구현될 수 있는 패턴이다.


클로저에서 주의 할점은  내부함수에서 참조하는 외부 함수의 변수는 복사(Clone)본이 아닌 값을 직접 참조한다는 것이다.

위의 코드에서도 func1과 func2가 같은 외부 함수의 변수를 참조하고 있으므로 func1이 실행되어도 func2에 영향을 미친다.


두번째로, 외부 함수를 실행하면 내부 함수가 리턴된다.

따라서 외부함수의 리턴값을 여러개 생성하면, 이러한 클로저가 여러개 생성되고, 각 클로저 마다 외부 함수의 변수는

주소 값이 다르므로 영향을 미치지 않는다.

그러므로, out과 out2는 별개의 변수라고 할 수 있다.


이와 같은 특성은 객체와 비슷하다고 할수 있는데, 실제로 자바스크립트에서는 public이나 private 키워드를 제공하지 않으므로

위의 예제 처럼 클로저를 이용하여 외부 함수의 변수를 private로 지정할수 있다.

이러한 패턴을 모듈 패턴이라고 한다.





1
2
3
4
5
6
7
8
9
10
11
function Person(inputname) {  
    this.name = inputname;
    this.getName = function() {
        return this.name;
    };
    this.setName = function(rename) {
        this.name = rename;
    };
}
var obj= new Person("nununununu");  
console.log(obj.getName()); 
cs


그러나 쓸데없는 클로저의 남용은 메모리면에서 큰 비효율을 낳는다.

위의 코드는 객체의 생성자를 정의하여 인스턴스를 생성하는 생성자 패턴이다.

getName과 setName이라는 클로저가 2개나 있는데, 이 상태에서 인스턴스를 만들면 

같은 일을 하는 클로저가 객체마다 생성될 것이다.

이를 클로저의 오남용 이라고한다.


프로그래밍의 발전사에 가장 중요한 철학인 효율성과 중복성의 제거에 비추어 봤을 때도

인스턴스가 생길때마다 클로저가 중복되므로 상당히 좋지 않게된다.





1
2
3
4
5
6
7
8
9
10
11
function Person(inputname) {  
    this.name = inputname;
}
Person.prototype.getName = function() {  
    return this.name;
};
Person.prototype.setName =   function(rename) {            
    this.name = rename;
};
var obj= new Person("nununununu");  
console.log(obj.getName());  
cs


따라서 클로저는 객체의 prototype 안에 저장함으로써 같은 기능을 모든 인스턴스가 공유하는 형태로

코드를 만들어야한다.

이렇게 prototype안에 클로저를 넣으면, 인스턴스가 생성되더라도 중복으로 메모리를 낭비하지않고,

클로저를 참조할 때 생성자 내부 prototype으로 들어오기 때문에, 효율적인 코드가 된다.



참고 : http://www.nextree.co.kr/p7363/

저작자 표시
신고

 함수(function)





자바스크립트에서 함수는 객체이다.

그 중에서도 함수는 특히나, 1급 객체(first-class Object)이다. 

여러 블로그나 자료들을 보면 함수는 객체이며, 1급 객체이다 라는 말이 많이 등장한다.

그렇다면 1급 객체란 무엇일까?


1급 객체를 알아보기전에 먼저 1급 시민(first-class citizen)을 알아야한다.

1급 시민의 개념은 영국의 컴퓨터 과학자 크리스토퍼 스트레이치에 의해 1960년대 처음 소개 되었으며,

 조건은 다음과 같다.


1) 변수에 담을 수 있다.

2) 인자로 전달할 수 있다.

3) 반환값으로 돌려 받을 수 있다.

4) 할당된 이름과 상관없이 고유한 구별이 가능하다

5) 동적으로 프로퍼티 할당이 가능하다.


예를 들어, 대부분의 프로그래밍 언어에서 숫자(Number) 값은 변수에 담을 수 있으며,

인자로 전달이 가능하고, 반환값으로 돌려 받을 수 있으므로 1급 시민의 자격을 가진다.

그러나 C 등의 언어에서는 배열이나 문자열이 값이 아닌 포인터를 전달하므로 1급 시민의 자격을 얻지 못한다.


이와 같은 맥락에서 1급 객체란,

객체로써 1급 시민의 조건에 모두 해당되는 것을 뜻한다.

이를 기반으로 볼 때, 자바스크립트에서는 함수가 위의 조건을 모두 충족하기 때문에

1급 객체, 또는 1급 함수라고 불린다.





함수 선언식(declaration)과 표현식(expression)


1
2
3
4
// 함수선언식(function declaration)
function example() {  
    /* 실행코드 */
}
cs


자바스크립트에서 함수를 선언할 때는 일반적으로 다음과 같이 표현한다.

이와 같은 방식을 함수 선언식(function declaration)이라고 하는데, 

전통적인 프로그래밍에서 선언하던 함수 모양과 비슷하다.


함수 선언식으로 함수를 선언하면, 인터프리터가 스크립트를 로딩할 때 초기화 되며,

이를 변수 객체(VO - variable object)에 저장한다. 

따라서 위치와 상관없이 어느 곳에서나 호출이 가능하게 된다.





1
2
3
4
5
6
7
8
9
// 기명 함수표현식(named function expression) 
var func = function func() {  
    /* 실행코드 */
}; 
 
// 익명 함수표현식(anonymous function expression)
var func = function() {  
    /* 실행코드 */
};
cs


일반적이 함수 선언식과 다르게, 1급 객체가 변수로써 저장이 가능하다는 점에 착안하여

위와 같은 코드로 함수를 만드는것이 가능한데,

이를 함수 표현식(function expression)이라고 한다.


함수 선언식은 인터프리터가 스크립트를 로딩시 함수를 변수 객체에 저장하는데 반해 

표현식은 런타임시에 해석되고 실행되는 것이 큰 차이점이라고 할 수있다. 


따라서 함수 선언식으로 사용할 경우 사용하기 쉽지만 대규모 애플리케이션을 만들 때

인터프리터가 너무 많은 코드를 변수 객체에 저장하므로 애플리케이션의 응답속도가 떨어지기 때문에 주의해야한다.





즉시 실행 함수(immediately-invoked function)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 일반적인 함수호출(function declaration)
var hello = function() {  
    console.log('Hello world');
};
hello();
 
// 기명 즉시실행함수(named immediately-invoked function expression)
(function company() {
    console.log('Hello world');
}());
 
// 익명 즉시실행함수(immediately-invoked function expression)
(function() {
    console.log('Hello world');
}());
 
// 익명 즉시실행함수(immediately-invoked function expression)
(function() {
    console.log('Hello world');
})();
cs


자바스크립트에서 끊임없이 나온 문제점은 전역 변수로 인한 오류이다.

전역 변수를 사용할 경우


1) 어디서나 전역 변수에 접근이 가능하다

2) 외부에 공유되면 안되는 메소드가 노출된다.

3) 동일한 메소드나 변수 이름으로 원치 않은 결과가 나온다.


이러한 문제점을 해결하기 위해, 코드 작성 과정에서 여러가지 패턴들이 등장 하게 되었고,

함수 선언 시에는 즉시 실행 함수(immediately-invoked function)를 사용하게 되었다.


즉시 실행 함수는 함수 자체를 단순히 괄호 안에 집어넣고 실행한 것이다

그러나 함수를 정의하고, 변수에 함수를 저장하고 실행하는 과정이 있는 함수 선언식과는 다르게

이와 같은 과정을 거치지 않고 바로 실행된다는 특징이 있다.


위의 코드들은 기존에 사용하던 함수 선언식과 즉시 실행 함수를 만드는 3가지 방법을 나타낸 것이다.

이 중에서 더글라스 크락포드는 첫번째 익명 즉시 실행 함수를 권장한다.





1
2
3
4
5
6
7
8

var app = (function() {  
    var hidenVal = 'hiden';
    return {
        val : hiden
    };
}());
console.log(app.val);
cs


이러한 즉시 실행 함수를 사용하게 되면 위와 같이 private 변수를 흉내낼 수 있다.

사실 위와 같은 방법은 클로저를 사용한 것으로

이런 코드 방식을 패턴화 시켜 모듈 패턴이라고 부른다.


자바스크립트는 public, private에 대한 구체적인 문법은 없지만

언어에서 제공되는 기본적인 기능을 활용해서 비공개 변수/메소드를 만들수 있다.


따라서 즉시 실행함수는 글로벌 네임스페이스에 변수를 추가하지 않아도 되기 때문에

코드 충돌없이 구현할 수 있어, 플러그인이나 라이브러리등을 만들때 많이 사용된다.




참고 : http://www.nextree.co.kr/p4150/

http://bestalign.github.io/2015/10/18/first-class-object/

https://ko.wikipedia.org/wiki/%EC%9D%BC%EA%B8%89_%EA%B0%9D%EC%B2%B4


저작자 표시
신고

자바스크립트의 실행 문맥(Context)




1
2
3
4
5
6
7
8
9
10
11
12
console.log("global context");  
 
function context1(){  
    console.log("first context");
};
 
function context2(){  
    context1();
    console.log("second context");
};
 
context2(); 
cs


자바스크립트는 하나의 콜 스택을 가지며 이 콜 스택에 함수의 호출 정보 등이 쌓인다.

콜 스택에 쌓이는 함수의 정보를 실행 문맥(Context)이라고 한다.


자바스크립트의 변수는 함수 단위의 Scope를 가지며, 

때문에, 콜 스택에 쌓인 실행 문맥을 기준으로 변수의 스코프가 결정된다.


JS파일이 실행되면 가장 먼저 main context가 스택에 쌓이며,

여기 위로 함수가 호출될 때마다 해당 함수의 context가 쌓이고 실행되며 없어지기를 반복한다.

스택의 모든 함수가 호출되고 JS 파일이 가장 끝에 도달 했을 때 비로소 main context가 사라지며 실행이 완료된다.






즉, 위의 코드는 다음과 같은 그림으로 스택에 쌓이게 된다.

먼저 글로벌 컨택스트인 Main이 가장 먼저 실행되고 Main안에서 함수들이 차례로 스택에 쌓이게된다.

실행이 완료되면 스택에서 제거(Pop)되며, 마지막으로 Main 없어지면 해당 파일은 완전히 실행되는 것이다.






실행문맥은 크게 3가지로 이루어져있다.

1) 활성화 객체 : 실행에 필요한 변수들과 arguments를 담은 객체

2) 유효 범위 정보 : 현재 실행되고 있는 컨텍스트의 유효범위가 담긴 정보

3) this 객체 : 현재 실행 문맥을 포함하는 객체


위의 그림에서 addNumber이라는 함수를 실행하면 아래와 같은 컨텍스트가 만들어 진다.

우선 인자로 들어온 1,2와 이를 모두 담고있는 유사 객체인 arguments이 생기며,

유효범위를 담고 있는 Scope Chain과 현재 실행 객체를 담는 this 또한 컨텍스트에 담긴다.







이렇게 실행 문맥은 위와 같은 그림의 순서로 생성된다.

scope chain은 유효 범위를 담고 있는 일종의 리스트이다.


 arguments 객체는 함수로 들어 오는 인자를 모두 담고 있다.

arguments는 유사 배열 객체의 형태이며 배열이 아니다.

따라서 map, forEach등 배열의 메소드를 사용할수 없으며, 이를 사용하기 위해서는 Array.prototype.~를 써야한다.



참고 자료 : http://www.nextree.co.kr/p7363/

저작자 표시
신고

Scope와 변수



Scope는 '범위'를 나타낸다.

좀 더 자세히 말하자면 변수가 적용되는 '유효 범위'의 개념이다.


유효 범위에 따라서 변수는 지역 변수(Local variable)전역 변수(global variable)로 나뉜다.

예를 들어 지역 변수는 해당 스코프, 즉 자신이 영향을 미칠수 있는 범위에서만 호출, 변경 될 수 있고

전역 변수는 전 영역에서 사용될 수 있다.

이는 프로그래밍에서 상당히 중요한 개념이며 각 언어마다 스코프의 개념이 조금 다를 수 있다.


자바스크립트가 대표적으로 Scope의 개념이 전통적인 방식과 다른 예인데,

C나 Java 등의 언어는 블록 단위로 스코프가 매겨지만, 자바 스크립트는 함수 단위 스코프이다.


그 말인즉, C나 Java 등이 if나 while 등의 블록 내에서 변수를 선언할 경우 해당 블록내에서만 영향을 미치는데 반해,

자바스크립트는 블록과 상관없이 function 단위로 구분되어진다.


이 때문에 처음 자바스크립트를 접하는 사람들은 간혹 실수를 저지르게 된다.

이러한 Scope 특징의 장단점과 보완을 따지기에 앞서 자바스크립트 변수선언에 사용되는 Var의 특징을 알아보자





1. 함수 단위의 유효 범위


1
2
3
4
5
6
7
8
9
10
11
12
function scope() {  
    var a = 0;
    if (true) {
        var b = 0;
        for (var c = 0; c < 5; c++) {
            console.log("c=" + c);
         }
         console.log("c=" + c);
    }
    console.log("b=" + b);
}
scope();  
cs


앞서 말했던것과 같이 Var는 함수 단위의 유효 범위를 가진다.

따라서 if문안에 있는 b를 밖에서도 사용할 수 있고, for 또한 마찬가지이다.

위의 코드를 콘솔창에서 돌려보면 에러가 발생하지 않고 모든 값이 출력된다.





2. 변수명의 중복 허용


1
2
3
4
5
6
var scope = 10;  
function scopeExam(){  
    var scope = 20;
    console.log("scope = " +scope);
}
scopeExam();
cs


처음 자바스크립트를 접했을 때 느낀점은 관대하다는 것 이였다.

위의 사례도 그 중 하나이다.

변수명이 중복될 경우 Java나 다른 언어는 바로 컴파일 에러를 발생시키면서 코드가 실행되지 않지만 

자바스크립트는 그렇지 않다.

실행되고 있는 컨텍스트(context)에 있는 변수를 우선적으로 찾아 실행을 하게되면

이를 스코프 체이닝(Scope Chaining) 이라고 한다.


가까운 변수를 참조 한다고 말할수도 있겠으나, scopeExam 함수가 끝난 바로 아래에서

변수 scope를 호출할 경우, 전역으로 선언된 scope가 불려지기 때문에

현재 실행되고 있는 context의 변수를 찾는다고 할 수 있다.


만약 해당 컨텍스트에서 변수를 찾지 못하면 상위 컨텍스트로 넘어가며

최상위 main 컨텍스트에서도 변수를 찾지 못하면 그때서야 에러가 발생한다.





3. var 키워드의 생략


1
2
3
4
5
6
7
8
9
10
function scopeExam(){  
    scope = 20;
    console.log("scope = " +scope);
}
 
function scopeExam2(){  
    console.log("scope = " + scope);
}
scopeExam();  
scopeExam2();  
cs


자바스크립트의 관대함의 연장선에 있는 특징이다.

자그마치 var 키워드가 생략 가능하다.

이렇게 var 키워드가 생략 되었을 때는 전역으로 판단되어, 최상위 컨텍스트에 등록된다.


따라서 첫번째 함수에서 선언된 변수지만 최상위 컨텍스트로 이동하여 전역 변수가 되며,

때문에 두번째 함수에서도 호출이 가능하다.


이러한 특징 때문에 자신도 모르는 사이에 var를 생략하여 전역 변수를 만드는 일이 생길수 있다.

이렇게 되면 변수끼리 이름이 부딪혀 예상치 못한 결과를 불러 올 수 있으니

평소에 각 텍스트 에디터에서 제공되는 JSHint등을 사용하도록 하자.





4. 렉시컬 특성


1
2
3
4
5
6
7
8
function f1(){  
    var a= 10;
    f2();
}
function f2(){  
    return a;
}
f1();
cs


렉시컬 특성이란 함수 실행 시 유효 범위를 함수 실행 환경이 아닌 함수 정의 환경으로 참조하는 특성이다. 

위의 코드를 보면 f1에 지역변수 a가 선언되어있고 내부에서 f2를 호출하는데, 

f2를 '선언할 때' 지역변수 a를 가져올수 없으므로 a의 값은 찾을수 없게 된다.


즉, 함수를 정의 할 때의 상태만 반영 되며, 함수가 생성되고 난 후의 상태는 의미가 없다는 것이다.





let과 const


위에서 살펴본대로 자바스크립트의 var는 장점과 단점이 둘 다 존재한다.

여기서 크나큰 단점이 존재하는데, 함수 단위 스코프와 전역에 대한 처리이다.


기존 블럭단위 스코프 패턴에 익숙해져있던 프로그래머들로 하여금 var의 스코프는 사용하기 난감했었고

여러 개로 나뉜 js 파일들이 합쳐지면서 전역으로 선언된 변수들이 충돌하거나 예상치못한 오류를 낳을때가 있다.


따라서 스코프에 대한 해결책의 수요가 증가하는 가운데 ES6에서 새로운 변수 키워드를 제공하게 되었다.

바로 let과 const 이다.


1. let

et은 블록 단위의 변수 타입이며 var보다 엄격하므로, 같은 스코프내에서 중복이나 호이스팅을 허용하지 않는다.

 따라서 let의 유효범위는 {}, 브라켓 내부가 된다.


2.const

const는 타 언어에서 상수로 사용되는 변수 타입이다. 

실제로 원시형 변수에 대해서는 const를 사용하게 되면 상수의 역할을 하게된다(초기값을 설정하지 않으면 에러 발생). 

하지만 참조형 변수(Object, array, function)의 경우 const로 선언하는것이 좋은데, 

참조형 변수의 경우 const로 선언하더라도 멤버값을 조작하는것이 가능하다.

 이유는 해당 참조형 변수를 가르키는 값이 변한것이 아니라 메모리 내부의 변수값을 변경한 것이기 때문이다. 

따라서 const로 지정된 참조형 변수의 참조값을 변경하려고 할 경우만 오류가 일어난다. 

마찬가지로 블록 스코프를 가진다.


저작자 표시
신고

객체(Object)




1
2
3
4
var MyInfo = {
  firstName: 'nu',
  lastName: 'nu'
};
cs


자바스크립트에는 

(Boolean, Null, Undefined, Number, String, Symbol (ES6), Object)

다음과 같은 7개의 자료형이 존재한다.


이 중에 Object(객체)를 제외한 자료형은 원시형(Primitive type)이며 Object는 원시형이 아니다.

함수 또한 1급 객체로써 Object 타입에 들어가며, 배열도 객체이다.


위의 코드에서 콤마로 구분되는 값들을 속성(Property)이라고 한다.

이 때, 좌측의 값이 key, 우측의 값이 value이다.

즉 속성은 Key : Value 값으로 이루어져있다.





배열(Array)


1
2
var firstArray = [];
var secondArray = [100"good", [1,2,3], { one: 1 }];
cs


앞서 말한것 처럼 자바스크립트에서는 배열도 객체이다.

배열의 특징으로는 [] 괄호로 표현되며, 이 안에 데이터를 쉼표를 구분하여 넣게 된다.

그리고 데이터는 원시형과, 배열, 객체를 모두 담을 수 있다.

또한, 각 주소값(index)마다 순서가 있으며, 일반적인 객체와 다르게 키 값이 없다.


좀 더 다르게 말하자면 배열에서는 주소값이 키의 역할을 한다고 생각하면 편하다.

다른 언어에서와 마찬가지로 배열이 가지고 있는 가장 큰 장점중의 하나가 

순서가 있고, 주소값으로 접근이 가능하다는 것이다.





자바스크립트 객체의 생성방법



1. 객체 리터럴


1
2
3
4
5
6
7
8
9
var obj = {
    name : "jim",
    gender : "male",
    phoneNumber : "01029320323",
    
    callPeople : function(){        
    console.log("hello " + this.name +"!");
    }
};
cs


일반적인 객체를 선언하는데 자바 스크립트에서 제공하는 리터럴을 사용하자.

리터럴 방식이란 var obj = { } 이나 var arr = [] 와 같은 방식으로 선언하는 것을 말한다.

리터럴이 아닌 방식은 obj = new Object() 나 arr = new Array() 등이 있다.


공식 문서에서는 리터럴 방식을 권장하는데 리터럴을 사용할 경우

1. 가독성 향상

2. 속도 향상

3. Object나 Array라는 생성자 함수의 Override방지

3가지 장점이 있기 때문이다.


리터럴 방식으로 객체를 만들면 반복적으로 동일한 객체 생성하기 위해 

하드코딩 해야하는 단점이 있다.





2. 생성자 패턴


1
2
3
4
5
6
7
8
9
10
11
function People(name, gender, phoneNumber){
    this.name = name;
    this.gender = gender;
    this.phoneNumber = phoneNumber;
    this.callPeople = function(){
        console.log("hello " + this.name +"!");
    }
}
 
var john = new People("john","male","0293029302");
var jim = new People("jim","male","01029392839");
cs


앞서 살펴본 리터럴 생성과는 다르게, 동일한 속성값을 가지는 인스턴스를 여러개 만들어야한다면

베이스가 되는 생성자를 만들고, 이를 new 키워드로 인스턴스화 시켜줄 수 있다.


생성자는 기본적으로 함수의 모습을 하고 있는데, 

자바스크립트는 언어내에서 별도로 생성자 함수와 함수의 구분을 지어줄만한 것을 제공해주지 않는다.

그러므로 함수가 있을 때, 일반적인 호출과 new 키워드를 이용한 호출이 동시에 가능하다.


생성자는 인스턴스를 만드는데 필요한 설계도와 같은 역할을 한다.

따라서 프로퍼티를 선언할 때, this 키워드를 사용한다.


위 코드와 같은 생성자 함수에서는 prototype을 사용하지 않으므로 

내부에 정의된 메소드는 인스턴스를 만들때마다 계속 생겨 메모리를 낭비하는 단점이 있다.





3. 생성자 패턴 + 프로토타입


1
2
3
4
5
6
7
8
9
10
11
12
13
function People(name, gender, phoneNumber){
    this.name = name;
    this.gender = gender;
    this.phoneNumber = phoneNumber;
 
}
 
People.prototype.callPeople = function(){        
    console.log("hello " + this.name +"!");
}
 
var john = new People("john","male","0293029302");
var jim = new People("jim","male","01029392839");
cs

기존의 생성자 패턴에 메소드를 prototype에 추가시키는 패턴이다.
함수를 제외한 원시형에 따른 프로퍼티들은 생성자 내부에 정의하고
인스턴스마다 동일하게 사용되는 메소드는 객체에 기본적으로 생기는 prototype에 넣어뒀다.

메소드를 생성자로 사용되는 함수에 넣지않고 prototype에 넣을 때 장점은
인스턴스화 될 때 메소드가 해당 인스턴스마다 만들어지지 않고,
메모리에서 하나의 메소드만을 참조하기 때문에 메모리 낭비가 작아진다.


저작자 표시
신고

 Jasmine을 이용한 테스트 코드 작성




테스트 주도 개발(TDD)에서 테스트 코드는 가장 중요한 요소로 손꼽힌다.

이러한 테스트코드 작성을 도와주기 위한 툴(라이브러리)은 여러개가 있는데

그 중 자바스크립트 관련 도서에서 자주 언급되는 자스민(jasmine)을 사용하는 기본적인 방법에 대해 알아보자




1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!DOCTYPE html>
<html lang="en">
 
<head>
    <title>test</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link data-require="jasmine@*" data-semver="2.0.0" rel="stylesheet" href="http://cdn.jsdelivr.net/jasmine/2.0.0/jasmine.css"
    />
    <script data-require="jasmine@*" data-semver="2.0.0" src="http://cdn.jsdelivr.net/jasmine/2.0.0/jasmine.js">
    </script>
    <script data-require="jasmine@*" data-semver="2.0.0" src="http://cdn.jsdelivr.net/jasmine/2.0.0/jasmine-html.js">
    </script>
    <script data-require="jasmine@*" data-semver="2.0.0" src="http://cdn.jsdelivr.net/jasmine/2.0.0/boot.js">
    </script>
</head>
 
<body>
    <script src="main.js"></script>
    <script src="main_test.js"></script>
</body>
 
</html>
cs


우선 테스트 코드를 사용하기 위한 기본적인 HTML 코드이다.

자스민은 다운받은 후 폴더에 추가하여 사용 할 수도 있지만 

개인적으로 이렇게 예제 용으로 작성하는것은 CDN을 선호하므로 위와 같이 자스민 코드의 link를 걸어준다.

그리고 하단에 테스트를 원하는 js파일과 이에 대한 테스트 검증 코드를 추가 해준다.




1
2
3
4
5
6
7
function helloWorld() {
    return "Hello world!"
}
 
function welcomeWorld(){
    return "Welcome world!";
}
cs

테스트를 원하는 코드이다.
간단하게, 호출하면 2개의 텍스트를 반환하는 함수를 만들었다.



1
2
3
4
5
6
7
8
9
10
11
describe("Hello world"function() {
    it("says hello"function() {
        expect(helloWorld()).toEqual("Hello world!");
    });
});
 
describe("welcome world"function() {
    it("says welcome"function() {
        expect(welcomeWorld()).toEqual("Welcome world!");
    });
});
cs


다음은 이에 대한 테스트를 수행하는 코드이다.

테스트에 가장 결정적인 함수는 expect인데 expect에 들어가는 인자와 

메소드 체이닝된 toEqual의 인자가 같아야 테스트 성공 메세지가 뜨게 된다.

이와같은 expect - toEqual을 여러개 작성해서 다수의 테스트를 진행할수도 있다. 





결과는 다음과 같다.

저작자 표시
신고

비동기(asynchronous) 방식의 동작 원리




웹을 공부하다가, 이벤트 루프 콜백 함수가 비동기 적으로 동작한다고 했을 때

내부에서 어떤식으로 돌아가는지 궁금했었다.

비동기로 동작하면 그냥 순서에 상관 없이 실행되는구나~ 하면서 넘겼는데

멀티 스레드에서는 각 스레드 별로 함수를 실행하면 되는데

싱글 스레드 환경에서는 동작 방식이 아무래도 납득이 가지 않았다.


그러던중 다음과 같은 컨퍼런스 영상을 보고 

비동기 방식의 동작원리에 대해서 어느정도 생각해보게 되었다.


https://www.youtube.com/watch?v=8aGhZQkoFbQ






자바 스크립트에서 함수를 실행 시키면 함수가 call stack에 쌓이며 

LIFO(Last in First out)방식으로, 나중에 들어온 함수부터 처리하게 된다.

이는 자바 스크립트 뿐만 아니라 다른 언어에서도 거의 동일하게 볼 수 있다.





이 때, 다음과 같이 무한 루프를 순회하면 call stack의 용량이 초과되며

'stack overflow' 상태로 오류가 발생한다.

크롬 개발자 창에서는 

'RangeError : Maxmum call stack size exceeded'

경고가 뜬다





자바 스크립트는 싱글스레드로 동작하므로 1개의 call stack에서 함수를 처리한다.

그렇다면 위와 같은 예제에서는 어떻게 동작하는 걸까

stack이 1개 이기때문에 jquery로 getSync 함수를 호출하면

해당 함수가 리턴 될때 까지 로딩상태에 빠지게 된다.

그때까지 아래의 bar와 qux는 동작하지 못한다.


bar와 qux로 순서가 넘어왔을때도 비슷한 상황이 된다.

이와 같은 상황이 지속된다면, 클라이언트에서는 사용자가 원하는 시간내에

view를 응답하지 못하게 되며, 좋은 서비스를 제공하지 못한다.







그렇다면 이와 같은 문제를 해결할 수 있는 방법은 무엇일까.

바로 비동기 콜백 함수이다.

위의 그림은 비동기 콜백 함수의 예제이다.

setTimeout은 1개의 콜백함수와 1개의 숫자를 인자로 받는데,

입력한 숫자만큼의 시간이 지났을 때 콜백 함수가 동작하게 된다.

(인자는 1000이 1초가 된다)


결론부터 말하면 setTimeout 함수가 먼저 stack에 등록되었다가 실행되면서 사라지고

바로 밑의 console.log('JSConfEU')가 실행된 후,

5초가 지난뒤 setTimeout의 콜백함수의 내용인 console.log('there')이 실행된다.





동작 원리는 다음과 같다.

setTimeout은 위와 같이 call stack에 쌓인다.

그리고 비 동기적으로 동작하기때문에 setTimeout의 Timer를 

브라우저가 제공하는 webapi에 등록한다.


그리고 call stack에서 setTimeout은 사라지게 된다.

이 후, 가장 밑줄의 console.log('JSConfEU')가 실행되고 stack에서 사라진다.

물론, main 함수도 코드가 끝까지 실행되었으므로 stack에서 사라진다.





webapi에 등록된 타이머는 계속 동작하다가 5초가 되었을때 동작을 중지하고

콜백함수를 task queue로 전달한다.

queue는 FIFO(First in First out)의 구조를 가졌으므로

stack와 다르게 먼저 들어온 함수가 우선권을 가진다.





task queue에서 대기중이던 콜백함수는 stack에 아무런 함수가 쌓이지 않았을 때

queue에서 빠져나와 call stack으로 넘어가 실행되게 된다.

이와 같은 동작 원리로 웹의 비동기 처리가 완성된다.


원본 영상에서는 애니메이션 효과로 다이나믹하게 동작 원리를 설명한다.

또한 예제를 보여주며 실제로 어떤식으로 보여지는지 설명해준다.

자막은 없지만, 공대생들이 사용하는 언어가 그리 어렵지 않으므로

동영상을 시청해 보는것을 추천한다.

저작자 표시
신고

private 변수가 있는 클래스 생성



JavaScript 코드에서 Ajax로 Json 파일을 받아오는 예제를 만드는중이다.

서버가 없었지만 나름대로 view 내부에서 MVC패턴을 구현하던 중에

 Model 쪽 코드에서 데이터를 관리하기 위해 private 변수가 있는 객체가 필요하게 됐다

아래는 실제 예제에서 사용한 private 변수가 있는 클래스 생성 코드 샘플이다



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var dataObjFn = (function() {
 
   var json = ["aaaa"];
 
   function dataModelObj() {
 
   }
 
   dataModelObj.prototype = {
       getName : function() {
           return json;
       }
   }
 
   return dataModelObj;
 
})();
 
var dd = new dataObjFn();
cs



코드를 자세히 보면 클로저가 사용 되었음을 알 수 있다.


1. 우선 dataObjFn 변수 내부에 private로 사용될 변수를 선언한다.


2. dataModelObj와 여기에 들어갈 프로토타입 메소드나 변수를 선언한다.


3. dataModelObj를 리턴하는 함수를 바로 실행해서 변수(dataObjFn)에 저장한다.

이 변수는 dataModelObj를 리턴받아서 생성자로 쓰인다. 

console에 변수 이름을 출력하면 리턴된 dataModelObj가 나온다.


4. 이렇게 만들어진 dataObjFn()를 사용하여 필요한 만큼 인스턴스 객체를 만들어낸다.

저작자 표시
신고

+ Recent posts

티스토리 툴바