brunch

You can make anything
by writing

C.S.Lewis

by zwoo Sep 02. 2020

자바스크립트의 함수는 어디서 오는 걸까?

Prototype 의 메소드를 빌려서 씁니다. 클래스가 없기 때문이죠.

제스는 두근거리는 심장소리를 느끼며 그 물줄기를 따라갔다. 그렇게 몇백 미터 정도 더 걸어갔을 때 그녀 앞에 펼쳐진 건, 강의 시작점이었다.


초기 자바스크립트는 많은 객체지향 언어들과는 다르게 클래스 상속 개념이 없는 프로토타입 기반의 언어였다. 클래스 기반의 언어에서는 예를 들어 전자기기 클래스 으면 하위 클래스인 애플기기, 삼성기기 상위 클래스의 속성을 상속받는다. 애플기기의 하위클래스에는 아이폰, 맥북, 아이패드 등이 있고 상위 클래스들의 속성을 모두 상속받는다. 가령 아이폰은 전자기기 클래스의 '전기를 동력으로 움직임' 이라는 속성과, 애플기기 클래스의 '애플에서 만들어짐' 이라는 속성을 모두 물려받고 여기에 자기 자신의 속성인 '전화통화와 sns   있음' 이라는 속성까지 가지고 있는 것이다.


나는 클래스 기반 언어인 자바와 C를 깊이 공부하지 않아서 클래스의 개념을 '상위 클래스가 하위 클래스에게 내부 요소와 메소드를 상속한다' 라고 추상적으로 이해하고 있다. 나는 늘 상속이라는 개념이 꼭 강물같다고 생각했다. 위에서부터 아래로 흘러내리는 물줄기같다고. 위에서 흐른 물은 반드시 아랫물로 향하니까.


최신 업데이트된 ES6 버전 문법에서는 클래스 문법이 추가되었지만 자바스크립트에서는 원래 상속 개념이 없는 대신 prototype 이라는 객체를 new 연산자를 써서


let arr = new Array(5);


이런식으로 arr 이라는 이름의 인스턴스를 호출하면 길이 5의 배열이 생성된다. 그리고 이때, Array 라는 객체는 자바스크립트 문법상에 미리 만들어져있는 리스트 형태의 전역객체이다. arr 은 이제 Array 이 가진 메소드 중 일부를(전부가 아니다!) 마치 상속받은 것처럼 사용할 수 있다. 그러나 엄밀히 말하면 arr은 Array라는 전역객체를 참조해서 일부 메소드를 사용할 수 있는 것일 뿐, 완벽하게 복사하거나 모든 속성과 메소드를 전부 상속받은 것이 아니다. (자바스크립트에는 얕은 복사깊은 복사라는 개념도 등장한다. 이에 대해서는 아래에서 이어서 쓰겠다. )


Array.prototype.slice()가 아니라 Array.slice()


이렇게 new 연산자로 생성할 수 있는 기본 전역객체들은 prototype 이라는 속성을 가지며 이 역시 타입은 객체이다. 중요한 것은 각 전역객체가 가지고 있는 prototype 객체 안에 우리가 자바스크립트 코드를 구현하며 실행하는 메소드들이 들어있다는 것이다. 그래서 자연스럽게 arr.slice() 같은 메소드를 실행할 수 있는 것이다. 여기서 잠깐, 전역객체 Array가 가지고 있는 prototype 객체 안에 있는 메소드를 실행하고 있는데, Array.prototype.slice() 라고 쓰지는 않는다. 'prototype' 은 생략한다. 생략해도 되는 것이 아니라 원래 생략하도록 문법이 정해져있다. 도대체 왜 그런가 열심히 찾아봤는데 자바스크립트를 만든 사람이 그렇게 정했다고 한다.


얕은 복사와 깊은 복사


솔직히 디테일한 개념은 아직 완벽하게 이해하지 못한 부분도 있다. 자바스크립트의 각 전역객체가 가진 메소드들은 얕은 복사를 수행한다. 깊은 복사를 수행하는 기본 메소드는 내가 알기로는 없다. lodash 와 같은 유명한 라이브러리들이 깊은 복사를 수행하는 메소드를 제공한다. 깊은 복사는 그러니까, 중첩구조를 독립적으로 복사할 수 있는 복사를 말한다.


 let a = [1,2,3]; 이라고 변수를 만들어 값을 할당하고 let b = a; 로 복사를 해오면 둘은 같은 [1,2,3]을 바라본다. 둘은 전혀 독립적이지 않고 b[3]에 값 4를 추가하면 a = [1,2,3,4] 로 같이 변한다.


