자바스크립트(JavaScript)만의 특성은 무엇일까 ?

셔노·2022년 7월 1일
0
post-thumbnail

❗1. 자바스크립트는 인터프리터(interpreter) 언어입니다.

JavaScript는 프로그램의 소스코드를 한 줄마다 기계어로 번역하는 방식을 취하고 있으며, 이는 한 줄 한줄 번역하므로 테스트에 용이한 구조를 가지고 있지만, 처리 속도가 컴파일 언어 보다 느리다는 단점이 있습니다. (아래 그림 참조)

인터프리터의 예

❗2. 자바스크립트는 프로토타입(prototype) 기반 객체지향 언어이다.

💡프로토타입(prototype)이 뭘까요?

프로토타입 패턴의 특징은 객체를 복제를 통해 생성한다는 점이 있습니다.

클래스 패턴 언어(Java)와 비교해보며 알아봅시다. 예를 들어 게임 캐릭터를 하나 생성한다고 생각하면, 처음 캐릭터가 생성될 때 너무 맨몸이면 유저가 싫어할 것 같으니 기본적인 무기과 갑옷을 가진 상태로 생성하고자 합니다.

// Player.java
class Weapon {}
class Armor {}
class BasicSward extends Weapon {}
class BasicArmor extends Armor {}

class Player {
    public Weapon weapon;
    public Armor armor;

    public Player() {
        this.weapon = new BasicSward(); // 기본 무기
        this.armor = new BasicArmor(); // 기본 갑옷
    }
}

위와 같이 Java(자바)의 경우에는 Player 객체가 자신이 생성될 때 BasicSward 객체와 BasicArmor 객체까지 함께 생성해야 합니다. 이 경우 그냥 Player 객체만 생성하는 상황보다 객체의 생성 비용이 높다고 할 수 있습니다. 그리고 다른 기본 아이템을 추가할수록 Player의 객체의 생성 비용 계속해서 높아집니다.

💡그럼 이 비용을 줄일 수 있는 좋은 방법이 없을까?

만약 캐릭터가 처음 생성될 때 가지고 있는 아이템이 항상 같다는 전제 조건이 있다면 생성 비용이 높은 Player객체를 딱 한번만 생성하고 그 다음부터는 생성된 객체를 복사해서 사용해도 될 것 같다는 생각을 할 수 있습니다.

이런 관점으로 접근하는 것이 바로 프로토타입 패턴이라고 할 수 있습니다. 프로토타입, 즉 원본 객체가 존재하고 그 객체를 복제해서 새로운 객체를 생성하는 방법인 것입니다. (프로토타입의 사전적인 정의는 "본래의 형태", "시제품" 등이고 여기서는 "본래의 형태"의 의미로 사용된 듯합니다.)

자바스크립트의 프로토타입

자바스크립트도 역시 복제를 통해 새로운 객체를 생성합니다. 자바스크립트가 어떻게 활용되는지 알아봅시다.

Code

function User () {}

const evan = new User();

console.log(11, evan);
console.log(22, typeof evan);

console창 결과물

11, User { __proto__: Object }
22, "object"

User함수에 new라는 생성자를 붙여서 객체를 인스턴스화 했습니다. 그런데 evan의 타입을 찍어보면 함수가 아니고 객체이다. evan은 무엇으로부터 복제된 것일까? 사실 evan은 User함수의 prototype 객체를 복제한 것입니다. 자바스크립트의 모든 객체는 prototype 객체를 가지고 있고 그 prototype을 복제하면서 객체를 생성합니다.

code

function User () {}

console.log(11, User.prototype);
console.log(22, typeof User.prototype);

console창 결과

11, { constructor: f User(), __proto__: Object }
22, "object"

처음 User에서 함수를 선언할 때 함수의 프로토 타입 객체도 함께 생성됩니다. 그리고 이 프로토타입 객체는 함수를 사용해서 새로운 객체를 생성할 때 원본 객체 역할을 해줄 객체를 의미합니다.

즉, new User()라는 문법을 사용하여 새로운 객체를 만들게 되면 User 함수 자체가 아니라 User 함수가 생성될 때 함께 생성된 User 함수의 프로토타입 객체를 복제해서 새로운 객체를 만든다는 것입니다.

