그때는 자바 개발하기에도 바쁜데 자바스크립트에 제이쿼리를 사용하는 것도 모자라 소스를 공부?
라고 생각했었다.
지금은 자바스크립트를 배우는 것이 자바에도 도움되고 더 나아가 내가 어떤 언어를 짜던 잘나가는 오픈소스를 읽는 것이 중요하다고 생각한다. (하지만 제대로 소스를 판 적은...)
오늘은 제이쿼리에게서 한 수 배워보고자 한다.
오늘 주제는 jQuery는 어떻게 new를 없앴는가? 이다.
대부분 new를 없앤다는 것은 생성자를 사용하지 않는 다는 말과 같다. 매개변수가 필요하면 그 매개변수를 클로저로 덮어서 함수로 던지는 것이다. 그러면 그 함수의 스코프는 자기 함수 밖에 있는 변수(매개변수 포함)를 덮은(closure) 채로 세상 밖에 내던져진다.
하지만 jQuery는 그런 것 같지 않다. 어떻게 그렇지 않은지 아주 쉬운 방법으로 알 수 있다.
이처럼 __proto__안에 수 많은 함수들이 있는 것을 알 수 있다. 어쩌면 당연한 것이다. 수 많은 기능들을 사용할 때마다 각 Execution Context에 포함해서 던진다는 말은 사용할 때마다 동일한 기능들이 메모리에 올라간다는 말이니까. 한 두개면 괜찮겠지만 저게 몇 개인가!
new를 사용하는 것이 분명하다.
그렇다면 jQuery소스를 한번 구경해보자.
jQuery($)의 정체
제이쿼리 사이트에서 일반 개발용소스를 참고했다.(https://code.jquery.com/jquery-3.4.1.js)일단 jQuery를 실행하면 무엇이 나오는가? 그것이 시작일 것이다. 일단 jQuery는 함수일 것이다. 그러니까 new가 없이 실행되는 것이다. jQuery라는 이름의 함수를 찾아보자.
var version = "3.4.1", // Define a local copy of jQuery jQuery = function( selector, context ) { // The jQuery object is actually just the init constructor 'enhanced' // Need init if jQuery is called (just allow error to be thrown if not included) return new jQuery.fn.init( selector, context ); }, ...
그렇다. jQuery를 실행하면 내부적으로 jQuery.fn.init이라는 생성자를 실행한다!
jQuery.fn.init의 정체
이제부터는 jQuery의 소스를 이용하면, 말이 많아지기 때문에, 제이쿼리의 간단한 구조를 간단히 설명하고자 한다. 조그만한 자바스크립트 라이브러리를 만들었다고 해보자./* 제이쿼리 구조를 설명하기 위해 만든 간단한 함수 */ (function (global) { var mini = function(name) { return new mini.fn.init(name); } mini.fn = mini.prototype = {}; mini.fn.sayHi = function() { console.log("hi " + this.name); return this; } mini.fn.sayBye = function() { console.log("bye " + this.name); return this; } mini.fn.init = function(name) { this.name = name; } mini.fn.init.prototype = mini.fn; global.mini = mini; })(window); var M = mini("Nam"); M.sayHi().sayBye();
녀석의 이름은 mini다. 즉시실행함수를 만들어서 스코프가 밖으로 나가지 못하도록 하였으며 글로벌에 mini라는 변수를 넣어야 하기 때문에 window를 매개변수로 넣었다.
jQuery에서는 이 global이라는 녀석을 받아서 jQuery에서 사용할 수 있는 녀석인지 유효성 체크도 한다.
그리고 mini를 실행하면 mini.fn.init 이라는 객체를 뱉을 것이다.
한번보자.
실행해보자.
var M = mini("nam"); M.sayHi().sayBye();
잘 보면 두 함수를 연속적으로 실행할 수 있는데, 이건 다른 라이브러리에서도 많은 보았을 것이다. 이렇게 함수를 연결하는 방법은 우리가 사용하는 객체를 계속 리턴하는 것이다.
그런데 왜 prototype이 아니라 fn이라는 녀석에 넣은 것일까?
아마 계속 쓰일 녀석이라서 fn으로 간편화 한게 아닌가 추측을 하지만 정확한 이유는 모른다.
이렇게 new가 없이 객체를 생성하는 방법을 알아보았다.