0. 프롤로그
- 클래스 기반 언어
· 클래스 객체로 기본적인 형태와 기능을 정의하고, 생성자로 인스턴스를 만들어서 사용할 수 있다.
· 런타임에 인스턴스의 구조를 바꿀 수 없다.
· 장점 : 정확성, 안전성, 예측성
- 프로토타입 기반 언어
· 객체의 자료구조, 메서드 등을 동적으로 바꿀 수 있다.
· 동적 변경 가능
1. 클래스, 생성자, 메서드
- 여러개의 인스턴스를 만들 때 비효율적인 코드
function Person(arg){
this.name = arg;
this.getName = function(){
return this.name;
}
this.setName = function(value){
this.name = value;
}
}
var me = new Person("zzoon");
console.log(me.getName()); // 출력 결과 : zzoon
me.setName("iamhjoo");
console.log(me.getName()); // 출력 결과 : iamhjoo
- 개선한 코드
function Person(arg){
this.name = arg;
}
Person.prototype.getName = function(){
return this.name;
}
Person.prototype.setName = function(value){
this.name = value;
}
var me = new Person("me");
var you = new Person("you");
console.log(me.getName());
console.log(you.getName());
- 메소드 생성 함수 사용
Function.prototype.method = function(name, func){
if(!this.prototype[name])
this.prototype[name] = func;
}
}
function Person(arg){
this.name = arg;
}
Person.method("setName", function(value){
this.name = value;
});
Person.method("getName", function(){
return this.name;
});
var me = new Person("me");
var you = new Person("you");
console.log(me.getName());
console.log(you.getName());
함수를 생성자로 사용하여 프로그래밍하는 것을 추천하지 않는다. 그 이유는 생성된 함수는 new로 호출될 수 있을 뿐만 아니라, 직접 호출도 가능하기 때문이다. 여기서 문제는 new로 호출될 때와 직접 호출될 때의 this에 바인딩 객체가 달라진다는 것이다.
생성자로 사용되는 함수는 첫글자를 대문자로 표기하라!
2. 상속
- 클래스 기반 상속
- 프로토타입을 이용한 상속
1) 프로토타입을 이용한 상속
function create_object(o){
function F(){}
F.prototype = O;
return new F();
}
create_object()함수는 인자로 들어온 객체를 부모로 하는 자식 객체를 생성하여 반환한다.
이렇게 프로토타입의 특성을 활용하여 상속을 구현하는 것이 프로토타입 기반의 상속이다.
ex)프로토타입을 이용한 상속 예
var person = {
name : "zzoon",
getName : function(){
return this.name;
},
setName : function(arg){
this.name = arg;
}
};
function create_object(o){
function F(){};
F.prototype = o;
return new F();
}
var student = create_object(person);
student.setName("me");
console.log(student.getName()); // 출력값 : me
- 프로토타입 상속의 특징
· 클래스에 해당하는 생성자 함수를 만들지 않았고, 그 클래스의 인스턴스를 따로 생성하지도 않았다.
- jQuery의 extend()함수로 자신이 원하는 객체나 함수를 추가할 수 있다.
jQuery.extend = jQuery.fn.extend = function(obj, prop){
if(!prop){prop = obj; obj = this;}
for(var i in prop ) obj[i] = prop[i];
return obj;
};
jQuery.fn 은 jQuery.prototype이다.
jQuery.extend = jQuery.fn.extend = function(obj, prop)
=> jQuery 함수 객체와 jQuery 함수 객체의 인스턴스 모두 extend 함수가 있겠다는 말이다.
즉, jQuery.extend()로 호출 할 수 있고, var elem = new jQuery(..); elem.extend(); 형태로도 호출할 수 있음을 뜻한다.
if(!prop){prop = obj; obj = this; }
인자가 하나만 들어오는 경우에는 현재 객체(this)에 인자로 들어오는 객체의 프로퍼티를 복사함을 의미하고,
두 개가 들어오는 경우에는 첫 번째 객체에 두 번째 객체의 프로퍼티를 복사하겠다는 것을 뜻한다.
for(var i in prop)obj[i] = prop[i];
루프를 돌면서 prop의 프로퍼티를 obj로 복사한다.
- extend()함수 deep copy하기
for(; i<lenth; i++){
//Only deal with non-null/undefined values
if((options = arguments[i])!=null){
//Extend the base object
for(name in options){
src = target[name];
copy = options[name];
//prevent never-ending loop
if(target === copy){
continue;
}
//Recurse if we're merging plain objects or arrays
if(deep && copy &&
(jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)))){
if(copyIsArray){
copyIsArray = false;
clone = src && jQuery.isArray(src) ? src : [];
}else{
clone = src && jQuery.isPlainObject(src) ? src : {};
}
// Never move original objects, clone them
target[name] = jQuery.extend(deep, clone, copy);
//Don't bring in undefined values
}else if(copy !== undefined){
target[name] = copy;
}
}
}
//Return the modified objects
return target;
}
- jQuery 플러인을 만드는데 사용되는 extend 함수
내가 구현한 함수를 extend 함수를 이용하여 jQuery 객체에 집어넣을 수 있다.
(function($){
$.extend( $.fn, {
my_func: function(){
.....
}
});
})(jQuery);
extend 함수로 사용자는 자신이 정의한 my_func 함수를 jQuery 함수 객체에 추가할 수 있다.
그러면 사용자는 다음과 같이 자신의 함수를 호출할 수 있다.
$.my_func();
- extend() 함수를 예제에 적용
var person = {
name : "zzoon",
getName : function(){
return this.name;
},
setName : function(){
this.name = arg;
}
};
function create_object(o){
function F(){};
F.prototype = o;
return new F();
}
function extend(obj, prop){
if(!prop){prop = obj; obj = this;}
for(var i in prop) obj[i] = prop[i];
return obj;
};
var student = create_object(person);
var added = {
setAge : function(age){
this.age = age;
},
getAge : function(){
return this.age;
}
};
extend(student, added);
student.setAge(25);
console.log(student.getAge());
2) 클래스 기반의 상속
function Person(arg){
this.name = arg;
}
Person.prototype.setName = function(value){
this.name = value;
};
Person.prototype.getName = function(){
return this.name;
};
function Student(arg){
Person.apply(this, arguments); // 부모생성자가 호출되지않아 초기화가 되지 못함
}
var you = new Person("iamhjoo");
Student.prototype = you;
var me = new Student("zzoon");
me.setName("zzoon"); // apply를 안했을 경우 이 타이밍에 초기화 된다.
console.log(me.getName());
위와 같은 코드는 자식 클래스와 부모 클래스의 인스턴스가 독립적이지 못하다.
자식클래스의 prototype이 부모클래스의 인스턴스를 참조한다. 이 구조는 자식 클래스의 prototype에 메소드를 추가할 때 문제가 된다. 이는 부모 클래스의 인스턴스 you와 자식 클래스의 인스턴스 me가 독립적이어야 함을 의미한다.
- 프로토타입 사이에 중계자 만들기
function Person(arg){
this.name = arg;
}
Function.prototype.method = function(name, func){
this.prototype[name] = func;
}
Person.method("setName", function(value){
this.name = value;
});
Person.method("getName", function(){
return this.name;
});
function Student(arg){
}
function F(){};
F.prototype = Person.prototype;
Student.prototype = new F();
Student.prototype.constructor = Student;
Student.super = Person.prototype;
var me = new Student();
me.setName("zzoon");
console.log(me.getName());
- 상속관계를 즉시 실행 함수와 클로저를 활용하여 최적화된 함수를 소개
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;
}
}();
앞 코드에서 클로저(반환되는 함수)는 F()함수를 지속적으로 참조하므로, F()는 가비지 컬렉션의 대상이 되지 않고 계속 남아 있다. 이를 이용해 함수 F()의 생성은 단 한번 이루어지고, inherit함수가 계속해서 호출되어도 함수 F()의 생성을 새로 할 필요가 없다.
3. 캡슐화
- 캡슐화란 기본적으로 관련된 여러 가지 정보를 하나의 틀 안에 담는 것을 의미한다.
· 멤버변수와 메서드가 서로 관련된 정보가 되고 클래스는 이것을 담는 하나의 큰 틀이라고 할 수 있다.
- 자바스크립트의 정보 은닉
var Person = function(arg){
var name = arg ? arg : "zzoon";
this.getName = function(){
return name;
}
this.setName = function(arg){
name = arg;
}
};
var me = new Person();
console.log(me.getName());
me.setName("iamhjoo");
console.log(me.getName());
console.log(me.name); // (출력값) undefined
private 멤버로 name을 선언하고, public 메서도르 getName()롸 setName()을 선언하였다.
즉, this 객체의 프로퍼티로 선언하면 외부에서 new 키워드로 생성한 객체로 접근할 수 있다.
하지만 var로 선언된 멤버들은 외부에서는 접근이 불가능하다.
public 메소드가 클로저 역할을 하면서 private 멤버인 name에 접근할 수 있다.
- 코드를 더 깔끔하게 수정
var Person = function(arg){
var name = arg ? arg : "zzoon";
return {
getName : function(){
return name;
},
setName : function(arg){
name = arg;
}
};
}
var me = Person(); /* or var me = new Person(); */
console.log(me.getName());
- private 멤버가 객체나 배열이면 얕은 복사로 참조만을 반환하므로 사용자가 이 후 이를 쉽게 변경할 수 있다.
var ArrCreate = function(arg){
var arr = [1,2,3];
return {
getArr : function(){
return arr;
}
};
}
var obj = ArrCreate(); /* or var me = new Person(; */
var arr = obj.getArr();
arr.push(5);
console.log(obj.getArr()); // 출력 결과 [1, 2, 3, 5]
- 반환받은 객체는 Person 함수 객체의 프로토타입에는 접근할 수 없다는 단점이 있다.
var Person = function(arg){
var name = arg ? arg : "zzoon";
var Func = function(){}
Func.prototype = {
getName : function(){
return name;
},
setName : function(arg){
name = arg;
}
};
return Func;
}();
var me = new Person();
console.log(me.getName());
'Language > Java Script' 카테고리의 다른 글
함수형 프로그래밍 (0) | 2020.03.05 |
---|---|
객체지향 프로그래밍 응용 예제 (0) | 2020.03.05 |
자바스크립트 클로저 (0) | 2020.02.24 |
자바스크립트 스코프 체인 (0) | 2020.02.24 |
자바스크립트 실행 컨텍스트 (0) | 2020.02.24 |