https://www.freecodecamp.org/news/understanding-the-core-of-nodejs-the-powerful-chrome-v8-engine-79e7eb8af964/

 

Understanding How the Chrome V8 Engine Translates JavaScript into Machine Code

by Mayank Tripathi Understanding How the Chrome V8 Engine Translates JavaScript into Machine Code Before diving deep into the core of Chrome’s V8, first, let’s get our fundamentals down. All of our systems consist of microprocessors, the thing that is sitt

www.freecodecamp.org

 

드디어 자바스크립트 엔진이다...

js엔진인 Chrome의 V8엔진을 설명하기 전에 엔진의 기본에 대해서 먼저 설명한다.

우리 시스템은 마이크로프로세스들로 구성되어있다. 이건 전기 신호를 받아 일을 수행하는 아주 작은 기계이다. 우리는 이 기계에 코드로 일을 수행하라는 명령을 보낸다. 

이 마이크로프로세스들은 언어로된 명령을 알아들을 수 있는데 마이크로프로세스 별로 해석하는 언어가 다르다. IA-32, x86-64, MIPS, ARM같은 것들이 있단다.

마이크로프로세스들과 가까운 명령어(언어로 작성된 코드)에 가까울 수록 low-level언어라 하고 우리가 작성하는 코드에 가까울 수록 high-level이라 한다. 설명이 좀 이상한데 언어가 더 추상화되어 있으면 high-level에 가깝다고 한다.

 

- V8 engine

V8은 구글이 만들고 배포한 js엔진 오픈소스이다. 갓 구글

근데 이게 진짜 엔진일까 ?? 사실 이건 js코드를 lower-level 또는 마이크로프로세스들이 이해할 수 있는 기계어에 가깝게 변환시켜 주는 역할을 한다.

js엔진도 여러가지가 있는데 Rhino, JavaScriptCore, SpiderMonkey 등이 있다. 이 엔진들은 ECMAScript라는 표준을 따르는데, 또 ECAMAScript는 스크립팅 언어의 표준을 따른다. JS 또한 이카마스크립트 베이스이다.

기본적으로 V8은 C++로 만들어졌고 브라우저 크롬과 node.js에서 사용된다. 그래서 C++ 프로그램에 JS를 끼워 넣을 수 있다. 물론 우리가 컴파일 하면 에러가 뜨지만 할 수는 있단다. ( 여기 내용을 몰라서 모르겠다 )

어찌 됬든 V8엔진이 만든 C++과의 합작이 JS를 하드웨어에 접근 할 수 있게 만들었고 이게 보통 JS엔진과 큰 차이를 보인다는 내용이 있었다. 뭐 대충 Node.js의 탄생같은거 말한거 같다.

 

 

 

 

 

https://javascript.info/settimeout-setinterval

 

Scheduling: setTimeout and setInterval

 

javascript.info

 

setTimeout과 setInterval 메소드는 자바스크립트의 메소드라고 보기는 힘들다. 이건 웹 브라우저와 node.js에서 지원하는 메소드라고 보는게 편하다. ( 그렇다고한다 )

 

- setTimeout()

설정한 시간이 지나고 함수를 실행시키는 함수이다.

문법은 이렇게 생겼다. 1000의 숫자가 1초이다.

function sayHi() {
  alert('Hello');
}

setTimeout(sayHi, 1000); // Hello


function sayHi2(phrase, who) {
  alert( phrase + ', ' + who );
}

setTimeout(sayHi2, 1000, "Hello", "John"); // Hello, John

 

setTimeout을 취소 시키는 방법이 있다. 

let timerId = setTimeout(() => alert("never happens"), 1000);
alert(timerId); // timer identifier

clearTimeout(timerId);
alert(timerId); // same identifier (doesn't become null after canceling)

timerId 변수를 만들고 clearTimeout() 메소드를 사용하면 setTimeout을 취소할 수 있다.

 

- setInterval()

setInterval은 setTimeout과 문법이 유사하다. 이렇게 생겼다.

// repeat with the interval of 2 seconds
let timerId = setInterval(() => alert('tick'), 2000);

