Upload
young-beom-rhee
View
281
Download
7
Embed Size (px)
Citation preview
스파르탄 스터디CH.04. 객체지향 , 함수형 프로그래밍
자바 , C++, C# 등의 class 가 없다 .
Prototype 기반 언어들에 영감을 받음 . (https://ko.wikipedia.org/wiki/프로토타입_기반_프로그래밍)
JavaScript 는 prototype 객체 , 생성자 등을 통해 구현 가능
클래스 기반 vs 프로토타입 기반
function Func(x, y) { this.x = x; this.y = y;}
var func = Func(1, 2);
this === ?
생성자 함수와 this
function Func(x, y) { this.x = x; this.y = y;}
var func = new Func(1, 2);
this === ?
this === func=> 인스턴스를 바라본다
When Number is called as a function rather than as a constructor, it performs a type conversion.
var num = 1;var num2 = Number(1);var num3 = new Number(1);
console.log(num === num2);console.log(num === num3);
console.dir(num);console.dir(num3);
new 키워드 필수Number, String 등등에는 안써도 되던데 ?
Type 변환을 위한 메서드를 추가로 가지고 있을뿐 .new 로 생성하지 않으면 instance 가 없다 .var func = Func(1, 2); // func === undefined
Spec.
function Person(name) { this.name = name;
this.getName = function () { return this.name; }
this.setName = function (name) { this.name = name; }}
var me = new Person("yb"); console.log(me.getName());
me.setName("new yb");console.log(me.getName());
공통속성을 추가하는 경우
// Quiz : getName, setName 메서드는 Per-son 에 추가 되었을까 ? 아니면 me 에만 추가 되었을까 ?
중복되는 영역을 메모리에 올려놓고 사용-> 메모리 낭비
var personA = new Person("personA");var personB = new Person("personB");var personC = new Person("personC");
불필요한 중복
personA.getName = function () { return 'Hello, ' + this.name;}
console.log(personA.getName());console.log(personB.getName());console.log(personC.getName());
변경이 발생했을 때
Hello, personApersonBpersonC
변경된 사항이 적용되지 않는다 .
function Person(name) { this.name = name;}
Person.prototype.getName = function () { return this.name;}
Person.prototype.setName = function (name) { this.name = name;}
var personA = new Person("personA");var personB = new Person("personB");var personC = new Person("personC");
console.log(personA.getName());console.log(personB.getName());console.log(personC.getName());
공통속성을 추가하는 경우 – Prototype 을 이용
Instance 마다 개별적으로 가져야 할 프로퍼티
공통으로 가져야 할 프로퍼티
Prototype 을 사용하지 않은 경우
Prototype 참조
변경이 발생했을 때Person.prototype.getName = function () { return 'Hello, ' + this.name;}
console.log(personA.getName());console.log(personB.getName());console.log(personC.getName());
Hello, personAHello, personBHello, personC
Prototype 을 참조하고 있는 다른 모든 Instance 에도 모두 적용
Prototype 의 몇 가지 속성
name : name / hasOwnProperty : truename : getName / hasOwnProperty : falsename : setName / hasOwnProperty : false
for (var obj in personA) { console.log('name : ', obj, ' / hasOwnProperty : ' + personA.ha-sOwnProperty(obj));}
- 마치 자신의 속성처럼 열거할 수 있다 .(for in 사용 )- hasOwnProperty() 메서드를 사용해 소유여부를 확인할 수 있다 .
function Circle(r) { this.r = r;}
Circle.prototype.PI = 3.14;
Circle.prototype.getArea = function () { return 2 * this.r * this.PI;}
var r2 = new Circle(2);console.log(r2.getArea());
var r3 = new Circle(3);console.log(r3.getArea());
var r3Alien = new Circle(3);r3Alien.PI = 4.74;console.log(r3Alien.getArea());
내부의 property 가 있는 경우에 prototype 을 참고하지 않는다 .이런 경우를 다른 property 가 가렸다 (shadows) 혹은 숨겼다(hides) 라고 한다
- Prototype property 가 참조되지 않을 수 있다 .
Function.prototype.addMethod = function (name, func) { if(!this.prototype[name]) { this.prototype[name] = func; }}
function Person(name) { this.name = name;}
Person.addMethod('setName', function(name) { this.name = name;});
Person.addMethod('getName', function () { return this.name;});
var personA = new Person("personA");var personB = new Person("personB");var personC = new Person("personC");
크락포드옹 스탈
Overriding 을 방지할 수 있다
Function 의 용도가 2 가지실행 , 생성자혼란을 피하기 위해 생성자 함수는 대문자로 시작하도록 권고
크락포드옹님 주의사항
function createObject(parentObj) { function Func() {} Func.prototype = parentObj; return new Func();}
Object.create() 와 동일
var personPrototype = { getName : function() { return this.name; } , setName : function (name) { this.name = name; }};
var student = createObject(personPrototype);
console.log(student.getName());student.setName('student');console.log(student.getName());
이런 방식으로 prototype 을 구현한 것과 같이 생성 가능
Class 기반의 생성자처럼 생성하는 시점에 초깃값을 주고 싶다면 ?
상속을 구현
// 초깃값을 넣고 싶은 경우var personPrototypeFunc = function(name){ return { name : name , getName : function() { return this.name; } , setName : function (name) { this.name = name; } }};
var student2 = createObject(personPrototypeFunc('student2'));
console.log(student2.getName());student2.setName('student2_changed');console.log(student2.getName());
- 초깃값을 주고 싶은 경우
재정의 , 기능확장function extend(obj, prop) { if(!prop) { prop = obj; obj = this; }
for (var i in prop) { obj[i] = prop[i]; }
return obj;}
var added = { setAge : function (age) { this.age = age; } , getAge : function () { return this.age; }};
extend(student, added);
student.setAge(25);console.log(student.getAge());
extend 메서드를 구현하거나jQuery 의 extend 메서드를 사용
슈퍼 클래스와 서브 클래스function Rectangle(w, h) { this.width = w; this.height = h;}
Rectangle.prototype.area = function () { return ' 넓이 : ' + this.width * this.height;}
// 아래 코드는 Rectangle 클래스를 어떻게 서브 클래스화 하는지 보여준다function PositionedRectangle(x, y, w, h) { // 생성자 체이닝 Rectangle.call(this, w, h);
this.x = x; // 사각형의 좌표를 저장한다 . this.y = y;}
// Rectangle 를 서브 클래스화 시키려면 명시적으로 프로토타입 객체를 생성해야 한다 .PositionedRectangle.prototype = new Rectangle();
// PositionedRectangle 객체의 constructor 를 가지도록 기본값을 다시 할당한다 .PositionedRectangle.prototype.constructor = PositionedRectangle;
PositionedRectangle.prototype.getPosition = function () { return 'x : ' + this.x + ' / y : ' + this.y;}
// 3, 4 에 위치한 2x2 사각형var rect = new PositionedRectangle(3, 4, 2, 2);console.log(rect.getPosition()); // x : 3 / y : 4console.log(rect.area()); // 넓이 : 4
// rect 객체는 세 개 클래스의 인스턴스가 된다 .console.log(rect instanceof PositionedRectangle && rect instanceof Rectangle && rect instanceof Object); // true
클래스 방식의 상속패턴 완성 // 라이브러리로var inherit = function(Parent, Child) { var F = function() {}; return function(Parent, Child) { F.prototype = Parent.prototype; Child.prototype = new F(); Child.prototype.constructor = Child; Child.super = Parent.prototype; return new Child(); }}();
var student2 = inherit(Person, Student);
console.dir(student2);
생성자 체이닝
function PositionedRectangle(x, y, w, h) { this.superclass(w, h); this.x = x; this.y = y;}
// 상위 클래스 생성자에 대한 참조를 저장한다 .PositionedRectangle.prototype.superclass = Rectangle;
처음의 예제처럼 call 이나 apply 를 사용하지 않아도 된다 .
생성자 함수에서 상위 클래스의 생성자 함수를 명시적으로 호출할때 아래와 같은 방법도 가능
재정의 된 메서드 호출하기서브 클래스에서 재정의하는 메서드는 종종 기존 메서드에 있던 기능을 완전히 교체하기보다 확장시킨다 .
// 메서드 재정의Rectangle.prototype.toString = function () { return '[' + this.width + ',' + this.height + ']';}
PositionedRectangle.prototype.toString = function () { return '(' + this.x + ',' + this.y + ')' + // PositionedRectangle 필드들 Rectangle.prototype.toString.apply(this); // 상위 클래스에 체이닝 . 어떤 객체를 참조할지 지정하기 위해 apply() 와 함께 호출 .};
console.log(rect.toString());
재정의 된 메서드 호출하기서브 클래스에서 재정의하는 메서드는 종종 기존 메서드에 있던 기능을 완전히 교체하기보다 확장시킨다 .
// 메서드 재정의Rectangle.prototype.toString = function () { return '[' + this.width + ',' + this.height + ']';}
PositionedRectangle.prototype.toString = function () { return '(' + this.x + ',' + this.y + ')' + // PositionedRectangle 필드들 Rectangle.prototype.toString.apply(this); // 상위 클래스에 체이닝 . 어떤 객체를 참조할지 지정하기 위해 apply() 와 함께 호출 .};
console.log(rect.toString());
Functional Programming
함수의 조합으로 작업을 수행한다 .
작업에 필요한 데이터와 상태는 변하지 않는다 .오로지 함수만 변한다 .
명령형 프로그래밍 (Imperative Programming)<-> 함수형 프로그래밍 (Functional Programming)
명령을 순서대로 기술하는 것은 겉모양이 함수여도 Procedure 에 가깝다 .
함수형 프로그래밍의 함수는 독립적으로 작동하는 순수함수 (Pure function)
명령형 프로그래밍function sum(arr) { var len = arr.length; var i = 0, sum = 0; for (; i < len; i++) { sum += arr[i]; } return sum;}
var arr = [1, 2, 3, 4];
console.log(sum(arr));
function multiply(arr) { var len = arr.length; var i = 0, sum = 1; for (; i < len; i++) { sum *= arr[i]; } return sum;}
var arr = [1, 2, 3, 4];
console.log(multiply(arr));
유사한 로직이지만 매번 새로 구현해야 한다 .
함수형 프로그래밍function reduce(func, arr, memo) { var len = arr.length, i= 0, accum = memo; for (; i < len; i++) { accum = func(accum, arr[i]); } return accum;}
var arr = [1, 2, 3, 4];
var sum = function(x, y) { return x+y;}
var multiply = function(x, y) { return x*y;}
console.log(reduce(sum, arr, 0));console.log(reduce(multiply, arr, 1));
추상화 , 모듈화를 통한 재사용이 가능하다 .
핵심요소함수 = 일급객체Þ 값으로 사용 가능하고 전달할 수 있다 . 클로저=> 함수의 실행이 끝난 후에도 존재하고 , 모듈화를 가능하게 해준다 .
함수 실행 소요시간function getCostTime(func, param, repeatTime) { var start = new Date().getTime(); if(repeatTime) { for (var i = 0; i < repeatTime; i++) { func(param); } } else { func(param); } var elapsed = new Date().getTime() - start; console.log(' 소요시간 : ' + elapsed + ' ms'); return elapsed;}
getCostTime(function(){ for (var i = 0; i < 1000000000; i++) { }});
Memoization
var fact = function () { var cache = {'0' : 1}; var func = function (n) { var result = 0;
if(typeof(cache[n]) === 'number') { result = cache[n]; } else { result = cache[n] = n * func(n-1); } console.log(n + '! = '+ result); return result; }
return func;}();
console.log(fact(10));console.log(fact(20));
Memoization - Prototype 에 선언Function.prototype.memoization = function (key) { var arg = Array.prototype.slice.call(arguments, 1); this.data = this.data || {};
return this.data[key] !== undefined ? this.data[key]: this.data[key] = this.apply(this, arg);}
function myCalculate1(input) { return input * input;}
function myCalculate2(input) { return input * input / 4;}
myCalculate1.memoization(1, 5);myCalculate1.memoization(2, 4);myCalculate2.memoization(1, 6);myCalculate2.memoization(2, 7);
console.log(myCalculate1.memoization(1));console.log(myCalculate1.memoization(2));console.log(myCalculate2.memoization(1));console.log(myCalculate2.memoization(2));
피보나치 수열var fibo = function() { var cache = {'0' : 0, '1' : 1};
var func = function (n) { if(typeof(cache[n]) === 'number') { result = cache[n]; } else { result = cache[n] = func(n-1) + func(n-2); } return result; }; return func;}();
console.log(fibo(10));
피보나치 수열var cacher = function (cache, func) { var calculate = function (n) { if(typeof(cache[n]) === 'number') { result = cache[n]; } else { result = cache[n] = func(calculate, n); } return result; } return calculate;};
var fact = cacher({'0' : 1}, function (func, n) { return n*func(n-1);});
var fibo = cacher({'0' : 0, '1' : 1}, function (func, n) { return func(n-1) + func(n-2);});
//console.log(fact(10));console.log(fibo(5));
Curry – 새로운 함수 정의function calculate(a, b, c) { return a * b + c;}
function curry(func) { var args = Array.prototype.slice.call(arguments, 1);
return function () { return func.apply(null, args.concat(Array.prototype.slice.call(arguments))); }}
var new_func1 = curry(calculate, 1);console.log(new_func1(2, 3));var new_func2 = curry(calculate, 1, 3);console.log(new_func2(3));
Bind - 새로운 함수 정의 var print_all = function (arg) { for (var i in this) { console.log(i + " : " + this[i]); } for (var j in arguments) { console.log(j + " : " + argu-ments[j]); }}
var myobj = {name: "zzoon"};
var myfunc = print_all.bind(myobj);myfunc();
var myfunc1 = print_all.bind(myobj, 'yb', 'others');myfunc1('insidejs');
Wrapper 함수 구현function wrap(object, method, wrapper) { var fn = object[method]; return object[method] = function () { return wrapper.apply(this, [fn].concat(Array.prototype.slice.call(arguments))); };}
Function.prototype.original = function (value) { this.value = value; console.log("value : " + this.value);}
var Mywrap = wrap(Function.prototype, "original", function (orig_func, value) { this.value = 20; orig_func(value); console.log("wrapper value : " + this.value);});
var obj = new Mywrap('yb');
each 함수 구현function each(obj, fn, args) { if(obj.length == undefined) { for (var i in obj) { fn.apply(obj[i], args || [i, obj[i]]); } } else { for (var i = 0; i < obj.length; i++) { fn.apply(obj[i], args || [i, obj[i]]); } } return obj;}
each([1, 2, 3], function (idx, num) { console.log(idx + " : " + num);});
map 함수 구현Array.prototype.map = function (callback) { var obj = this; var value, mapped_value; var A = new Array(obj.length);
for (var i = 0; i < obj.length; i++) { value = obj[i]; mapped_value = callback.call(null, value); A[i] = mapped_value; } return A;};
var arr = [1, 2, 3];var new_arr = arr.map(function (value) { return value * value;});
console.log(new_arr);
reduce 함수 구현Array.prototype.reduce = function (callback, memo) { var obj = this; var value, accumulated_value = 0;
for (var i = 0; i < obj.length; i++) { value = obj[i]; accumulated_value = callback.call(null, accumulated_value, value); } return accumulated_value;};
var arr = [1, 2, 3];
var accumulated_val = arr.reduce(function (a, b) { return a + b;})
console.log(accumulated_val);
참고자료- http://www.ecma-international.org/ecma-262/5.1/
- 자바스크립트 완벽 가이드 데이비드 플래너건- 자바스크립트 핵심 가이드 더글라스 크락포드- 인사이드 자바스크립트 송형주 고현준- JavaScript Patterns 스토얀 스토파노프