❗3. 자바스크립트는 동적 타입 언어이다.

정적타입(static/strong type)의 언어와 동적타입(dynamic/weak type)의 언어의 차이점을 알아봅시다.

정적 타입(static/strong type)의 언어

정적 타입의 언어는 변수를 선언할 때 변수에 할당할 수 있는 값의 종류, 즉 데이터 타입을 사전에 선언해야한다 이를 명시적 타입 선언(explicit type declaration)이라 한다

  • 컴파일 시간에 변수의 타입이 결정되는 언어
  • c,java 등이 대표적이다
  • 정적 언어는 변수에 들어갈 값의 형태에 따라 자료형을 지정해주어야한다
  • 컴파일 시에 자료형에 맞지 않는 값이 들어있을 경우 컴파일 에러가 발생
  • 컴파일 시간에 변수의 타입을 체크하고 타입에러로 인한 문제점을 초기에 발견할 수 있어서 안정성이 올라간다

동적 타입(dynamic/weak type)의 언어

동적 타입 언어는 변수에 어떤 데이터 타입의 값이라도 자유롭게 할당할 수 있다.

  • 런타임 시에 값에 의해 변수의 데이터 타입이 결정되는 언어
  • 즉 소스가 빌드될 때 자료형을 결정하는것이 아니라 실행시에 결정이 된다
  • javascript가 대표적이다
  • 매번 타입을 써줄 필요가 없기 때문에 유연하고 편하다
  • 실행도중 예상치 못한 타입이 들어와 사용자가 직접 에러를 확인하는 큰 문제가 발생

🤔 유연성 (flexibility)은 높지만 신뢰성(reliability) 는 떨어게되는 것이다

⚠ ⚠ ⚠ 동적 타입의 언어를 사용할때 주의사항 ⚠ ⚠ ⚠

1) 변수를 억제하는 것이 좋다

  • 재할당 문제로 타입을 잘못 예측하여 오류를 불러오기 때문 확률적으로 변수가 많다면 오류 발생확률도 높기 때문

2) 재할당을 하지 않는 것이 좋다

  • 계속적으로 바뀌게 되면 추적이 어렵기 때문입니다.

3) 상수를 쓰는것이 좋다

  • 재할당하지 않아야하기 때문입니다.

4) 전역 변수를 최대한 사용하지 않는다.

  • 어디서든 참조하고 변경 가능한 변수는 값이 변경될 가능성이 높아서 다른 코드에 영향을 준다

👉 동적 타입의 언어의 특성을 고려하여 4가지 정도 고려하여 코드를 작성한다면 가독성이 올라갈 것 입니다.

❗4. 자바스크립트는 함수가 일급 객체(First Class Object)이다.

여기서 말하는 일급객체가 뭘까요?
특정 언어의 일급 객체란, 컴퓨터 프로그래밍 언어에서 일반적으로 다른 객체들에 적용 가능한 연산을 모두 지원하는 객체를 가리킨다. 즉, JavaScript의 함수는 보통의 다른 언어와 다르게 변수에 담을 수도 있고 다른 함수의 인자로도 사용이 가능하다.

다음과 같은 조건을 만족할 때 일급 객체라고 말할 수 있다.

  • 변수(variable)에 담을 수 있다.
// 변수에 함수 할당
var bar = fucntion() { return 'javscript'; };
console.log(bar()); // javascript
  • 파라미터(parameter)로 전달 할 수 있다.
var test = function(func) {
   func(); // 파라미터로 받은 함수 호출
}

// test() 함수에 다른 함수를 파라미터로 넣어 호출
test(function() {
   console.log('javascript');
});
  • 변환값(return value)으로 사용할 수 있다.
// 함수를 리턴하는 test() 함수
function test() {
   return function() {
       console.log('javscript');
   }
}

var bar = test();
bar();

JavaScript에서는 이런 특성 때문에 숫자와 문자처럼 함수를 변수에 저장거나, 파라미터로 전달하고, 함수의 리턴값으로도 사용할 수가 있는 것이다.