// after 5 seconds stop
setTimeout(() => { clearInterval(timerId); alert('stop'); }, 5000);

setInterval 메소드의 함수는 설정한 시간이 지나면 한번 실행되는게 아니라 설정한 시간 마다 실행된다.

 

Zero delay setTimeout

setTimeout( func, 0 )과 setTimeout( func )은 특별한 상황에서 사용한다. 0은 시간의 디폴트 값이다. 

사실 0의 시간이 진짜 zero 시간이 아니다. 최소 4 milliseconds의 시간이 걸린다.

 

특정한 상황이란 상 '바로 다음'에 실행하는 상황이다. 

setTimeout(() => alert("World"));

alert("Hello");

예를 들어 위의 코드의 경우 hello가 알랏 되자마자 바로 world가 알랏된다. 

 

- requestAnimationFrame()

http://www.javascriptkit.com/javatutors/requestanimationframe.shtml

 

Understanding JavaScript's requestAnimationFrame() method for smooth animations

Understanding JavaScript's requestAnimationFrame() method for smooth animations Updated: Nov 1st, 2017 The modern web of today is filled with sights to behold on every page, where menus slide in and out, content gently fade into view, and elements animate

www.javascriptkit.com

 

 

https://www.zeolearn.com/magazine/understanding-the-javascript-event-loop

 

Understanding the JavaScript event loop

Event Loop is one of the powerful aspects of JavaScript. This article clarifies and highlights how JavaScript works and what makes it weird when compared to languages that you might have previously used.

www.zeolearn.com

C++, Ruby등의 언어와는 다르게 자바스크립트는 싱글스레드 언어이다. 싱글스레드는 한번에 한가지 일을 하고 하나의 일을 할 때 다른 일을 하지 못 한다. 이게 또 물론 멀티스레드 데드락 같은 현상이 이러나지 않지만 복잡한 일을 하지 못하는 단점을 가진다. 

 V8엔진이라고 구글이 만든 자바스크립트 엔진이 있다. 브라우저에서 동작한다. JS엔진은 2가지 메인 컴포넌트로 구성 되어있는데 그게 Heap과 Call Stack이다.

Heap은 모든 메모리가 할당되는 장소이다. 

Call Stack은 기본적으로 데이터 구조이고 우리가 프로그래밍을 하는 장소를 기록한다. 그러니까 만약 프로그램에 실행 컨텍스트(ex/ function call)가 있다면 콜 스텍이 실행 컨텍스트를 스택에 넣고 리턴을 받을 때 다시 꺼내는 역할을 한다. 

콜스텍 내부에서 어떻게 동작하는지 보자

function multiply(a, b) {
   return a*b;
}

function square(a) {
   const sq = multiply(a, a);
   
   console.log(sq);
}

square(3); // 9

JS엔진은 힙안에 함수할당에 대한 메모리를 만든다.  읽어내리다 square(3)코드에 도달하면 js엔진은 이걸 콜 스텍에 집어 넣는다.

먼저 함수 multiply, square를 힙에 올리고 square함수가 콜스텍에 들어간다. square함수 내부의 변수 sq가 그 다음으로 콜 스텍에 들어가고 sq변수가 참조하고 있는 multiply함수가 실행된다. multiply가 실행되고 sq변수가 콜 스텍에서 빠지고 콘솔로그sq가 다음으로 콜 스텍에 들어간다. 콘솔로그 찍고 콜스텍에서 빠지고 마지막으로 모든걸 다 실행한 함수 square가 콜 스텍에 빠지고 코드가 끝난다. 콜 스텍은 FILO( First Input Last Out )이다.

각 콜스텍의 입구는 Stack Frame이라 부르고 우리는 콘솔(크롬 F12눌러 나오는 창에서 console부분)에서 이걸 확인할 수 있다.

function customError() {
   throw new Error("Print the stack trace from here!!");
}
function foo() {
   customError();
}
function bar() {
   foo();
}
bar();

위의 코드를 실행하면 콘솔에

