프론트엔드스터디 e05 js closure oop

38
프프프프프 프프프 CH.05. Closure, OOP

Upload: young-beom-rhee

Post on 16-Apr-2017

10.016 views

Category:

Engineering


4 download

TRANSCRIPT

Page 1: 프론트엔드스터디 E05 js closure oop

프론트엔드 스터디CH.05. Closure, OOP

Page 2: 프론트엔드스터디 E05 js closure oop

1. Closure 와 가까워지기

2. 객체지향과 프로토타입

Page 3: 프론트엔드스터디 E05 js closure oop

Closure

함수를 선언할때 만들어지는 유효범위

함수는 클로저를 통해서 자신이 선언될 때 속해 있던 유효 범위 내의 변수와 함수를 사용할 수 있고 , 변수의 경우 그 값을 변경할 수도 있다 .

흥미로운건 함수를 선언한 후에 언제든지 , 심지어 함수가 속해 있던 유효 범위가 사라진 후에도 그 함수를 호출할 수 있는 경우가 생기게 된다

Page 4: 프론트엔드스터디 E05 js closure oop

Closure – 간단한 클로저

var outerValue = 'outerValue';

function outerFunction() { console.log(outerValue == 'outerValue');}

outerFunction();

외부에 있는 변수와 함수가 모두 전역 유효 범위에 선언되어있고 , 실제로는 클로저에 해당하는 전역 유효 범위는 페이지가

로드되어 있는 한 사라지지 않기 때문에 그다지 흥미로운 코드는 아니다 .

Page 5: 프론트엔드스터디 E05 js closure oop

Closure – 간단하지 않은 클로저

var outerValue = 'outerValue';

var later;

