비트 연산이란?

비트 연산이라 함은 &, |, ~, ^ 을 이용한 연산을 의미한다.

비트 연산은 언제 사용하는가?

우선 비트 연산을 사용하면 bit라는 최소 정보단위로 다른 추상화된 정보들과는 달리 재현의 과정을 거치지 않는다.(그렇기에 속도는 빠르다)

즉, 각 비트들의 0 혹은 1이라는 값을 특정 의미를 가지는 플래그로 활용한다.

비트 연산 예시

우선 아래의 예시를 보자.

const OPTION_A = 0x01;
const OPTION_B = 0x02;
const OPTION_C = 0x04;
const OPTION_D = 0x08;

위의 OPTION A, B, C, D의 값은 16진수로 표현하였다.

즉 16진수는 4비트로 표현이되며 A는 0001(1), B는 0010(2), C는 0100(4), D는 1000(8)을 의미한다.

만약 1바이트(8비트)를 표현하고 싶다면 어떻게 해야 될까?

아래의 예제를 보며 이해를 해보길 바란다.

const OPTION_E = 0x10;

E는 0001 0000을 의미한다. 추가적으로 비트를 늘릴 때는 위의 원리를 이용하면 된다.

해당 비트를 1로 변경할 때는 아래의 예제를 참고하자.

var flag |= OPTION_A

해당 비트가 1인지 확인할 때는 아래의 예제를 참고하자.

var flag &= OPTION_A

해당 비트를 0으로 변경할 때는 아래의 예제를 참고하자.

var flag &= ~(OPTION_A)

정리

비트 연사을 사용하면 하나의 정수로 여러 플래그의 표현이 가능하다.

비트 연산은 알고리즘 문제에 대해서도 많이 사용되므로 정확히 이해하고 넘어가면 좋을 것 같다.

'Language > Javascript' 카테고리의 다른 글

Name Spacing Pattern  (0) 2017.07.28
promise - 2 [Promise 사용 시 주의할 것]  (0) 2017.07.28
promise - 1 [then, catch, resolve, reject]  (0) 2017.07.28
closure  (0) 2017.07.28
scope - 3 [변수 숨기기]  (0) 2017.07.28

Namespacing Pattern

객체를 생성하는 패턴들로 Name space를 사용하게 되면 필요로 하는 전역 변수의 개수를 줄일 수 있습니다.

객체 리터럴 네임스페이싱(Object Literal Namespacing)

가장 기본적인 네임스페이스 패턴으로 하나의 전역 객체를 선언 후 함수, 객체, 변수를 여기에 추가하는 방식입니다.

단, namespace를 사용하고 있는 객체는 this를 사용해서 참조하면 안된다.

var APP = {};
 
APP.function1 = function() {
  console.log('function1');
}
APP.function2 = function() {
  console.log('function2');
}
 
// 변수를 추가하는 경우
APP.variable = 1;
 
// 객체 컨테이너를 추가하는 경우
APP.modules = {};
APP.modules.module1 = {};
APP.modules.module1.data = 1;

범용 네임스페이스 함수

이 방식은 재정의를 막기 위해 미리 선언되었는지 확인하고 정의해주는 함수를 말합니다.

APP.namespace = function(string) {
  // 단일 var 패턴으로 변수들 선언
  // string 문자열을 '.' 를 기준으로 분리
  var parts = ns_string.split('.'),
  parent = APP,
  i;
 
  if(parts[0] === "APP") {
    //parts 배열에서 parts[0] 원소를 삭제
    parts = parts.slice(1);
  }
  for(i=0 ; i<parts.length ; i++) {
    // 중복되지 않으면 생성
    if(typeof parent[parts[i]] == "undefined") {
      parent[parts[i]] = {};
    }
    parent = parent[parts[i]];
  }
  return parent;
}


'Language > Javascript' 카테고리의 다른 글