Uncaught Error: Print the stack trace from here !!
	at customError (main.js:2)
    at foo (main.js:6)
    at bar (main.js:10)
    at main.js:13

위의 에러가 뜬다. 여기에 콜 스텍 순서(Stack Trace)를 볼 수 있다.

 

- Asynchronous 

setTimeout은 비동기 API이다. 보통 동기적으로 코드가 작동되면 콜스텍에 들어간 코드가 끝나야 다음 코드라인으로 넘어가는데 비동기는 이러한 코드들을 비 동기적으로 실행하게 한다.

console.log("a");

setTimeout(() => {
	console.log("b");
}, 500);

console.log("c");

위의 코드를 실행하면 a,b,c 순서대로 로그가 찍히지 않는다.

먼저 a 로그가 콜 스텍에 들어갔다 나오며 로그를 찍고 다음으로 b로그를 찍기 위해 setTimeout이 콜 스텍에 들어가지만 실행하지 않고 시간이 될때 까지 대기 상태가 된다. 이때 안 기다리고 바로 c가 콜스텍에 들어가 로그를 찍고 빠지고 시간이 다 되면 b가 다시 콜 스텍에 들어가 b로그를 찍는다.

대기 상태의 코드들은 queue에 들어가게 된다. 이게 비동기방식으로 작동하는 코드이다.

 

그런데 자바스크립트는 싱글스레드인데 어떻게 동시적으로 프로세스들이 실행될 수 있는지 궁금할거다. 브라우저는 Wep APIs, Callback Queue, Event Loop같은 것들로 구성 되어있는데 이게 JS런타임 보다 많은 일을 하게한다.

Wep APIs는 우리가 요청하는 스레드들이고 그 스레드들을 콜 스텍이 비어있을 때 프로세스들을 실행하게 해준다. 

V8 브라우저 엔진 구성요소

위에 처럼 생겼다.

 

아까 그 비동기 코드들을 브라우저 구성요소와 함께 보면 이렇다.

비동기 코드 실행

setTimeout같은 API들은 js런타임에 존재하는게 아니라 Web API에 의해 실행된다. 이게 callback과 함께 등록되며 타이머가 만료되면 event queue에 들어가고 이벤트 루프가 콜스텍이 비었을 때 이걸 콜스텍으로 보낸다. 대충 위의 구조로 작동된다.

Event Loop는 한가지 일을 하는데 그건 Call Stack과 Callback Queue를 지켜보는 거다. 콜 스텍이 비어있을 때 이벤트 루프는 큐에 있는 첫 번째 이벤트를 갖고 스텍으로 보내준다. 이게 다다.

 

 

 

https://www.freecodecamp.org/news/javascript-modules-a-beginner-s-guide-783f7d7a5fcc/

 

JavaScript Modules: A Beginner’s Guide

by Preethi Kasireddy JavaScript Modules: A Beginner’s Guide Source: https://www.flickr.com/photos/qubodup/16258492451If you’re a newcomer to JavaScript, jargon like “module bundlers vs. module loaders,” “Webpack vs. Browserify” and “AMD vs. CommonJS” can q

www.freecodecamp.org

 

 

작곡가는 곡의 파트를 나누고, 작가는 글의 챕터를 나눈다. 프로그래머는 프로그램의 코드를 모듈로 나눈다. 모듈( Module )이 뭔가 ?

간단히 생각해서 코드같은 언어의 뭉치? 같은 개념이라고 생각 할 수 있다.

물론 좋은 모듈은 특정 목적을 위해 기능적으로 구분된 뭉치라고 볼 수 있다.

이같은 모듈을 프로그램에 통합 시키는 방법이 여러가지 있다.

 

- Anonymous closure

(function () {
  // We keep these variables private inside this closure scope
  
  var myGrades = [93, 95, 88, 0, 55, 91];
  
  var average = function() {
    var total = myGrades.reduce(function(accumulator, item) {
      return accumulator + item}, 0);
    
      return 'Your average grade is ' + total / myGrades.length + '.';
  }

  var failing = function(){
    var failingGrades = myGrades.filter(function(item) {
      return item < 70;});
      
    return 'You failed ' + failingGrades.length + ' times.';
  }

  console.log(failing());

}());       // ‘You failed 2 times.’