function outerFunction() {

var innerValue = 'innerValue'

function innerFunction() { console.log(outerValue); // innerValue 는 있을까 ? console.log(innerValue); }

later = innerFunction;}

outerFunction();

later();

클로저는 함수가 선언된 시점의 유효 범위에 있는 모든 함수와 변수를 가지고

있으며 , 필요할 때 그것들을 사용할 수있다 .

Page 6: 프론트엔드스터디 E05 js closure oop

Closure – 심화

var outerValue = 'outerValue';

var later;

function outerFunction() { // 함수 내에 변수를 하나 선언한다 . 이 변수의 유효 범위는 함수 내부로 제한이 되고 , // 함수 외부에서는 접근할 수 없다 . var innerValue = 'innerValue'

// inner 함수에 매개변수를 추가한다 . function innerFunction(paramValue) { console.log(outerValue); console.log(innerValue); console.log(paramValue); // 매개변수를 볼 수 있는지 테스트 console.log(tooLate); // 클로저가 함수가 선언된 이후에 정의된 변수를 볼 수 있는지 테스트 }

later = innerFunction;}

console.log(tooLate); // outer closure 에서는 tooLate 를 쓸 수 없다 .

// innerFunction 을 정의한 후에 변수를 선언var tooLate = 'tooLate';outerFunction();

later('paramValue');

Page 7: 프론트엔드스터디 E05 js closure oop

Closure – Closure 를 사용하지 않고 값을 가지고 있을 때

var uniqueId = function(){ if(!arguments.callee.id){ arguments.callee.id = 0; } return arguments.callee.id++;}

uniqueId(); // 0uniqueId(); // 1uniqueId(); // 2

// id 를 0 으로 초기화 할 수 있을까 ?

Page 8: 프론트엔드스터디 E05 js closure oop

var uniqueId = function(){ if(!arguments.callee.id){ arguments.callee.id = 0; } return arguments.callee.id++;}

uniqueId(); // 0uniqueId(); // 1uniqueId(); // 2

// id 를 0 으로 초기화 할 수 있을까 ?uniqueId.id = 0;

uniqueId(); // 0

Closure – Closure 를 사용하지 않고 값을 가지고 있을 때

Page 9: 프론트엔드스터디 E05 js closure oop

Closure – Closure 를 사용해서 값을 가지고 있을 때

var uniqueId = (function(){ var id = 0; return function(){ return id++; }})();

uniqueId(); // 0uniqueId(); // 1uniqueId(); // 2uniqueId(); // 3

// id 를 0 으로 초기화 할 수 있을까 ?

Page 10: 프론트엔드스터디 E05 js closure oop

var uniqueId = (function(){ var id = 0; return function(){ return id++; }})();

uniqueId(); // 0uniqueId(); // 1uniqueId(); // 2uniqueId(); // 3

// 0 으로 초기화uniqueId.id = 0 // ?uniqueId(); // 4

Closure – Closure 를 사용했을 때

Closure 의id 에는 접근할

수 없다x

=> Closure 를 통해 모듈화를 할 수 있다 .

Page 11: 프론트엔드스터디 E05 js closure oop

Closure – 응용 : Private 변수

function DtoObj(initVal) { var val = initVal;

this.getVal = function () { // this 는 새로 만들어진 객체를 가리킴 return val; };

this.setVal = function(pVal) { val = pVal; }}

var dto = new DtoObj(3);

// dto 객체의 내부 변수는 getter 를 통해서만 접근하여 값을 받을 수 있다 .console.log(dto.getVal()); // 3// dto 객체의 내부 변수는 setter 를 통해서만 접근하여 값을 바꿀 수 있다 .dto.setVal(4);console.log(dto.getVal()); // 4dto.val = 5;console.log(dto.getVal()); // 4

Page 12: 프론트엔드스터디 E05 js closure oop

Closure – 응용 : success callback

<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Closure 예제 </title></head><body> <button type="button" id="testButton">Go!</button> <div id="testSubject"></div>

<script src="https://code.jquery.com/jquery-2.2.3.js"></script> <script> $('#testButton').click(function () { var elem$ = $('#testSubject');

elem$.html(' 로딩 중 ...'); $.ajax({ url: '/test' , success: function (data) { console.dir(this.success); elem$.html(JSON.stringify(data)); } }); }); </script></body></html>

Page 13: 프론트엔드스터디 E05 js closure oop

Closure 핵심 정리

1. 내부 함수는 외부 스코프에 선언된 변수를 참조할 수 있다 .

2. 클로저는 자신을 생성한 함수보다 더 오래 지속된다 .

3. 클로저는 내부적으로 외부 변수에 대한 참조를 저장하고 , 저장된 변수를 읽고 갱신할 수 있다 .

Page 14: 프론트엔드스터디 E05 js closure oop

Javascript 에는 자바 , C++, C# 등의 class 가없다 .

Prototype 기반 언어들에 영감을 받음 . (https://ko.wikipedia.org/wiki/ _ _프로토타입 기반 프로그래밍)

JavaScript 는 prototype 객체 , 생성자 등을 통해 구현 가능

클래스 기반 vs 프로토타입기반

Page 15: 프론트엔드스터디 E05 js closure oop

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 === ?

Page 16: 프론트엔드스터디 E05 js closure oop

this === func=> 인스턴스를 바라본다

Page 17: 프론트엔드스터디 E05 js closure oop

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.

Page 18: 프론트엔드스터디 E05 js closure oop

function GoodWaffle() { if(!(this instanceof GoodWaffle)) { return new GoodWaffle(); } this.tastes = 'yummy';}

var waffle = new GoodWaffle();var waffle2 = new GoodWaffle();

console.log(waffle instanceof GoodWaffle);console.log(waffle2 instanceof GoodWaffle);

New 를 강제하는 방법

GoodWaffle.call(new GoodWaffle);

Page 19: 프론트엔드스터디 E05 js closure oop

var GoodWaffle = (function(){ var RealWaffle = function(){}; RealWaffle.prototype.xxx = xxx; return function(){ return new RealWaffle(); }})();

var waffle = new GoodWaffle();var waffle2 = new GoodWaffle();

New 를 강제하는 방법 - 더블쉐도우

Page 20: 프론트엔드스터디 E05 js closure oop

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 메서드는 Person 에 추가 되었을까 ? 아니면 me 에만

추가 되었을까 ?

Page 21: 프론트엔드스터디 E05 js closure oop

중복되는 영역을 메모리에 올려놓고 사용-> 메모리 낭비

var personA = new Person("personA");var personB = new Person("personB");var personC = new Person("personC");

불필요한 중복

Page 22: 프론트엔드스터디 E05 js closure oop

personA.getName = function () { return 'Hello, ' + this.name;}

console.log(personA.getName());console.log(personB.getName());console.log(personC.getName());

변경이 발생했을 때

Hello, personApersonBpersonC

변경된 사항이 적용되지 않는다 .

Page 23: 프론트엔드스터디 E05 js closure oop

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 마다 개별적으로 가져야 할 프로퍼티

공통으로 가져야 할프로퍼티

Page 24: 프론트엔드스터디 E05 js closure oop

cf. Prototype 을 사용하지 않은 경우

Prototype 참조

Page 25: 프론트엔드스터디 E05 js closure oop

변경이 발생했을 때

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 에도 모두 적용

Page 26: 프론트엔드스터디 E05 js closure oop

Prototype 의 몇 가지 속성

name : name / hasOwnProperty : truename : getName / hasOwnProperty : falsename : setName / hasOwnProperty : false

for (var obj in personA) { console.log('name : ', obj, ' / hasOwnProperty : ' + personA.hasOwnProperty(obj));}

- 마치 자신의 속성처럼 열거할 수 있다 .(for in 사용 )- hasOwnProperty() 메서드를 사용해 소유여부 ( 자신이

소유하고 있는지 , prototype 으로 참조하는지 ) 를 확인할 수 있다 .

Page 27: 프론트엔드스터디 E05 js closure oop

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; // PI 재정의console.log(r3Alien.getArea());

내부의 property 가 있는 경우에 prototype 을 참고하지 않는다 . 이런 경우를 다른 property 가 가렸다 (shadows) 혹은 숨겼다

(hides) 라고 한다

- Prototype property 가 참조되지 않을 수 있다 .

Page 28: 프론트엔드스터디 E05 js closure oop

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 을 방지할 수 있다

Page 29: 프론트엔드스터디 E05 js closure oop

Function 의 용도가 2 가지: 실행을 위한 함수 , 생성자 함수

혼란을 피하기 위해 생성자 함수는 대문자로 시작하도록 권고

크락포드옹님 주의사항

Page 30: 프론트엔드스터디 E05 js closure oop

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 기반의 생성자처럼 생성하는 시점에 초깃값을 주고 싶다면 ?

상속을 구현

Page 31: 프론트엔드스터디 E05 js closure oop

// 초기값을 넣고 싶은 경우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());

- 초기값을 주고 싶은 경우

Page 32: 프론트엔드스터디 E05 js closure oop

재정의 , 기능확장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 메서드를 사용

Page 33: 프론트엔드스터디 E05 js closure oop

슈퍼 클래스와 서브 클래스

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

Page 34: 프론트엔드스터디 E05 js closure oop

클래스 방식의 상속패턴 완성// 라이브러리로var inherit = (function () { var F = function () {}; return function (C, P) { F.prototype = P.prototype; C.prototype = new F(); C.uber = P.prototype; C.prototype.constructor = C; }});

inherit(PositionedRectangle, Rectangle);var positioned_rect = new PositionedRectangle(1, 2, 3, 4);console.log(positioned_rect);

Page 35: 프론트엔드스터디 E05 js closure oop

ES6 Class 사용class Rectangle { constructor(w, h) { this.width = w; this.height = h; } area() { return ' 넓이 : ' + this.width * this.height; }}

class PositionedRectangle extends Rectangle { constructor(x, y, w, h) { super(w, h); this.x = x; // 사각형의 좌표를 저장한다 . this.y = y; } getPosition() { return 'x : ' + this.x + ' / y : ' + this.y; }}

console.log( positioned_rect.getPosition());console.log( positioned_rect.area());

console.log(positioned_rect instanceof PositionedRectangle && positioned_rect instanceof Rectangle && positioned_rect instanceof Object);

Page 36: 프론트엔드스터디 E05 js closure oop

생성자 체이닝

function PositionedRectangle(x, y, w, h) { this.superclass(w, h); this.x = x; this.y = y;}

// 상위 클래스 생성자에 대한 참조를 저장한다 .PositionedRectangle.prototype.superclass = Rectangle;

처음의 예제처럼 call 이나 apply 를 사용하지 않아도된다 .

생성자 함수에서 상위 클래스의 생성자 함수를 명시적으로 호출할때 아래와 같은 방법도 가능

Page 37: 프론트엔드스터디 E05 js closure oop

메서드 재정의

서브 클래스에서 재정의하는 메서드는 기존 메서드에 있던 기능을 완전히 교체하기보다 확장시키는 것이 좋다 .

// 메서드 재정의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());

Page 38: 프론트엔드스터디 E05 js closure oop

참고자료

- http://www.ecma-international.org/ecma-262/5.1/

- 자바스크립트 완벽 가이드 데이비드 플래너건

- 자바스크립트 핵심 가이드 더글라스 크락포드

- 인사이드 자바스크립트 송형주 고현준

- JavaScript Patterns 스토얀 스토파노프