[Nov. 11, 2020] Feature of JavaScript(자바스크립트의 특징)

Alpaca·2020년 11월 11일
0

Feature of JavaScript(자바스크립트의 특징)

JavaScript(이하 'js')는 동적 인터프리터 프로그래밍 언어이기 때문에 다른 전통적인 객체지향 프로그래밍 언어들과 구문이 다르다.
이처럼 이번 글에서는 js만의 독특한 특징들을 알아보도록 하자.

Scope(유효범위)

지난번 글에서 유효범위에 대해서 자세히 알아봤다.
자세한 내용을 알고 싶은 사람은 링크를 참고하도록 하자.

이 글에서는 간략하게 유효범위에 대해 설명하면 유효범위는 js 변수에 대한 접근 권한을 정의하는 것이라고 할 수 있다.
js 변수는 전역 범위 또는 지역 범위에 속할 수 있다.

Global Declaration : Global Scope(전역 선언 : 전역 범위)

js는 연산자 없이 변수를 선언할 수 있다. 예를들어,

test = "sss";
console.log(test); // "sss"를 출력한다.

위의 코드는 전역 변수(global variable)를 생성한다.
허나 위와 같은 변수는 굉장히 좋지 않은 방법이며, 항상 var 혹은 let을 이용해 선언하도록 하자.
마지막으로 수정하지 않을 변수를 선언할 때에는 const를 사용하도록 하자.

var keyword를 사용하여 선언하기 : 함수 범위

js에서 var는 변수를 선언하는 keyword로써 변수를 어디에서 선언하든 변수 선언이 함수의 맨 앞으로 이동한다. 이를 변수 호이스팅(variable hoisting)이라고 한다.
스크립트 실행 시 변수가 스크립트의 가장 마지막에 선언됐다고 하더라도 해당 코드가 가장 마지막에 실행되는 것은 아니다.

function scope1(){
    var top = "top";
    bottom = "bottom";
    console.log(bottom);

    var bottom;
}
scope1(); // "bottom"을 출력한다.

위의 코드는 어떤식으로 동작하는 것일까?

function scope1(){
  var top = "top";
  var bottom;
  bottom = "bottom";
  console.log(bottom);

}
scope1(); // "bottom"을 출력한다.

위와 같이 동작한다.
var keyword에서 주목해야 할 핵심적인 사항은 해당 변수의 범위가 가장 가까운 함수 범위라는 것이다.

function scope2(print) {
    if (print) {
        var insideIf = '12';
    }
    console.log(insideIf);
}
scope2(true); // '12'를 출력한다.

위의 함수는 아래의 함수와 동일하게 작동한다.

function scope2(print) {
  var insideIf;
  if (print) {
    insideIf = '12';
  }
  console.log(insideIf);
}
scope2(true); // '12'를 출력한다.

이는 js는 기본적으로 Function Level Scope이기 때문이다.
이 말의 뜻은 if문의 블록({...})내부에서 선언되었다 하더라도 접근 가능한 범위(scope)는 Function내부(body)이기 때문이다.

var a = 1;

function four() {
    if (true) {
        var a = 4;
    }
    console.log(a); // '4'를 출력한다.
}
four();

또한 위의 예시처럼 four();를 통해 출력되는 것은 a = 1이 아닌 a = 4이다.
이는 Execution Context(실행 컨텍스트)의 개념을 알아야 정확한 이해가 가능하지만, 지금은 function four()에서 변수 a가 다시 재선언되었기 때문이라고 이해하면 될 것 같다.

let keyword를 사용하여 선언하기 : 블록 범위

앞서 예를 들은 if문에서 if문의 블록범위({...}) 내에서만 변수를 사용하고 싶을 때에는 let keyword를 사용하면 된다.
let keyword로 선언된 변수는 가장 가까운 블록범위({...})를 갖는다.

function scope3(print) {
    if (print) {
        let insideIf = '12';
    }
    console.log(insideIf);
}
scope3(true); // Uncaught ReferenceError: "insideIf" is not defined

위와 같이 varlet으로 치환했을 뿐인데 ReferenceError가 발생했다.
이유는 varFunction Level Scope이지만 letBlock Level Scope이기 때문이다.

var a = 1;
console.log(a); // "1"이 출력된다.
a = 2;
console.log(a); // 변수 a가 재할당되어 "2"가 출력된다.

let b = 3;
console.log(b); // "3"이 출력된다.
b = 4;
console.log(b); // 변수 b가 재할당되어 "4"가 출력된다.

const c = 5;
console.log(c); // "5"이 출력된다.
c = 6;
console.log(c); // Uncaught TypeError: Assignment to constant variable.

varlet keyword와는 다르게 const는 재할당할 수 없다.
따라서 c = 5의 값은 변하지 않게 되고 변수의 재할당을 시도해 TypeError가 발생한다.

등가와 형

js에는 전통적인 언어와는 다른 자료형이 있다. 이러한 점이 어떤 영향을 끼치는지 알아보자.

변수형

js에는 boolean, number, string, undefined, function, symbol, object과 같이 일곱개의 기본 자료형이 있다.
undefined란 선언만 되고 아직 값이 할당되지 않은 변수에 할당하는 변수를 말한다. 일곱개의 기본 자료형들의 대한 변수의 형은