위의 예제에서는 average, failing 변수에 익명의 클로저 함수를 할당하고 전체를 IIFE( Immediately-Invoked Function Expressions )로 모듈을 만들어 사용하였다.

이러한 방식의 장점은 함수 내부의 로컬 변수에 접근을 하면서 동시에 글로벌 변수들의 namespace를 해치지 않는다는 점이다.

 

- Global import

(function ( globalVariable ) {

  // Keep this variables private inside this closure scope
  var privateFunction = function() {
    console.log('Shhhh, this is private!');
  }

  // Expose the below methods via the globalVariable interface while
  // hiding the implementation of the method within the 
  // function() block

  globalVariable.each = function(collection, iterator) {
    if (Array.isArray(collection)) {
      for (var i = 0; i < collection.length; i++) {
        iterator(collection[i], i, collection);
      }
    } else {
      for (var key in collection) {
        iterator(collection[key], key, collection);
      }
    }
  };

  globalVariable.filter = function(collection, test) {
    var filtered = [];
    globalVariable.each(collection, function(item) {
      if (test(item)) {
        filtered.push(item);
      }
    });
    return filtered;
  };

  globalVariable.map = function(collection, iterator) {
    var mapped = [];
    globalUtils.each(collection, function(value, key, collection) {
      mapped.push(iterator(value));
    });
    return mapped;
  };

  globalVariable.reduce = function(collection, iterator, accumulator) {
    var startingValueMissing = accumulator === undefined;

    globalVariable.each(collection, function(item) {
      if(startingValueMissing) {
        accumulator = item;
        startingValueMissing = false;
      } else {
        accumulator = iterator(accumulator, item);
      }
    });

    return accumulator;

  };

 }( globalVariable ));

위의 글로벌 임폴트 예제가 좀 길긴 한데 이러한 방식은 클로저를 만드는 방법과 비슷하다.  받은 파라미터가 글로벌 변수인 점을 빼고.

이렇게 하는 이유는 그냥 코드를 읽기 쉽게 하려고 한단다.

 

- Object interface

var myGradesCalculate = (function () {
    
  // Keep this variable private inside this closure scope
  var myGrades = [93, 95, 88, 0, 55, 91];

  // Expose these functions via an interface while hiding
  // the implementation of the module within the function() block

  return {
    average: function() {
      var total = myGrades.reduce(function(accumulator, item) {
        return accumulator + item;
        }, 0);
        
      return'Your average grade is ' + total / myGrades.length + '.';
    },

    failing: function() {
      var failingGrades = myGrades.filter(function(item) {
          return item < 70;
        });

      return 'You failed ' + failingGrades.length + ' times.';
    }
  }
})();

myGradesCalculate.failing(); // 'You failed 2 times.' 
myGradesCalculate.average(); // 'Your average grade is 70.33333333333333.'

 self-contained object interface(이게 한국말로 뭔지 모르겠다)를 사용해서 모듈을 만든 예제이다.

이러한 방식의 모듈의 장점은 어떤 변수나 메소드를 private할지 혹은 expose할지 결정할 수 있다는 점이다.

함수안에 return 안에 있는 것들이 노출을 시키려고 의도를 하였고, 나머지 부분(myGrades)를 프라이빗으로 감추었다.

 

 

 

https://javascriptweblog.wordpress.com/2010/07/06/function-declarations-vs-function-expressions/

 

Function Declarations vs. Function Expressions

Lets start with a short quiz. What is alerted in each case?: Question 1: Question 2: Question 3: Question 4: If you didn’t answer 8, 3, 3 and [Type Error: bar is not a function] respectively,…

javascriptweblog.wordpress.com

 

함수선언( Function Declaration )은 함수변수를 다른 변수에 할당 없이 정의하는 것이다. 

때문에 다른 함수, 변수 블럭에 엮이지 않는다. 