다중 플래그값을 위한 비트 연산  (0) 2017.12.30
promise - 2 [Promise 사용 시 주의할 것]  (0) 2017.07.28
promise - 1 [then, catch, resolve, reject]  (0) 2017.07.28
closure  (0) 2017.07.28
scope - 3 [변수 숨기기]  (0) 2017.07.28

Promise의 올바른 사용

Promise를 배우고 좀 사용하다 보면 Promise가 너무 무분별하게 사용되는 건 아닌지, 잘 사용하고 있는지 고민될 때가 있습니다.

composing promise

비동기 프로그래밍 시 callback hell 의 문제를 해결 하기 위해 promise를 사용하는데 promise hell? 처럼 promise를 사용하면서도 pyramid 형식으로 작성하는 것을 피해야 합니다.

// A bad example
promise1()
.then(function(result) {
  promise2()
  .then(function(result) {
    promise3()
    .then(function(result) {
      /* */
    })
    .catch(function(error){
      console.log(error);
    })
  })
});

위의 방식을 아래 방식처럼 표현한 것을 composing promise라 부릅니다.

// A good example
promise1()
.then(function(result) {
  return promise2();
})
.then(function(result) {
  return promise3();
})
.catch(function(error) {
  console.log(error);
});

catch를 항상 사용하자

에러가 발생하지 않는다고 확신해도 catch를 붙이는 습관을 갖도록 합니다.

promise1()
.then(function(result) {
  /* */
})
.catch(function(error) {
  console.log(error);
});

return 을 이용하자

then() 메서드 안에 return 과 throw를 이용합니다. return은 비동기적으로 발생된 값 뿐만 아니라 동기적으로 발생된 값도 전달할 수 있습니다.

promise1().then(function() {
  // return value or undefined
  if(동기적으로 발생한 값을 리턴할 경우)
    return value;
 
  // throw error
  if(에러일 시)
    throw new Error("error");
 
  // return another promise1
    return promise2();
 
});

아래 예제는 return 을 사용하지 않았을 때 p2와 p3 가 동기적으로 실행되지 못한 것을 확인할 수 있습니다. return을 사용하지 않아 p2와 p3가 비동기적으로 실행이 되어 part 2 부분에서 flag 값이 undefined가 출력되었습니다.

var p1 = function() {
  return new Promise(function(resolve, reject) {
    console.log(1);
    resolve(1);
  });
};
 
var p2 = function() {
  return new Promise(function(resolve, reject) {
    console.log(2);
    resolve(2);
  });
};
 
var p3 = function() {
  return new Promise(function(resolve, reject) {
    console.log(3);
    resolve(3);
  });
};
 
var flag = 1;
 
p1()
.then(function(data) {
  // part 1
  flag = data;
  console.log("flag : " + flag);  // flag : 1
  p2();
})
.then(function(data) {
  // part 2
  flag = data;
  console.log("flag : " + flag);  // flag : undefined
  p3();
});

then 은 함수 형태로 전달되어야 한다.

then은 함수 형태로 전달해야 한다는 기억해야 합니다. 아래의 예제에서 result 값으로 bar 가 출력됨을 예상하겠지만 foo 가 출력됩니다. 그 이유는 then() 함수에 함수의 형태가 아닌 것을 전달하기 때문입니다.

Promise.resolve('foo')
.then(Promise.resolve('bar'))
.then(function(result) {
    console.log(result);  // foo
})

아래의 형태로 함수를 then() 메서드로 함수 형태로 promise 를 리턴해야 합니다.

Promise.resolve('foo')
.then(function(result) {
  return Promise.resolve('bar');
})
.then(function(result) {
  console.log(result);
})


'Language > Javascript' 카테고리의 다른 글

다중 플래그값을 위한 비트 연산  (0) 2017.12.30
Name Spacing Pattern  (0) 2017.07.28
promise - 1 [then, catch, resolve, reject]  (0) 2017.07.28
closure  (0) 2017.07.28
scope - 3 [변수 숨기기]  (0) 2017.07.28

