본문 바로가기

Language/Java Script

객체지향 프로그래밍

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