그리고 다른 변수 선언처럼 var ## = 형식으로 시작되는게 아니라 function ##() 으로 시작한다.

함수를 호출 하는 것은 그 함수의 스코프와 부모 스코프에서 가능하다. 

function bar() {
    return "it's me";
}
 
bar() // it's me
bar  // function

 

 

함수표현( Function Expression )은 더 넓은 범위의 문법으로 정의된 함수이다.

( A Function Expression defines a function as a part of a larger expression syntax )

함수 표현으로 선언된 함수는 이름을 붙일수도 안 붙일 수도 있다.

그리고 function ##() 으로 시작하지 않고 이 함수가 표함된 스코프 밖에서는 호출 불가능하다.

// 이름 없는 함수 표현
var a = function() {
    return 3;
}
 
// 이름 있는 함수 표현
var a = function bar() {
    return 3;
}
 
// 스스로 호출하는 함수 표현, IIFE
// ()로 함수를 감싸 선언이 아닌 표현식으로 표현됨
(function sayHello() {
    alert("hello!");
})();

 

두 방식의 차이점은 일단 선언형의 경우 함수가 코드 상단으로 끌어올림( hoisted ) 된다.

표현형은 끌러올림되지 않는다. 때문에 표현된 함수가 존재하는 스코프의 변수들에 접근할 수 있다.

사실 자바스크립트에 선언된 변수들은 호이스팅 된다. 하지만 거기에 표현된 함수는 그렇지 않다.

 

아직은 잘 모르겠지만 함수 표현이 함수 선언보다 함수형 프로그래밍에 더 어울린다고 한다.

 

https://css-tricks.com/javascript-scope-closures/

 

JavaScript Scope and Closures | CSS-Tricks

Scopes and closures are important in JavaScript. But, they were confusing for me when I first started. Here's an explanation of scopes and closures to

css-tricks.com

 

Scope

 

scope는 간단히 말하면 접근 할 수 있는 변수들의 공간 이라고 생각하면 편하다. 두 가지 종류로 나뉠 수 있다. global scope와 local scope가 있다.

 

- Global Scope

 

변수가 함수나 중괄호 밖에 선언되었다면 그 변수는 글로벌 스코프에 선언된 것이다. 그리고 어느곳에서나 변수를 사용할 수 있다.( Node.js에서는 좀 다르게 선언된다고 한다 )

 

글로벌 변수로 선언하는 것을 그다지 추천하지 않는다. 코드를 작성 하다보면 글로벌 변수에 의해 다른 변수들 선언할 때 혹은 사용할 때 혼돈을 줄 수 있기 때문이다.

 

- Local Scope

 

특정한 범위의 코드 안에서 선언되는 변수들을 로컬 스코프 변수라고 한다. 여기에 또 2가지로 나뉘어지는데

하는 function scope 다른 하나는 block scope라고 한다.

 

function scope

함수 안에 선언된 변수들을 함수 스코프라고 한다. 함수 안에서만 이 변수에 접근 할 수 있다.

 

block scope

블럭 스코프는 중괄호 안에 선언된 변수들을 말한다. 중괄호 밖에서는 이 변수들에 접근 할 수 없다. 블럭 스코프는 함수 스코프의 부분 집합이라 할 수 있다.

 

 

 

 

https://codeburst.io/javascript-double-equals-vs-triple-equals-61d4ce5a121a

 

JavaScript — Double Equals vs. Triple Equals

Learn equality in JavaScript in 3 minutes

codeburst.io

 

자바스크립트는 2가지 방법의 균등 테스트(testing equality)가 있다. ==와 ===이다.

 

===는 엄격하게 검사할 때 사용하고 이거는 type과 value를 같이 검사한다. 

5 === 5
// true (Both Numbers, equal values)
'hello world' === 'hello world'
// true (Both Strings, equal values)
true === true
// true (Both Booleans, equal values)

위와 같은 경우가 ===로 검사 했을 때 true가 나오는 상황이다.

 

아래와 같은 경우가 false가 나오는 상황이다.