promise란?

자바스크립트의 callback Hell 문제를 해결하기 위해 사용합니다. 하지만 그 외에도 비동기 프로그래밍을 하면서 사용하기 어렵게 된 throw와 return을 사용할 수 있게 해줍니다.

Promise.prototype.then()

then() 메서드를 사용하게 되면 Promise를 리턴하게 되고 성공했을 때 실행되는 콜백함수 resolve, 실패했을 때 실행되는 콜백 함수 reject 를 인수로 받습니다.

var promise1 = new Promise(function(resolve, reject) {
  resolve("success!");
  // 또는
  // reject ("error!");
});
 
promise1.then(function(data) {
  console.log(data); // 성공!
}, function(error) {
  console.log(reason); // 오류!
});

위의 개념을 이용하여 chaining 개념을 잡을 수 있습니다.

var promise2 = new Promise(function(resolve, reject) {
  resolve(1);
});
 
promise2.then(function(data) {
  console.log(data); // 1
  return data + 1;
}).then(function(data) {
  console.log(data); // 2
});
 
promise2.then(function(data) {
  console.log(data); // 1
});

Promise.resolve(data)

Promise.prototype.then()에서 인자로 받았던 resolve, reject 중 Promise.resolve 함수를 사용하게 되면 결정된 Promise를 반환합니다.(즉, reject 부분은 무시하게 됩니다.)

Promise.resolve("sucess").then(function(data) {
  console.log(data); // "Success"
}, function(error) {
  // 호출되지 않음
});

Promise.reject(error)

Promise.resolve 와 마찬가지로 거부된 Promise를 반환하게 하여 resolve 부분을 무시하게 합니다.

Promise.reject("test promise reject").then(function(reason) {
  // 호출되지 않음
}, function(reason) {
  console.log(reason); // "test promise reject"
});

Promise.prototype.catch()

catch() 는 거부된 Promise를 반환받게 되었을 때 실행됩니다.

var promise1 = new Promise(function(resolve, reject) {
  resolve('success');
});
 
promise1.then(function(data) {
  console.log(data); // "success!"
  return Promise.reject('reject');
}).catch(function(e) {
  console.log(e); // "reject"
});

promise는 비동기 콜백 내에서 발생한 오류는 잡을 수 없습니다. 즉, 직접적으로 Promise 스코프에 있는 Error 처리만 잡을 수 있습니다.

var promise1 = new Promise(function(resolve, reject) {
  throw 'reject error in promise1';
});
 
promise1.catch(function(e) {
  console.log(e); // "reject error in promise1"
});
 
var promise2 = new Promise(function(resolve, reject) {
  setTimeout(function() {
    throw 'reject error in async callback function';
  }, 1000);
});
 
p2.catch(function(e) {
  console.log(e); // 이는 전혀 호출되지 않음
});

Promise.all(iterable)

인자로 Promise 값들로 이루어진 배열을 받아 resolve 된 Promise 값으로 이루어진 배열을 반환합니다.

var promise1 = Promise.resolve(1);
var promise2 = 2; // Promise가 아닌 값은 자동으로 Promise.resolve 값으로 계산
 
var promise3 = new Promise(function(resolve, reject) {
  resolve(3);
});
 
Promise.all([promise1, promise2, promise3]).then(function(values) {
  console.log(values); // [1, 2, 3]
});

만약 전달 된 배열 중에서 Promise 값들 중 하나라도 reject 되면 Promise.all은 reject 된 Promise를 반환하게 됩니다.

var promise1 = Promise.resolve(1);
var promise2 = 2;
var promise3 = new Promise(function(resolve, reject) {
  reject("reject");
});
 
Promise.all([promise1, promise2, promise3]).then(function(values) {
  console.log(values); // [1, 2, 3]
})
.catch(function(error) {
  console.log(error);
});


'Language > Javascript' 카테고리의 다른 글