JavaScript의 함수가 일급 객체이기 때문에 할 수 있는 것

  • 콜백 패턴 을 사용할 수 있다.
  • 고차함수(High-order function) 를 만들 수 있다.
  • Javascript의 클로저(closure) 를 사용해커링(currying) 과 메모이제이션(memoization) 이 가능하다.

❗5. 자바스크립트는 함수가 클로저(Closures)를 정의한다.

클로저는 함수와 함수가 선언된 어휘적 환경의 조합이다. 클로저를 이해하려면 자바스크립트가 어떻게 변수의 유효범위를 지정하는지(Lexical scoping)를 먼저 이해해야 한다.

어휘적 범위 지정(Lexical scoping)

다음 코드를 보자.

    function init() {
      var name = "Mozilla"; // name은 init에 의해 생성된 지역 변수이다.
      function displayName() { // displayName() 은 내부 함수이며, 클로저다.
        alert(name); // 부모 함수에서 선언된 변수를 사용한다.
      }
      displayName();
    }
    init();

init()은 지역 변수 name과 함수 displayName()을 생성한다. displayName()은 init() 안에 정의된 내부 함수이며 init() 함수 본문에서만 사용할 수 있다. 여기서 주의할 점은 displayName() 내부엔 자신만의 지역 변수가 없다는 점이다. 그런데 함수 내부에서 외부 함수의 변수에 접근할 수 있기 때문에 displayName() 역시 부모 함수 init()에서 선언된 변수 name에 접근할 수 있다. 만약 displayName()가 자신만의 name변수를 가지고 있었다면, name대신 this.name을 사용했을 것이다.

위 코드를 실행하면 displayName() 함수 내의 alert()문이 부모 함수에서 정의한 변수 name의 값을 성공적으로 출력한다. 이 예시를 통해 함수가 중첩된 상황에서 파서가 어떻게 변수를 처리하는지 알 수 있다. 이는 어휘적 범위 지정(lexical scoping)의 한 예이다. 여기서 "lexical"이란, 어휘적 범위 지정(lexical scoping) 과정에서 변수가 어디에서 사용 가능한지 알기 위해 그 변수가 소스코드 내 어디에서 선언되었는지 고려한다는 것을 의미한다. 단어 "lexical"은 이런 사실을 나타낸다. 중첩된 함수는 외부 범위(scope)에서 선언한 변수에도 접근할 수 있다.

클로저(Closure)

그럼 다음 예제를 봅시다.

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

    var myFunc = makeFunc();
    //myFunc변수에 displayName을 리턴함
    //유효범위의 어휘적 환경을 유지
    myFunc();
    //리턴된 displayName 함수를 실행(name 변수에 접근)

이 코드는 바로 전의 예제와 완전히 동일한 결과가 실행된다. 하지만 흥미로운 차이는 displayName()함수가 실행되기 전에 외부함수인 makeFunc()로부터 리턴되어 myFunc 변수에 저장된다는 것이다.

한 눈에 봐서는 이 코드가 여전히 작동하는 것이 직관적으로 보이지 않을 수 있다. 몇몇 프로그래밍 언어에서, 함수 안의 지역 변수들은 그 함수가 처리되는 동안에만 존재한다. makeFunc() 실행이 끝나면(displayName함수가 리턴되고 나면) name 변수에 더 이상 접근할 수 없게 될 것으로 예상하는 것이 일반적이다.

하지만 위의 예시와 자바스크립트의 경우는 다르다. 그 이유는 자바스크립트는 함수를 리턴하고, 리턴하는 함수가 클로저를 형성하기 때문이다. 클로저는 함수와 함수가 선언된 어휘적 환경의 조합이다. 이 환경은 클로저가 생성된 시점의 유효 범위 내에 있는 모든 지역 변수로 구성된다. 첫 번째 예시의 경우, myFunc은 makeFunc이 실행 될 때 생성된 displayName 함수의 인스턴스에 대한 참조다. displayName의 인스턴스는 변수 name 이 있는 어휘적 환경에 대한 참조를 유지한다. 이런 이유로 myFunc가 호출될 때 변수 name은 사용할 수 있는 상태로 남게 되고 "Mozilla" 가 alert 에 전달된다.