77 === '77'
// false (Number v. String)
'cat' === 'dog'
// false (Both are Strings, but have different values)
false === 0
// false (Different type and different value)

 

js에서 == 는 좀 느슨하게 검사할 때 사용한다. 또한 이게 타입 강제를 수행한다.

77 === '77'
// false (Number v. String)

77 == '77'
// true

 

===는 ==의 상위 동등함 체크라고 생각하면 된다.

 

https://dev.to/promhize/what-you-need-to-know-about-javascripts-implicit-coercion-e23

 

What you need to know about Javascript's Implicit Coercion

Common Javascript implicit coercion gotchas, how it works, what should be avoided and why

dev.to

 

자바스크립트는 암묵적 강제(implicit coercion)라는게 있다. 예를들어 number타입이 예상되는 곳에 String타입을 집어 넣는 경우 String을 number타입으로 바꾸려는 시도를 한다. 

 

그래서 아래와 같은 경우가 일어난다.

3 * "3" //9
1 + "2" + 1 //121

true + true //2
10 - true //9


const foo = {
  valueOf: () => 2
}
3 + foo // 5
4 * foo // 8

const bar = {
  toString: () => " promise is a boy :)"
}
1 + bar // "1 promise is a boy :)"


4 * [] // 0
4 * [2] // 8
4 + [2] // "42"
4 + [1, 2] // "41,2"
4 * [1, 2] // NaN

"string" ? 4 : 1 // 4
undefined ? 4 : 1 // 1

 

숫자 표현에 숫자가 아닌 값을 연산 시켰을 때

 

String의 경우 

3 * "3" // 3 * 3
3 * Number("3") // 3 * 3

String의 값이 오직 숫자형태만 존재 할 경우에 Number()메소드를 불러 string을 number로 변환시켜 연산한다.

 

그런데 + 기호는 다른 연산과 좀 다르다.  + 는 수학적으로 더하는 기능과 문자를 합치는 기능을 한다.

string이 + 기호와 함께 숫자와 연산된다면 자바스크립트는 string을 number로 변환하지 않고 오히려 number를 string으로 변환시켜 두개의 단어를 합쳐버린다.

//concatenation all through
1 + "2" + 1 // "121"
(1 + "2") + 1 // "121"

 

Objects의 경우 

 

대부분의 자바스크립트 객체 변환은 [object Object] 라는 결과값을 불러온다. 

모든 자바스크립트는 toString()이라는 메소드를 갖고 있는데 이 메소드는 객체가 String값으로 변환될 때 사용된다. 

const boo = {}
"sadcode" + boo // "sadecode[object Object]"


const foo = {
	toString: () => " hi"
   }
"sadcode" + foo // "sadcode hi"

 

Array objects의 경우

 

배열객체의 경우 toString 메소드가 좀 다르게 작동한다. 이건 약간 join 메소드가 작동하는 방식과 유사하게 작동한다.

[1,2,3].toString() // "1,2,3"
[1,2,3].join() // "1,2,3"
[].toString() // ""
[].join() // ""

"me" + [1,2,3] // "me1,2,3"
4 + [1,2,3] // "41,2,3"
4 * [1,2,3] // NaN

 

True, False , "" 의 경우

Number(true) // 1
Number(false) // 0
Number("") // 0

4 + true // 5
3 * false // 0
3 * "" // 0
3 + "" // "3"

 

 

객체에 valueOf 메소드를 정의하는게 가능하다. 그리고 이게 다른 타입과 연산이 될 때 불러와진다.

그럼 toString과 valueOf 메소드가 둘 다 정의되었을 때의 경우에는 뭐가 실행되는가 ?

 

const bar = {
  toString: () => 2,
  valueOf: () => 5
}

"sa" + bar // "sa5"
3 * bar // 15
2 + bar // 7

valueOf 메소드가 실행된다. 이 메소드는 숫자 값이 필요한 경우에 나타나도록 설계되있기 때문이다.

 

 

--------------------------------------------\

 

자바스크립트에는 모든 값들이 참과 거짓으로 표현될 수 있다. 

 

false 값으로 표현되는 값들은 아래와 같다.

 