var is20 = false; // boolean
typeof is20; // boolean

var age = 19;
typeof age; // number

var lastName = "paca";
typeof lastName; // string

var fruits = ["Apple", "Banana", "Kiwi"];
typeof fruits; // object

var me = {firstName:"Al", lastName:"paca"};
typeof me; // object

var nullVar = null;
typeof nullVar; // object

var function1 = function(){
	console.log(1);
}
typeof function1 // function

var blank;
typeof blank; // undefined

참/거짓 확인

if문내에서 참/거짓 확인이 사용된다. 많은 언어들의 경우 if(parameter){...} 함수 내의 parameterboolean이어야 하지만 js(혹은 다른 동적으로 형이 결정되는 언어들)는 이 점에 있어 좀 더 유연하다.

if (node) {

}

여기서 node변수다. 해당 변수가 비었거나 null이거나 undefined이면 해당 변수는 false로 평가된다.
일반적으로 true로 평가되는 경우는

  • true
  • 0이 아닌 다른 숫자
  • 비어있지 않은 문자열
  • 비어있지 않은 객체

반대로 false로 평가되는 경우는

  • false
  • 0
  • 빈 문자열("" || '')
  • NaN(Not a Number)
  • undefined
  • null
 var printIfTrue = "";
 if (printIfTrue) {
     console.log("truthy");
 } else {
     console.log("falsey"); // 'falsey'가 출력된다.
 }

=== 와 ==

js는 스크립트 언어이고 변수 선언 시 변수에 형이 할당되지 않는다. 대신에 코드가 실행될 때 해당 변수의 형이 해석된다.
==만을 확인하는 반면, === 모두 확인한다.

"5" == 5 // "true"가 반환된다.
"5" === 5 // "false"가 반환된다.

객체

두 객체가 동일한지 확인하고자 할때 간단히 ==를 사용하면 좋겠지만 js는 이를 ture로 평가하지 않는다.

var o1 = {};
var o2 = {};

o1 == o2 // "false"가 반환된다.
o1 === o2 // "false"가 반환된다.

위의 객체는 동일함(동일한 속성과 값을 가짐)에도 두 객체는 동일하지 않다.
왜냐하면 js에서 두 변수의 메모리상 주소가 다르기 때문이다.
때문에 js application이 lodashunderscore와 같은 유틸리티 라이브러리를 사용하는 이유이다.

우리는 pure js로 객체가 같은지 정확하게 비교해 보자.

function isEquivalent(a, b) {
    // 배열의 속성이름
    var aProps = Object.getOwnPropertyNames(a);
    var bProps = Object.getOwnPropertyNames(b);

    // 속성의 길이가 다르다면 두 객체는 다른 객체이다.
    if (aProps.length != bProps.length) {
        return false;
    }

    for (var i = 0; i < aProps.length; i++) {
        var propName = aProps[i];

        // 속성의 값이 다르다면 두 객체는 다른 객체이다.
        if (a[propName] !== b[propName]) {
            return false;
        }
    }

    위에서의 모든 것들이 일치했다면 두 객체는 같은 객체이다.
    return true;
}
isEquivalent({'hi':12},{'hi':12}); // "true"가 반환된다.

허나 만약 객체 안에 또 다른 객체나 함수가 있다면 위의 함수로는 잘 동작하지 않는다.

var obj1 = {'prop1': 'test','prop2': function (){} };
var obj2 = {'prop1': 'test','prop2': function (){} };

isEquivalent(obj1,obj2); // "false"를 반환된다.

왜냐하면 js에서 함수는 객체에 적용되는 룰을 따르기 때문이다.
그렇기 때문에 완전히 같은 함수 function (){}이라도 메모리상 주소가 다르기 때문에 false를 반환된다.
이를 해결하려면 한 변수에 함수 function (){}을 할당하는 것인데

var func = function () {};
var obj1 = {'prop1': 'test','prop2': func };
var obj2 = {'prop1': 'test','prop2': func };

isEquivalent(obj1,obj2); // "true"를 반환된다.

위와 같이 하여 ture를 반환받을 수 있다.

var function1 = function(){console.log(2)};
var function2 = function(){console.log(2)};
console.log(function1 == function2); // "false"를 출력된다.

위에서 언급했듯 함수는 객체에 적용되는 룰을 따른다.
따라서 두 함수는 동일한 연산을 수행하지만 두 함수의 메모리상 주소는 다르다. 따라서 false가 출력된다.
기본 등가 확인 연산자인 =====문자열숫자같은 비객체형에만 사용 할 수 있다.
객체에 대한 등가 확인을 구현하려면 객체의 각 속성을 확인해야 한다.

Summary

js에는 대부분의 프로그래밍 언어들이 사용하지 않는 다른 방식의 변수 선언 방식이 존재한다.
var는 함수 범위 내에서 변수를 선언하고, let은 블록 범위에서 변수를 선언하고, 아무 연산자 없이 선언하는 경우 전역 변수가 된다.
=====문자열, 숫자, 불리언과 같은 비객체형에만 사용할 수 있다.

Reference

profile
2020년 10월 15일 퇴사하고 개발자의 길에 도전합니다.

0개의 댓글