mkdir build-release ../configure --disable-debug --enable-optimize --enable-win32-target=WIN95 make 5. SpiderMonkey 1.8.5 compile with NSPR (Mozill Build에서 제공한 MinGW를 사용한다.)
: 아래 파일 경로는 실제 파일 경로이다. 자기가 실제로 설정한 경로를 넣어주면 된다. mkdir build-release ../configure --disable-debug --enable-optimize --enable-win32-target=WINNT --enable-ctypes --enable-threadsafe \ --with-nspr-cflags="-I \ D:\project\Library\mozilla\nsprpub\build-release\dist\include\nspr" \ --with-nspr-libs=" \ D:\project\Library\mozilla\nsprpub\build-release\dist\lib\nspr4.lib \
14. Currying (커링) - 함수와 인수를 결합하여 새로운 함수를 만들 수 있게 한다. - 하나의 파라미터를 가진 함수를 여러번 호출하는 것과 같은 방법으로 여러 개의 파라미터를 가진 함수로 변형하는 것을 말한다. - 자바스크립트는 자체적으로 currying 메소드가 없으므로 아래와 같이 구현한다. - 개인적으로 어디다 이런 기능을 써야할지는 아직 잘 모르겠다. [code javascript] var print = function (strLog) { document.write (strLog); document.write ('<br>'); };
// 함수 객체에 method 추가 Function.prototype.method = function (name, func) { if (!this.prototype[name]) { this.prototype[name] = func; } };
Function.method ('curry', function () { // Array 객체의 slice 메소드를 가져온다. var slice = Array.prototype.slice; // Array 함수 객체에서 가져온 slice를 실행하는데 // apply를 사용하여 this객체를 arguments로 바꿔준다. // 추후 Array의 concat을 사용하기 위함이다. // 왜냐하면 arguments는 length 메소드만을 지원하는 배열이다. // 따라서 엄밀히 말하면 배열 객체가 아니다. // 아래구문은 arguments를 배열객체로 만들어 args에 할당한다. var args = slice.apply (arguments); // that 은 curry 메소드를 사용하는 함수 객체를 가리킨다. var that = this; // curry 함수를 호출한 함수 객체를 변경한다. // 변경된 함수 객체를 호출하면 arguments와 // 처음 curry 메소드 호출시 받은 arguments를 // 합치게 된다. return function () { return that.apply (null, args.concat (slice.apply (arguments))); }; });
// 단순히 arguments를 모두 더해서 리턴하는 함수객체이다. var add = function () { var iIndex = 0; var iCount = arguments.length; var iSum = 0;
// curry 를 호출해서 add1에 할당된 함수 객체는 // 1 이라는 arguments를 다음에 호출할 때 사용한다. var add1 = add.curry (1); // 변경된 add1을 호출한 함수 객체는 1 과 6을 // arguments 로 가지게 되므로 7을 리턴하게 된다. print (add1 (6)); // 7 [/code]
12. Memoization (메모이제이션) - 함수가 이전에 연산한 결과를 저장하고 있는 객체를 사용하는 기법을 말한다. - 아래 두 함수를 비교해보면 이 기능을 어디에 쓸지 알 것 같다. [code javascript] // 피보나치 수열 함수이다. // 아래의 연산을 하기 위해서는 453번의 호출이 발생한다. var fibonacci = function (n) { return n < 2 ? n : fibonacci (n - 1) + fibonacci (n - 2); };
for (var iIndex = 0; iIndex <= 10; i += 1) { print ('// ' + i + ': ' + fibonacci (i)); }
// 위의 함수를 아래와 같이 변경하면 29번의 호출만 발생한다. // 그 중 18번은 메모이제이션 결과를 얻기위한 과정이다. var fibonacci = function () { var memo = [0, 1]; var fib = function (n) { var result = memo[n]; if (typeof result !== 'number') { result = flb (n - 1) + flb (n - 2); memo[n] = result; } return result; }; return fib; } (); [/code]
- 아래와 같이 일반화 시킬 수 있다. [code javascript] // 결과를 저장할 배열과 메모제이션을 할 함수를 인자로 받는다. var memoizer = function (memo, fundamental) { var shell = function (n) { var result = memo [n]; // 메모에 새로 저장할 데이터이면 fundamental을 호출한다. if (typeof result !== 'number') { result = fundamental (shell, n); memo[n] = result; } return result; }; return shell; };
// 피보나치 함수 var fibonacci = memoizer ([0, 1], function (func, n) { return func (n -1) + func (n - 2); });
// 팩토리얼 함수 var factorial = memoizer ([1, 1], function (func, n) { return n * func (n - 1); }); [/code]
10. Closure - 내부 함수에서 자신을 포함하고 있는 외부 함수의 매개변수와 변수들을 접근할 수 있다. : 단, this와 arguments는 예외 - 이 기능을 이용하면 아래와 같이 private 속성을 구현할 수 있다. [code javascript] var print = function (strLog) { document.write (strLog); document.write ('<br>'); };
// myObject는 함수의 실행 결과를 리턴받았기 때문에 // 외부에서 함수의 내부 변수 (var value)에 접근할 수 없다. // 따라서 함수 내부에서 정의된 개체를 통해서만 접근할 수 있다.
var myObject = function () { var value = 0;
return { increment: function (inc) { value += typeof inc === 'number' ? inc : 1; }, getValue: function () { return value; } }; } (); // 함수의 실행한 결과를 myObject에 할당한다.
print (myObject.value); // undefined print (myObject.getValue ()); // 0 [/code] - 내부 함수가 외부 함수에 있는 변수의 복사본이 아니라 실제 변수에 접근한다. [code javascript] <html> <header> <script language='javascript'> var add_the_handlers = function (nodes) { var i; // 원하는 기능은 각 노드에 유일한 키값을 할당하는 것이고 // onClick이벤트가 발생하면 키값을 출력하도록 하는 것이다. // 하지만, Closure 기능에 의해 // alert (i)가 호출되면 외부 함수의 var i값을 사용하게 된다. // 외부 함수의 var i 는 이미 4로 바뀌어 있으므로 // onClick 이벤트가 발생하면 모두 4를 출력한다. // 따라서 다음 코드블럭 처럼 수정해야한다. for (i=0; i<nodes.length; i+=1) { nodes[i].onclick = function (e) { alert (i); }; } }; </script> </header> <body onLoad='javascript:add_the_handlers (document.frmText);'> <form name='frmText'> <input type='textbox' name='txt1' value='0'><br> <input type='textbox' name='txt2' value='1'><br> <input type='textbox' name='txt3' value='2'><br> <input type='textbox' name='txt4' value='3'><br> </form> </body> </html> [/code] [code javascript] var add_the_handlers = function (nodes) { var i; // 편의상 i를 iIndex로 사용했다. // 넘겨받은 iIndex 값을 사용하기 때문에 의도한대로 정상출력된다. for (i=0; i<nodes.length; i+=1) { nodes[i].onclick = function (iIndex) { return function (e) { alert (iIndex); }; } (i); } }; [/code] - 이벤트 함수 출력을 해보려고 print (document.frmText[2].onClick);을 호출해보았으나 undefined 로 출력되었다. 이유는 모르겠다. 그래서 한김에 아래와 같이 수행해 보았다. 별의미는 없지만 ~ ^^; [code javascript] var arrFunc = []; var testFunc = function () { var iIndex; for (iIndex = 0; iIndex < 2; iIndex ++) { arrFunc[iIndex] = function (iIndex) { return function () { return (iIndex); } } (iIndex); } }; testFunc (); var iIndex; for (iIndex = 0; iIndex < arrFunc.length; iIndex ++) { print (arrFunc[iIndex]); // 배열에 저장된 함수 및 값을 출력한다. print ('arrFunc[' + iIndex + ']=' + arrFunc[iIndex]()); } /* 실행 결과 function () {
return iIndex;
} arrFunc[0]=0 function () {
return iIndex;
} arrFunc[1]=1 */ [/code]
4. 인수 배열 (arguments) - 함수를 호출할 때 전달된 모든 인수를 접근할 수 있게 한다. : 매개변수 개수보다 더 많이 전달된 인수도 포함한다. [code javascript] var print = function (log) { document.write (log); document.write ('<br>'); };
var sum = function () { var i, sum = 0; // arguments 는 length라는 속성은 있지만 // 모든 배열이 가지고 있는 메소드가 있는 것은 아니다. // - 저자는 설계상의 문제로 지적했다. for (i = 0; i < arguments.length; i += 1) { sum += arguments[i]; } return sum; }; print (sum(4, 8, 15, 16, 23, 42)); // 108 [/code]
5. 반환 - 함수는 항상 값을 반환한다. : 반환값이 없는 경우 undefined가 반환된다. - 함수를 new 라는 전치 연산자와 함께 실행하고 반환값이 객체가 아닌 경우 반환값은 this (새로운 객체)가 된다. : 아래와 같이 생성자 호출 패턴에서 객체를 반환하도록 해보면 new라고 하는 키워드의 의미가 사라진다. (8번째 라인) [code javascript] var myObject = function () { return { name : 'taegony', age : 10 } }();
var Quo = function (string) { this.status = string; return myObject; };
Quo.prototype.get_status = function () { return this.status; };
var myQuo = new Quo ('confused'); print (myQuo.age); // 10 print (myQuo.status); // undefined
if (myQuo === myObject) { print ('same'); } else { print ('not same'); } // same (myQuo와 myObject는 같은 객체이다) [/code]
6. 예외 - throw문은 name, message 속성을 가진 예외 객체을 반환한다. - 예외 객체는 try 문의 catch 절에 전달된다. - try 문은 하나의 catch 블록을 가진다. : 따라서 catch 내에서 예외 객체의 name으로 종류를 구분해야한다. [code javascript] var add = function (a, b) { if (typeof a !== 'number' || typeof b !== 'number') { throw { name : 'TypeError', message : 'add needs numbers' }; } return a + b; }
7. 기본타입에 기능 추가하기 - 자바스크립트는 기본타입에 기능 추가를 허용한다. - Object.prototype 또는 Function.prototype에 추가한다. [code javascript] // 모든 함수객체에 method라고하는 property를 추가한다. // 내부에 정의된 property가 없으면 추가한다. Function.prototype.method = function (name, func) { if (!this.prototype[name]) { this.prrototype[name] = func; } };
// Number, String은 함수객체이다. print (typeof String); // function print (typeof Number); // function [/code]
8. 재귀적 호출 - 웹브라우져의 DOM(Document Object Model) 같은 트리 구조를 다루는데 효과적인다. - Tail reursion (꼬리 재귀) 최적화를 지원하지 않는다. [code javascript] <html> <header> <script language='javascript'> var walk_the_DOM = function walk (node, func) { func (node); node = node.firstChild; while (node) { walk (node, func); node = node.nextSibling; } }
// 인자 : attribute, value (value 는 optional) var getElementsByAttribute = function (att, value) { var results = []; walk_the_DOM (document.body, function (node) { var actual = node.nodeType === 1 && node.getAttribute (att); if (typeof actual === 'string' && (actual === value || typeof value !== 'string')) { results.push (node); } });
9. Scope - C언어 유형의 블록 scope를 제공하지 않는다. - 하지만 함수 scope는 제공한다. : 함수 내에서 정의된 매개변수와 변수는 함수 외부에서 유효하지 않다. 함수 내에서 정의된 변수는 함수 어느 곳에서도 접근할 수 있다. : 내부 함수에서 자신을 정의한 외부함수의 매개변수와 변수에 접근할 수 있다. (closure 라고한다.) - 함수에서 사용하는 모든 변수는 가능하면 첫 부분에 선언하도록 한다. [code javascript] var foo = function () { var a = 3, b = 5; var bar = function () { var b = 7, c = 11; // a = 3, b = 7, c = 11 a += b + c; // a = 21, b = 7, c = 11 }; bar (); // a = 21, b = 5, c = undefined }; [/code]
1. 함수 객체 - 함수는 객체이다. : 객체는 prototype 객체로 숨겨진 연결을 갖는 name/value 쌍들의 집합체이다. : 객체는 Object.prototype에 연결된다. - 함수 객체는 Function.prototype에 연결된다. : Function은 Object.prototype에 연결된다. - 모든 함수는 숨겨진 두 가지 속성이 있다. : context (함수의 문맥), code (함수의 행위를 구현) - 모든 함수 객체는 prototype이라는 속성이 있다. : prototype 속성은 객체이며 constructor 라는 속성을 포함하고 있다. : constructor는 함수 자체를 값으로 가진다. - 함수를 다른 객체와 구분 짓는 특징은 호출할 수 있다는 점이다. - 아래는 시험삼아 해본 코드들이다. [code javascript] // 칸띄우기 귀찮아서 넣은 함수 // 앞으로 계속 이놈을 사용할 예정 var print = function (log) { document.write (log); document.write ('<br>'); }
var myFunc = function () { print ('this is myFunc' ); }
myFunc (); // this is myFunc print (typeof myFunc.prototype); // object print (myFunc.prototype); // [object Object]
print (typeof Function.prototype); // function print (Function.prototype); // function Empty() { [Native code] }
print ('Function.prototype.constructor = ' + print.prototype.constructor); // function (log) { document.write(log);document.write(" // ");} // 함수 자체를 나타낸다. // <br>은 html 태그이므로 화면상의 줄이 바뀌었다. [/code]
2. 함수 리터럴 - 함수 리터럴은 4가지 부분이 있다. : function, 함수명, 매개변수 집합, 중괄호로 둘러싸인 문장들의 집합 - 함수 리터럴로 생성한 함수 객체는 외부 문맥으로의 연결이 있다. : 클로져 (closure)라고 한다.
3. 호출 - 함수를 호출하면 매개변수, this, arguments가 넘어가게 된다. - this는 호출 패턴에 따라 다르게 초기화한다. : 호출 패턴은 메소드 호출, 함수 호출, 생성자 호출, apply 호출 패턴이 있다. - 함수를 호출하는 호출 연산자는 함수를 나타내는 표현식 뒤에 이어지는 한 쌍의 괄호이다. - 매개변수 수보다 초과하는 인수는 무시된다. - 매개변수 수보다 적은 경우 남은 매개변수는 undefined가 할당된다. [code javascript] // myObject 는 함수를 받는게 아니라 함수를 호출한 결과를 받는다. var myObject = function () { return { name: 'taegony', age: 10 } }(); // 이 부분이 호출하는 부분이다.
print (myObject.name); // taegony [/code]
3.1 메소드 호출 패턴 - 함수를 객체의 속성에 저장하는 경우 이 함수를 메소드라고 한다. - this는 메소드를 포함하는 객체에 바인딩 된다. - this와 객체의 바인딩은 호출 시에 일어난다. - 자신의 객체 문맥을 this로 얻는 메소드를 public method라고 한다. [code javascript] var myObject_method = { value: 0, increment: function (inc) { this.value += typeof inc === 'number' ? inc : 1; } // '===' 는 type과 value가 같은 경우가 참이다. };
3.2 함수 호출 패턴 - 함수가 객체의 속성이 아닌 경우 함수로서 호출한다. - this는 전역객체에 바인딩된다. : 이 책의 저자는 이 경우를 언어의 잘못된 설계로인해 생긴 문제라고 본다. 객체의 메소드가 내부 함수를 가지는 경우 내부 함수에서 this는 자신을 포함한 객체를 가리키는 것이 라니라 전역객체를 가리키게 되기 때문이다. - 전역객체 문제에 대한 대안으로 아래와 같은 방법을 설명했다. [code javascript] var add = function (a, b) { return a + b; } // 이전에 사용한 myObject에 double이라는 메소드를 추가한다. // 이 때 아래 정의된 helper 함수에서 this는 전역객체를 가리키므로 // double 메소드의 this를 that 변수로 할당하여 helper 함수에서 사용한다. myObject.double = function () { var that = this; var helper = function () { that.age = add (that.age, that.age); } helper (); } print (myObject.age); // 10 myObject.double (); print (myObject.age); // 20 [/code]
3.3 생성자호출 패턴 - 함수를 new 연산자와 함께 호출하면 호출된 함수의 prototype 속성의 값에 연결되는 (숨겨진) 링크를 갖는 객체가 생성되고, 이 새로운 객체는 this에 바인딩 된다. - 생성자 함수를 new없이 사용하면 경고없이 그냥 넘어간다. 따라서 실수를 방지하기 위해 첫번째 문자를 대문자로 표기한다. [code javascript] // 생성자 함수 정의한다. // 여기서 this는 새로운 객체를 가리킨다. var Quo = function (string) { this.status = string; }; // public method를 정의한다. Quo.prototype.get_status = function () { return this.status; }; // 새로운 객체를 생성한다. var myQuo = new Quo ('confused'); print (myQuo.get_status); // confused print (myQuo.status); // confused [/code]
3.4 apply 호출 패턴 - apply 메소드는 함수를 호출할 때 사용할 인수들의 배열을 받아들인다. - this의 값을 선택할 수 있도록 해준다. - apply 메소드는 두 개의 매개변수를 가진다. : 첫 번째는 this이고, 두 번째는 매개변수들의 배열이다. [code javascript] // this는 null, 매개변수는 3과 4를 넘긴다. var array = [3, 4]; var sum = add.apply (null, array); print (sum); // 7
// statusObject 개체를 선언하고 // Quo.prototype.get_status 메소드의 this값을 statusObject로 변경한다. // 따라서 Quo.prototype.get_status는 statusObject.status를 리턴하게 된다. var statusObject = { status: 'A-OK' }; var status = Quo.prototype.get_status.apply (statusObject); print (status); // A-OK [/code]
1. 객체 리터럴 - 아무 것도 없거나 하나 이상의 이름/값 쌍들을 둘러싸는 중괄호이다. - 속성 이름은 어떤 문자열이라도 가능 (빈 문자열 포함) 하다. - 속성 이름이 유효한 이름이고 예약어가 아니면 따옴표는 생략가능하다. [code javascript] var objHello = {}; var objName = { "first-name": "Tae Gon", "last-name": "Kim", // '-' 이 있으므로 따옴표 사용 number: 9876 // 유효하고 예약어가 아니므로 따옴표 생략 }; [/code]
2. 속성값 읽기/갱신/추가 - 대괄호([])를 사용하여 읽는다. - 유효한 이름이고 예약어가 아닌 경우 마침표(.)를 대신 사용가능하다. - 존재하지 않는 속성을 읽으면 undefined 반환한다. - undefined 속성을 참조하려고 하면 TypeError 예외가 발생한다. - 존재하는 속성에 값을 할당하면 갱신된다. - 존재하지 않는 속성에 값을 할당하면 속성이 추가된다. [code javascript] document.write (objName["first-name"]); // Tae Gon 출력 document.write (objName.number); // 9876 출력 document.write (objName["middle-name"]); // undefined 출력
document.write (objName.mother); // undefined 출력 document.write (objName.mother.age); // TypeError 발생
// 예외 발생을 위해 다음과 같이 처리 var strValue = objName.mother && objName.mother.age; document.write (strValue); // undefined 출력 [/code]
3. 객체는 참조 방식으로 전달된다.
4. 프로토타입 - 객체 리터럴로 생성되는 모든 객체는 Object.prototype 객체에 연결된다. - 객체 생성시 해당 객체의 프로토타입이 될 객체를 선택가능하다. - 프로토타입 연결은 객체의 속성을 읽을 때만 사용한다. - 해당 속성이 객체에 없는 경우 프로토타입 객체에서 찾으려고한다. : prototype chain의 마지막인 Object.prototype까지 계속 이어진다. : 그래도 찾지 못하면 undefined를 반환한다. : 이것을 위임 (delegation)이라고 한다. [code javascript] // 함수를 new라는 전치 연산자와 함께 호출하면 // 호출한 함수의 prototype 속성의 값에 연결된 링크를 갖는 객체가 생성된다.
if (typeof Object.create !== 'function') { Object.create = function (o) { var F = function () { F.prototype = o; return new F(); }; } var objName2 = Object.create (objName); [/code]
5. Reflection - 객체에 어떠한 속성들이 있는지 접근해서 확인하는 것을 말한다. - typeof, hasOwnProperty, for in 등을 사용한다. [code javascript] // hasOwnProperty 는 prototype chain을 바라보지 않는다. document.writeln (objName.hasOwnProperty ('first-name')); // true document.writeln (objName.hasOwnProperty ('middle-name')); // false
// 열거 방식으로 확인 // 이 경우 순서가 이름순으로 나온다는 보장이 없다. var name; for (name in objName) { if (typeof objName[name] !== 'function') { document.writeln (name + ': ' + objName[name]); } } [/code]