/ false / 0 / null / undefined / "" / NaN / -0 

 

나머지는 다 true로 표현된다. 

 

근데 NaN 은 좀 특별한 숫자 값인데 자신은 자기와 같지 않다.

예를 들면 이러하다.

NaN === NaN // false

const notANumber = 3 * "a" // NaN

notANumber == notANumber // false
notANumber === notANumber // false

if (notANumber !== notANumber) // true

 

 

 

https://codeburst.io/explaining-value-vs-reference-in-javascript-647a975e12a0

 

Explaining Value vs. Reference in Javascript

A simple look at computer memory explains what’s happening

codeburst.io

javascript에서 Number, boolean, strings, null, undefined는 primitive 타입이며 값(value)에 의해 전달된다.

반면에 objects, arrays, functions은 객체로 볼 수 있고 참조(reference)에 의해 전달된다.

 

여기서 다른 언어와는 다르게 자바스크립트의 String은 char 타입의 배열이 아니다. 심지어 자바스크립트에는 char타입이 존재하지 않는다. String은 불변의 데이터 타입이다.

stirng의 변경은 데이터가 수정되는게 아니라 새로운 string을 만드는 것이다.

 

------

만약 primitive 타입이 변수에 할당된다면 그 변수가 primitive 타입 데이터를 갖고 있는거다. 

이게 뭔 소리나면 

var x = 10;
var y = x;

console.log(x, y);

x는 10을 갖고있고 y는 그 10을 복사해서 할당 받은거다. 즉 x의 값을 바꾼다고 해도 y는 바뀌지 않는다. 값을 전달했기 때문이다.

 

반면에 객체는 우리 컴퓨터 메모리에 공간을 만든다. 그리고 객체는 그 공간의 주소값을 받는다.

var arr = [];
arr.push(1);

예를들어 배열 arr을 만들면 메모리에 arr의 주소를 가진 메모리 공간이 생성된다.

push를 통해 값 1을 넣으면 arr에 1을 넣는게 아니라 arr이가 갖고 있는 주소를 참조하여 메모리에 가서 1을 넣는다.

 

이게 primitive 타입 데이터 복사와 다르게 작동하는데

var ref = [1];
var refcopy = ref;

ref.push(2);

console.log(ref, refcopy);  // [1,2] , [1,2]

배열 1을 갖고 있는 ref를 refcopy에 복사하였다. 그리고 ref에 2를 넣고 둘다 출력해보았다.

그럼 ref가 [1,2]로 찍히고 refcopy는 [1]만 나와야 하는데 둘 다 [1,2] 가 나왔다.

 

refcopy는 ref의 값을 할당받은게 아니라 ref가 갖고있는 메모리 주소를 할당받았기 때문이다. 

 

그럼 만약 

var obj = { a: 'a' };
obj = { b: 'b' };

obj는 json형태의 a의 주소를 할당 받았다.

그리고 다시 b의 주소를 할당 받았다. 추가한게 아니라 다시 할당받았다.

 

그럼 메모리에 있던 a 값은 어떻게 되는가? 이제 더이상 그 주소를 가르키는 변수가 없는데 ?

이 경우에 자바스크립트 엔진은 이 a 값을 가비지컬렉션 대상으로 지정하여 메모리에서 지운다.

그렇기 때문에 프로그래머는 다시 그 값을 지정할 수 없다.

 

여기 또 다른 이슈가 있는데 == 연산자의 경우에

var arr1 = ['a'];
var arr2 = arr1;

console.log( arr1 === arr2 );  // true

var arr3 = ['a'];
var arr4 = ['a'];

console.log( arr3 === arr4 ); //  false

===는 == 보다 더 엄격한 검사라고 대충 넘어가고 예제를 보면

arr1과 arr2는 같은 값을 가져 true가 나왔다. 왜 ? 같은 주소를 참조하고 있으니까

arr3과 arr4는 같은 데이터를 할당하는 듯 보이지만 둘은 서로 다른 메모리 주소를 갖고있다.

그래서 false가 나왔다. 

 