Name Spacing Pattern  (0) 2017.07.28
promise - 2 [Promise 사용 시 주의할 것]  (0) 2017.07.28
closure  (0) 2017.07.28
scope - 3 [변수 숨기기]  (0) 2017.07.28
Scope - 2 [정적, 동적, 블록, 중첩 스코프]  (0) 2017.07.21

Closure

클로저란 자바스크립트에 있어서 C언어의 포인터와 같은 관점으로 볼 수 있습니다. 먼저 scope chain에 대해 알아야 합니다.

scope chain

scope는 비동기 함수가 호출될 때까지 계속해서 지속되어 참고가 됩니다.(scope의 지속성) 새로운 scope를 생성함으로써 비동기적으로 호출 될 때의 scope를 조율할 수 있습니다.

아래의 예제에서는 outer() 함수 호출 후 inner() 내에 변수 a 에 x 를 대입할 때 실행문을 포함하고 있는 함수의 변수 스코프부터 먼저 검색됩니다. 만약 없는 경우 상위 스코프로 검색을 시작하게 되며 전역 스코프에도 없는 경우 에러를 발생시킵니다.

var x = 1;
function outer() {
  var y = 2;
  function inner() {
    var a = x;
  }
}
 
outer();

Clusure

클로저란 함수가 함수 내부에 선언된 변수에 접근할 수 있는 함수를 말합니다.

클로저는 독립적인 (자유)변수를 가리키는 함수이며 클로저 안에 정의된 함수는 만들어진 환경을 '기억'합니다.

아래의 예제를 통해 알아보겠습니다.

function makeFunc() {
  var name = "Mozilla";
  function displayName() {
    alert(name);
  }
  return displayName;
}
 
var myFunc = makeFunc();
myFunc();

위의 예제에서 name 변수는 makeFunc라는 함수 스코프에 존재합니다. 즉 함수 외부에서는 접근을 할 수 없습니다. 하지만 displayName라는 함수 내부의 함수를 name가 name 변수를 참조하게 하여 함수 외부에서도 접근할 수 있도록 return 을 하여 myFunc() 함수에서도 name을 접근할 수 있습니다.

생성 시 환경을 기억하는 클로저

클로저는 만들어질 때의 환경을 기억합니다. 다음 예제를 살펴보도록 하겠습니다.

var i;
for (i = 0; i < 10; i++) {
  setTimeout(function() {
    console.log(i);
  }, 100);
}

위 예제는 0~9까지의 출력을 예상할 수 있지만 setTimeout 함수에 의해 0.1초를 기다리는 동안 i가 10번을 돌게 되어 익명 함수 호출시에는 i가 이미 10이 되어 10을 10번 출력하게 됩니다.

이 상황을 클로저를 이용하여 생성 당시의 환경(i의 값)을 기억하도록 하여 setTimeout 함수에 의해 0.1초 후 익명함수가 호출되더라도 기억하고 있는 i의 값을 출력합니다.

var i;
for (i = 0; i < 10; i++) {
  (function(j) {
    setTimeout(function() {
      console.log(j);
    }, 100);
  })(i);
}

객체와 클로저를 이용한 데이터 은닉

일반적으로 객체를 정의할 때 prototype을 이용하지만 이 방법을 사용하게 되면 외부로부터 데이터를 보호할 수 없게 됩니다.

function Hello(name) {
  this._name = name;
}
 
Hello.prototype.say = function() {
  console.log('Hello, ' + this._name);
}
 
var hello = new Hello("person");
 
hello._name = "person2";

이 경우 클로저를 이용하여 외부로부터 데이터를 보호할 수 있습니다.

function Hello(name) {
  var _name = name;
  return function() {
    console.log('Hello, ' + _name);
  };
}
 
var hello = Hello("person");
 
hello();

Object VS Closure

먼저 객체 생성 시 객체의 프로토타입을 연결하는 방식이 더 좋다. 그 이유는 Object를 사용하게되면 생성자가 호출 될 때마다 메서드가 다시 할당된다.