얕은 복사는 여기에서 가령 Array.slice() 메소드를 사용하여 배열중 원하는 부분만 잘라서 새로운 배열을 만드는 복사를 말한다. 이때 중첩구조를 가진 배열은 내부 배열은 다시 원본을 참조하게 되므로 원본 변형 문제가 발생한다.


가령 a = [1, [2, 3, 4]] 라는 배열을 slice() 로 처음부터 끝까지 잘라와서 b에게 주었을 때, b[0]=0 ; 으로 b 배열의 가장 바깥 요소, 그러니까 깊이가 1뎁스인 요소의 값을 수정해도 a[0]은 변하지 않는다.

그러나 깊이가 2뎁스 이상인 요소에 접근해서 수정을 하면, b[1] 이 참조하고 있는 [2,3,4] 는 a[1] 과 같은 곳을 바라보기 때문에 b[1].append(5) 를 하면 a[1] 에도 5가 추가된다. 즉 자바스크립트 전역객체의 prototype 객체가 가진 메소드를 통해서는 얕은 복사밖에 할 수 없으며, 중첩구조를 가진 객체를 완전히 독립적으로 복사할 수 없다. 이때 깊은 복사가 필요하며 이는 lodash 라이브러리가 다양한 메소드로 제공해주고 있다.





TMI.

지금까지 굵직한 웹프로젝트 두개정도를 해봤는데, 첫번째는 고생한 만큼 잘됐고 두번째는 여러가지 사정으로 막바지에 중단됐다. 잘된 것도 결코 내가 대단히 잘해서가 아니고 안된 것도 나만 못해서는 아니지만, 새 프로젝트를 맡게 되자 가슴이 쿵쾅거린다. 전에는 프로젝트 앞두고 마냥 설렜는데.


TMI 2.

온라인 브런치북을 발간한 이유는 학습곡선이 조금씩 더뎌지고 있었기 때문이다. 사실은 올해 목표가 실물 책 출판하기 였는데, 브런치에는 실물 책을 만들려면 매거진에 등록된 글이 서른 개가 되어야 한다는 조건이 있다. <웹을 그리는 작가> 매거진에 연재한 스무개가 채 되지 않는 글들은 업무일지이면서 공부노트였다. 그런데 프론트엔드 개발자로서 알고리즘과 자바스크립트 기초, css 문법 개념에 대한 지식이 아직 초보자 수준을 벗어나지 못해 더이상 쓸 이야기가 없었다. 새로 공부한 개념들은 아직 다 소화가 되지 않아 글로 담기에는 벅찼다. 자칫 공부도 연재도 멈춰버릴 것 같은 두려움에 고민을 하다가, 두권으로 나누기로 했다. 1편을 내면 동기부여가 되어 더 열심히 공부해서 2편을 낼 수 있지 않을까 하는 마음으로! (혹시 올해안에 실물 책을 못 내게 되더라도 온라인으로라도 냈으니까 위안을 삼으려는 생각도 있었다)


생각보다 정말 많은 분들이 내 책을 좋아해주셔서 설레고 기쁘다.


모든 분들께,

진심으로 감사합니다 :) 




Photo by Roma Ryabchenko on Unsplash

상단 인용문구 - <리버보이> - 팀 보울러


참고자료

<코어 자바스크립트> - 정재남

얕은 복사(shallow copy) vs 깊은 복사(deep copy) (https://blueshw.github.io/2016/01/20/shallow-copy-deep-copy/)

Array - mdn 공식문서(https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/slice)

As a JS Developer, This Is What Keeps Me Up at Night - JUSTEN ROBERTSON (https://www.toptal.com/javascript/es6-class-chaos-keeps-js-developer-up)


이전 06화 카톡 1도 모자라 점점점(...) 기능이 나타났을 때
brunch book
$magazine.title

현재 글은 이 브런치북에
소속되어 있습니다.

작품 선택

키워드 선택 0 / 3 0

댓글여부

afliean
브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari