Develop/Node.js

[기초]03. 객체지향 자바스크립트

dawonny 2022. 2. 12. 05:21
728x90
반응형

자바스크립트는 완전한 객체 지향 언어.

객체 지향 하면 제일 먼저 떠올리는 개념은 다음과 같다.

  • 클래스
  • 객체
  • 메소드
  • 속성
  • 캡슐화
  • 집합
  • 재사용
  • 상속
  • 변형

하지만 js 는 자바나 c++ 과는 조금 다르다

자바가 c++ 는 클래스 기반의 언어이고

js 는 프로토타입 기반의 언어이다.


객체지향 컨셉에서

대상의 특성은 객체의 속성 이라고 하며

대상의 동작은 객체의 메소드 라고 함.


클래스의 개념이 사실 js 에서는 존재하지 않는다

js 에서 모든 것들은 객체에 기반을 두고 있다

 

js 에서는 프로토타입 이라는 표기법이 있는데

이를 이용해서 객체가 생성된다

 

ex) 전통적인 객체지향에서는 박지성이라는 새로운 객체 <- 축구 선수라는 클래스를 이용해서 생성

     이라면

     js 같은 prototypal 객체지향 언어에서는 축구선수라는 객체를 재사용하여 이것을 하나의 프로토타입으로서 밑바탕

    에 깔고 박지성이라는 새로운 객체를 생성한다 ...  라는 개념

 


캡슐화 :

객체가 데이터를 속성에 저장하거나 저장한 데이터를 이용하여 무엇인가를 수행하는 메소드를 포함하는 것

== 정보 은닉

 

집합

 

상속 :

부모 클래스의 메소드들 중 하나를 새로이 정의하는 것을 오버라이딩 이라고 함.


생성자 함수 :

생성자 함수를 이용해서 객체를 생성하는 방법의 장점은 새로운 객체를 만들 때 초깃값을 전달하여 생성할 수 있다는 점이다.

 

instanceof 연산자 :

이 연산자를 이용하면 특정 객체가 어떤 생성자를 이용하여 만들어졌는지 테스트할 수있다.

 

Constructor Property(컨스트럭터 속성) :

새로운 객체를 생성하면, 보이지는 않지만 constructor 라는 속성이 생긴다

이것은 객체를 만드는데 어떤 객체를 참조하였는지에 대한 정보를 가지고 있다.

 

player instanceof SoccerPlayer;
// true

player instanceof Object;
// true

내장형 객체 :

자료형을 포함한 다음과 같은 것들이 있다.

  • Object, Number, Array, String, Boolean, Function
  • RegExp : 정규식을 위한 객체입니다.
  • Math : 수학과 관련된 각종 값과 메소드를 내장한 객체입니다.
  • Date : 날짜와 시간에 관련된 메소드를 가진 객체입니다.
  • Error : 자바스크립트에서 발생하는 에러를 처리하기 위한 객체입니다. 

스코프와 호이스팅

 

js 에서 스코프와 클로저는 중요한 개념임

 

유효범위(Scope)란?

작성된 코드를 둘러싼 환경. 어떤 변수들에 접근할 수 있는지를 정의함

global 과 local 스코프로 정의 가능

 

global 스코프는 함수 안에 포함되지 않은 곳에 정의하는 것으로, 코드 어디에서든지 참조 가능

local 스코프는 함수 내에 정의된 것으로, 정의된 함수 내에서만 참조 가능

 

js 의 스코프는 다른 언어와 특징이 조금다른데 function-level scope(함수 레벨 스코프)를 사용한다는 점이다.

대부분 언어는 블록 레벨 스코프를 사용함으로써 변수선언이 코드블록 단위로 유효한데

function-level scope 인 js는 함수 블록 내에서 선언된 변수는 함수 블록 내에서만 유효함.

function foo() {
    if (true) {
        var a = 0; 
        console.log(a);
    }
    console.log(a); 
}

예를 들어 위와같은 상황일때에 js는 a 출력이 가능함

 

단 js ES6 부턴 const와 let 이용해 블록레벨 스코프도 지원하기 때문에

const, let -> block-level

var 같은 전통적인 js 변수들 -> function-level

이라고 할 수 있다.


변수가 함수 바깥이나 { } 바깥에 선언되었으면 global scope 에 정의 되었다고 함.

global 이 더 편하다고 느낄 수 있지만 global scope에 변수선언 안하는게 좋음

변수 이름이 충돌할 수 있기 때문!!

 


유효 범위 체인(Scope Chain)

 

함수 단위의 범위라면, 함수 안에 함수가 있으면 어떻게 될까?

var a = 1;

function outer() {
	var b = 2;
	console.log(a); // 1
	
	function inner() {
		var c = 3;
		console.log(b);
		console.log(a); 
	}
	
	inner();  // 2 1
}
outer();

console.log(c);  // c is not defined

찬찬히 보면 당연한건데,

a 는 전역공간에서 선언되었으니 inner 나 outer에서 사용할 수 있지만 

c는 함수 내부 블록에서 선언되었으니 outer 함수에서도 c는 사용 못함

 

근데 inner 에서는 a 랑 b 다 사용가능.

inner 에서는 a 변수를 참조할 때 일단 자신의 scope에서 a를 찾고 

없으면 outer 함수의 scope에서 찾고

없으면 전역범위 G로 올라가 a 를 찾는다.

 

이렇게 체인처럼 상위 scope 를 참조해서 스코프 체인이라고 이름이 붙여짐.


정적 범위(Lexical scope)

 

함수를 어디서 호출하는지가 아니라

어떤 스코프에 선언했는지에 따라 결정된다 라는것

var text = 'global';

function foo() {
	console.log(text);
}

function bar() {
	var text = 'bar';
	foo();
}

bar(); // 무엇이 출력될까요?

실행하면 global 이 출력된다

foo에서 출력한 text 는 전역변수 text 를 가리키기 때문

 

foo가 한번 저렇게 선언되었다면 전역변수 text를 참조하는 것을 바꿀순 없음

text 의 값을 bar로 바꾸고 싶다면 bar 안에서 지역변수 text 를 선언하지말고

아예 전역변수 text 의 값을 bar로 바꿔야함

var text = 'global';

function foo() {
    console.log(text);
}

function bar() {
    text = 'bar';
    foo();
}

bar();

호이스팅(Hoisting)

 

사전적 의미는 끌어올리기

함수 안에서 변수를 선언할 때 어떤 위치에 있든 함수의 시작 위치로 끌어올리는 현상

function foo() {
	console.log(a);  // undefined
	var a = 100;
	console.log(a);  // 100
}

foo();

js 에서는 이 코드에서 a 를 제일 위에서 선언해주지 않았는데도 에러 발생 안함.

대신 undefined 가 출력됨.

function foo() {
	var a;
	console.log(a);  // undefined
	var a = 100;
	console.log(a);  // 100
}

foo();

이거와 같다고 할 수 있음.

 

함수 호이스팅도 마찬가지긴 한데,

일단 함수는 호출하기 전에 제일 최상단에 선언하는 습관을 들이면 좋음.


클로저 :

외부 함수의 실행이 끝나고 외부함수가 소멸된 이후에도 내부 함수가 외부 함수의 변수에 접근할 수 있는 구조

-> Node.js 가 높은 효율성을 가지게 해주는 근간이 됨!

 

js 에서 함수를 호출할 때 이전에 쓰던 값을 유지하고 싶으면 클로저를 사용한다

아래를 보자

var num = 1;

function foo() {
	var num = 2;
	
	function bar() {
		console.log(num);
	}
	return bar;
}

var baz = foo();
baz();

foo 함수가 리턴되어 사라지고

내부함수 bar가 생성되는 건가

여전에 bar가 foo의 지역변수에 접근이 가능할까?

-> 가능!

 

이렇게 외부함수가 리턴되어 사라져야하는데 사라지지 않고 

내부함수의 참조로 인해 값을 유지하게 되는 것을 클로저라고 부름

정확히는 내부함수 == 클로저 함수

function f(arg) {
	var n = function() {
		return arg; 
	}
	arg++;
	return n; 
}

var m = f(123); 
console.log(m());

위 코드도 한번 보자

함수 n은 함수 f의 범위에 있는 것을 참조한다

그래서 함수 f에서 모든 처리가 끝나고 나서야 함수 n이 처리된다

(단순하게 함수 n에서 arg 리턴했다고 n이 123이 되고 끝나는게 아님)

function f() {
    var a = [];
    var i;
	
    for(i = 0; i < 3; i++){
      a[i] = function() {
        return i;
        }
    }
    return a;
  }
  
  var b = f();

  console.log( b[0]() ); 
  console.log( b[1]() ); 
  console.log( b[2]() );

예시 하나만 더!

실행해보면 333 이 출력되는데

a[i] = function() {
    return i;
}

이건 함수 선언만 된거고 실행되는건 console.log 부분이어서

var b = f();

문장에서 for 문 다 돌아가고 나서야 실제 참조가 이루어 진다

즉, 클로저는 그 순간 값을 저장하는게 아니라 

연결된 함수 범위에서 최종 처리된 값을 가지게 된다.


클로저를 사용하면 함수를 호출할 때마다 기존에 생성했던 값을 유지할 수 있어서

전역변수의 잘못된 사용없이 깔끔한 코드작성을 할 수 있다.

하지만 클로저로 참조하는 변수는 프로그램 종료시까지 계속 메모리에 할당되어 있어서

메모리 누수로 인해 성능저하될 수도 있으니 조심!

728x90
반응형