function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
  this.getName = function() {
    return this.name;
  };
 
  this.getMessage = function() {
    return this.message;
  };
}

클로저를 이용하게 되면 이 부분을 보완할 수 있으며 prototype에 다시 정의하는 방법보다 기존 프로토타입에 정의를 하는 것이 좋습니다.

function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
}
 
/*
MyObject.prototype = {
  getName: function() {
    return this.name;
  },
  getMessage: function() {
    return this.message;
  }
};
*/
 
MyObject.prototype.getName = function() {
  return this.name;
};
MyObject.prototype.getMessage = function() {
  return this.message;
};

위 코드에서 즉시 함수 실행 기법 을 사용하면 더 좋은 성능을 얻을 수 있습니다. (함수 표현은 함수 정의 및 저장 후 실행하는 과정을 거치지만 즉시 실행함수를 사용하게 되면 함수 정의 후 바로 실행하여 위의 과정을 거치지 않습니다.)

function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
}
 
(function() {
    this.getName = function() {
        return this.name;
    };
    this.getMessage = function() {
        return this.message;
    };
}).call(MyObject.prototype);

클로저 사용 후 정리하기

c 와 c++ 에서 동적할당 후 마지막에 동적해제하는 과정을 거칩니다. 그 이유는 메모리를 회수하기 위함입니다. 자바스크립트에서는 스코프 체인 성질에 의해 어떠한 변수 혹은 함수가 참조를 하고 있으면 내부 변수가 차지하는 메모리를 GC(Garbage Collector)가 회수하지 않습니다. 따라서, 클로저 사용 후 참조를 제거하는 것이 좋습니다.

hello = null;


변수를 숨기는 방법들

var를 이용하는 방법

아래의 예제에서 var를 이용하여 b를 외부로부터 숨겼습니다. var를 사용하지 않고 선언할 경우 전역으로 선언되기 때문입니다.

function f1(a) {
  function f2(a) {
    return a - 1;
  }
 
  var b;
  b = a + f2(a);
  console.log(b);
}
 
f1(2);  // 3

같은 이름을 선언하는 방법

아래의 예제는 같은 이름을 선언하여 외부의 x에 접근하지 못하도록 하였습니다.스코프 체인 성질 을 이용하여 정의되고 있는 함수의 변수 스코프에서부터 먼저 검색되기 때문에 외부의 x에 접근하기 전에 내부 블록에 x를 선언 후 정의함으로써 외부 블록의 x에 접근을 못하게 합니다.

{
  let x = 'blue';
  console.log(x); // blue
  {
    let x = 'red';
    console.log(x); // red
  }
  console.log(x);  // blue
}

모듈 패턴 사용

아래의 예제는 모듈화를 하여 변수에 접근가능한 함수만을 제공하여 직접적으로 변수에는 접근하지 못하게 하였습니다.

function examModule() {
    var something = "cool";
    var another = [1,2,3];
 
    function doingSome() {
        console.log(something);
    }
 
    function doingAnother() {
        console.log(another);
    }
 
    return {
        doSomething: doSomething,
        doAnother: doAnother
    };
}


정적 스코프

렉시컬 스코프(Lexical Scope)라고도 불립니다. 렉시컬 특성을 활용한 스코프 방식입니다. 렉시컬 특성은 함수 실행 시 유효범위를 함수 정의 환경을 기준으로 하기 때문에 아래의 예제처럼 func1 함수에서 global 이 출력이 되었음을 확인할 수 있습니다.

var x = "global"
 
function func1() {
  var x = "local";
  func2();
}
 
function func2() {
  console.log(x);
}
 
func1();  // global
func2();  // global

동적 스코프

렉시컬 소코프가 소스코드가 작성된 그 문맥에서 결정된다고 하면, 동적 스코프는 프로그램의 런타임 도중의 실행 컨텍스트나 호출 컨텍스트에 의해 결정이 됩니다. 자바스크립트는 렉시컬 특성을 가지고 있습니다.