다시 말해서 arr3 === arr4는 'a' === 'a' 을 하는게 아니라 arr3메모리주소 === arr4메모리주소 이걸 비교하는거다.

그래서false가 나온다.

 

 

 

위의 링크에 더 자세한 설명이 나온다.

https://javascriptweblog.wordpress.com/2010/09/27/the-secret-life-of-javascript-primitives/

 

The Secret Life of JavaScript Primitives

You may not know it but, in JavaScript, whenever you interact with string, number or boolean primitives you enter a hidden world of object shadows and coercion. So dust off your Sherlock Holmes out…

javascriptweblog.wordpress.com

 

객체는 특성(Property)들의 집합이고 특성으로 객체와 기초요소(Primitive)를 추론할 수 있다. 

Primitive는 특성을 갖지 않은 값이라고 할 수 있다.

 

자바스크립트에서는 5개의 기초 타입이 있다. undefined, null, boolean, string, number

그 밖의 나머지 타입은 객체이다. 기초타입이 아니다.

기초타입인 boolean, string, number은 보통 객체에 감싸져 있고 객체들은 생성자에 의해 인스턴스화 되어 있다.

 

 

그럼 어떻게 기초타입인 abc에서 abc.length를 하면 값을 반환할까? 특성을 갖고 있지 않는데 어떻게 ?

 

자바스크립트는 기초요소와 객체를 강제한다. String타입인 primitive abc는 .length 값을 반환하기 위해 객체에 묶여진다. 자바스크립트가 그렇게 한다. 이 묶인 객체는 아주 잠깐( length값을 반환하는 순간 ) 존재하다가 가비지콜렉션에 의해 지워진다.

 

예제이다.

String.prototype.returnMe = () => this;

var a = "abc";
var b = a.returnMe();

a; // "abc"
typeof a; // "String" ( 이건 기초요소 )

b; // "abc"
typeof b; // "object"

선언된 변수 a는 기초타입인 string이다. b는 변수 a를 받아서 자신이 뭔지 반환하는 메소드이다.

a의 타입은 string으로 출력이 되는데 b는 객체로 출력이 된다. 이유는 b에서 .returnMe()라는 메소드를 만들어 잠시 존재하는 객체를 붙잡았기 때문이다. 

 

a는 객체가 abc를 반환하고 GC에 의해 처리됬는데 b는 객체가 GC되지 않았다.

(strict mode에서는 b도 GC된단다.)

 

그니까 기초타입에서 값을 직접 반환하지 않고 객체가 반환한다는 얘기인거 같다.

 

 

근데 이걸 왜 알아야 하냐 ???

 

나중에 자바스크립트를 갖고 놀려면 알아야 하기도 하고, 당장은 될 것 같은 코드에서 에러가 왜 나는지 이해하는데 도움이 될 것이다.

 

예를 들어 이런 상황이다.

var hi = "hello";
hi.sad = 1;

hi.sad; // undefined

hi라는 변수에 String hello를 집어 넣었다. 여기에 또 .sad를 하여 객체를 생성하여 1을 집어 넣었다. 

근데 hi.sad가 왜 1이 반환되는게 아니고 undefined일까 ??

 

위의 예제를 풀어서 보면 아래와 같다.

var hi = "hello";
hi.sad = 1;

// 1을 넣기 위해 새로운 객체를 생성
(new String("hello")).sad = 1;

hi.sad;
// 호출되었으니까 다시 객체 생성
(new String("hello")).sad; // undefined

hi객체가 .sad에 1을 넣었다 한들 가비지콜렉션 대상이 되서 사라졌다. 새롭게 생성된 객체에 있을리 만무한 .sad를 호출 했기 때문에 undefined가 뜬 것이다.

 

 

솔직히 지금 당장 primitive type에 대해 이해하고 코드를 작성하는건 힘들지 몰라도 내가 쓴 코드에 에러가 나고 그게 자바스크립트의 이 특성 때문이라는 걸 알고 있다는게 중요한 것이다. 모르는걸 알면 찾으면 되니까...

+ Recent posts