다음은 조금 더 흥미로운 예제인 makeAdder 함수이다:

    function makeAdder(x) {
      var y = 1;
      return function(z) {
        y = 100;
        return x + y + z;
      };
    }

    var add5 = makeAdder(5);
    var add10 = makeAdder(10);
    //클로저에 x와 y의 환경이 저장됨

    console.log(add5(2));  // 107 (x:5 + y:100 + z:2)
    console.log(add10(2)); // 112 (x:10 + y:100 + z:2)
    //함수 실행 시 클로저에 저장된 x, y값에 접근하여 값을 계산

이 예제에서 단일 인자 x를 받아서 새 함수를 반환하는 함수 makeAdder(x)를 정의했다. 반환되는 함수는 단일인자 z를 받아서 x와 y와 z의 합을 반환한다.

본질적으로 makeAdder는 함수를 만들어내는 공장이다. 이는 makeAdder함수가 특정한 값을 인자로 가질 수 있는 함수들을 리턴한다는 것을 의미한다. 위의 예제에서 add5, add10 두 개의 새로운 함수들을 만들기 위해 makeAdder함수 공장을 사용했다. 하나는 매개변수 x에 5를 더하고 다른 하나는 매개변수 x에 10을 더한다.

add5와 add10은 둘 다 클로저이다. 이들은 같은 함수 본문 정의를 공유하지만 서로 다른 맥락(어휘)적 환경을 저장한다. 함수 실행 시 add5의 맥락적 환경에서 클로저 내부의 x는 5 이지만 add10의 맥락적 환경에서 x는 10이다. 또한 리턴되는 함수에서 초기값이 1로 할당된 y에 접근하여 y값을 100으로 변경한 것을 볼 수 있다. (물론 x값도 동일하게 변경 가능하다.) 이는 클로저가 리턴된 후에도 외부함수의 변수들에 접근 가능하다는 것을 보여주는 포인트이며 클로저에 단순히 값 형태로 전달되는것이 아니라는 것을 의미한다.

📢 클로저 함수의 장점 (활용방법)

👍 장점1. 데이터를 보존할 수 있다.

  • 클로저 함수는 외부 함수의 실행이 끝나더라도 외부 함수 내 변수를 사용할 수 있다.
    클로저는 이처럼 특정 데이터를 스코프 안에 가두어 둔 채로 계속 사용할 수 있게하는 폐쇄성을 갖는다.

👍 장점2. 정보의 접근 제한 (캡슐화)

  • ‘클로저 모듈 패턴’을 사용해 객체에 담아 여러 개의 함수를 리턴하도록 만든다.
    이러한 정보의 접근을 제한하는 것을 캡슐화라고 한다.

👍 장점3. 모듈화에 유리하다.

  • 클로저 함수를 각각의 변수에 할당하면 각자 독립적으로 값을 사용하고 보존할 수 있다.
    이와 같이 함수의 재사용성을 극대화 함수 하나를 독립적인 부품의 형태로 분리하는 것을 모듈화라고한다.
    클로저를 통해 데이터와 메소드를 묶어다닐 수 있기에 클로저는 모듈화에 유리하다.

❓예시코드

  • JavaScript 함수 캡슐화 설정
    function makeSizer(size) {
      return function() {
        document.body.style.fontSize = size + 'px';
      };
    }

    var size12 = makeSizer(12);
    var size14 = makeSizer(14);
    var size16 = makeSizer(16);
  • JavaScript 캡슐화된 함수와 연결
    document.getElementById('size-12').onclick = size12;
    document.getElementById('size-14').onclick = size14;
    document.getElementById('size-16').onclick = size16;
  • HTML
    <p>Some paragraph text</p>
    <h1>some heading 1 text</h1>
    <h2>some heading 2 text</h2>

    <a href="#" id="size-12">12</a>
    <a href="#" id="size-14">14</a>
    <a href="#" id="size-16">16</a>
  • CSS
body {
  font-family: Helvetica, Arial, sans-serif;
  font-size: 12px;
}

h1 {
  font-size: 1.5em;
}
h2 {
  font-size: 1.2em;
}
profile
초보개발자

0개의 댓글