블록 스코프

자바스크립트는 기본적으로 함수 스코프를 지원하지만, 블록 스코프도 역시 지원한다.

  • with 역시 블록 스코프를 지원하지만 추가적인 스코프를 생성하는 추가적인 자원 소모가 있기 때문에 사용하지 말자.

  • try/catch 의 catch 부분에서 선언된 변수는 catch 블록 스코프에 속한다

try {
  f();
} catch(err) {
  console.log(err);
}
 
console.log(err);   // ReferenceError
  • let키워드(ES6)를 사용하면 선언된 변수를 둘러싼 블록의 스코프를 이용한다.

var foo = true;
 
if(foo) {
  {
    let bar = foo * 2;
    console.log(bar);
  }
}
 
console.log(bar);   // ReferenceError

중첩 스코프

중첩 스코프는 가장 인접한 지역을 우선하여 참조합니다.

var foo = function ( ) {
 
  var a = 2, b = 3;
 
  var bar = function ( ) {
    var b = 5, c = 6;
 
    // 이 시점에서 a는 2, b는 5, c는 6
 
    a += b + c;
 
    // 이 시점에서 a는 13, b는 5, c는 6
 
  };
 
  // 이 시점에서 a는 2, b는 3, c는 not defined
 
  bar( );
 
// 이 시점에서 a는 13, b는 3
};


'Language > Javascript' 카테고리의 다른 글

promise - 2 [Promise 사용 시 주의할 것]  (0) 2017.07.28
promise - 1 [then, catch, resolve, reject]  (0) 2017.07.28
closure  (0) 2017.07.28
scope - 3 [변수 숨기기]  (0) 2017.07.28
Scope - 1 [스코프란 무엇일까?]  (0) 2017.07.21

Scope

Scopes는 유효범위를 나타내며, 전역 유효 범위와 지역 유효범위로 나누어집니다.

자바스크립트의 유효범위란?

크게 아래의 특징이 있습니다.

함수 단위의 유효범위

블로 단위가 아닌 함수 단위로 유효 범위가 정의되기 때문에 아래의 a, b, c 변수는 모두 같은 유효범위를 갖습니다.

function A() {  
    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);
}
A();  

변수명 중복 허용

같은 변수명이 여러 개 있을 경우, 변수를 참조할 때 가장 가까운 범위의 변수를 참조합니다.

var scope = 10;  
function A(){  
    var scope = 20;
    console.log("scope = " +scope);
}
A();  

var 키워드의 생략

var 키워드를 빼먹고 변수를 선언할 경우 전역 변수로서 선언이 됩니다.

렉시컬 특성

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

아래 예제처럼 함수 정의될 때의 환경을 보기 때문에 변수 a를 찾을 수 없다는 에러 메시지가 출력됩니다.

function f1(){  
    var a= 10;
    f2();
}
function f2(){  
    return a;
}
f1();
 
// Error : a is not defined

아래 코드를 실행하게 되면 전역 전역 변수 a와 함수 변수 f가 정의가 됩니다. 그런 다음에 f()를 실행하면 함수 f가 호출되고 f 레벨의 파싱이 일어나게 됩니다. 이 파싱의 결과로 함수 내부에 있는 a가 변수 스포크 객체에 정의가 됩니다.

var a = 20;
function f(){  
    console.log(a); // 20
    var a = 10;
    console.lgo(a); // 10
}
 
f();


'Language > Javascript' 카테고리의 다른 글

promise - 2 [Promise 사용 시 주의할 것]  (0) 2017.07.28
promise - 1 [then, catch, resolve, reject]  (0) 2017.07.28
closure  (0) 2017.07.28
scope - 3 [변수 숨기기]  (0) 2017.07.28
Scope - 2 [정적, 동적, 블록, 중첩 스코프]  (0) 2017.07.21

+ Recent posts