컴퓨터 공학/프로그래밍 과외 기록

[자바스크립트 과외] 9. Day4 요점정리

ITISIK 2021. 7. 25. 18:27
반응형


[객체로서의 함수]

  자바스크립트에서의 함수는 Function이라는 객체이다. 객체의 특징을 그대로 따르기 때문에, 함수는 속성과 메서드를 갖는다. 또한 함수는 다음과 같은 특징들을 갖는다.

 

1. 함수는 변수나 프로퍼티나 배열 요소에 대입할 수 있다.

2. 함수는 함수의 인수로 사용할 수 있다.

3. 함수는 함수의 반환값으로 사용할 수 있다.

4. 함수는 프로퍼티와 메서드를 가질 수 있다.

5. 함수는 이름없는 리터럴로 표현할 수 있다. (익명 함수)

6. 함수는 동적으로 생성할 수 있다.

 

  이러한 특징을 가진 객체를 가리켜 일급객체라고 하고, 일급 객체인 함수를 일급 함수라고 한다. 자바스크립트 함수는 일급함수이기 때문에, 함수형 프로그래밍을 할 수 있다.

 

Function객체의 프로퍼티는 아래와 같다.

프로퍼티 이름 설명
caller 현재 실행 중인 함수를 호출한 함수
length 함수의 인자 개수
name 함수를 표시할 때 사용하는 이름
prototype 프로토타입 객체의 참조
constructor Function 생성자의 참조

 

메서드 이름 설명
apply() 선택한 this와 인수를 사용하여 함수를 호출한다. 인수는 배열이다.
call() 선택한 this와 인수를 사용하여 함수를 호출한다. 인수는 쉼표로 구분한 값(CSV)이다.
bind() 선택한 this와 인수를 적용한 새로운 함수를 반환한다.
toString() 함수의 소스코드를 문자열로 만들어 반환한다.

 

e.g.

function hello(arg1, arg2){

    console.log(arg1 + " " + arg2 + this.name);

}

const tom = {name : "Tom"};

const becky = {name : "Becky"};

 

hello.apply(tom, ["안녕", "내 이름은 "]);

hello.call(becky, "안녕", "내 이름은 ");

 

const sayToTom = hello.bind(tom);

sayToTom("안녕 ", "내 이름은 ");

 

함수의 프로퍼티로 유사배열 객체를 활용하여 메모이제이션을 구현하는 코드를 보았다(8-13/ 8-14)

 

[고차함수]

고차함수란, 함수를 인수로 받거나, 반환값으로 뱉는 함수를 말한다.

 

[콜백함수]

  콜백함수란, 특정조건(함수의 실행이 끝나는 등)이 충족됐을 때 실행시킬 함수를 의미한다. 이는 앞서서 배웠던 setInterval 함수나, 이후에 배울 addEventListener에 인수로 넘겨주는 함수등이 콜백함수이다.

 

[화살표 함수]

함수 리터럴 방식으로 함수를 정의하는 코드는 아래와 같다.

const func = function(x) { return x * x; };

이를 화살표 함수 표현식으로 변경하면 아래와 같다.

const func = (x) => { return x * x; };

인수가 하나면 괄호를 생략할 수 있다.

const func = x => { return x * x; };

인수가 없으면 괄호를 생략할 수 없다.

const func = () => { return 9; };

함수 몸통의 내용이 return문 뿐이라면, 생략할 수 있다.

const func = x => x * x;

함수 몸통의 내용이 return문 뿐이라고 하더라도, 객체 반환시에는 괄호로 묶어야 한다.

const func = x => ( { key : x } );

 

화살표 함수와 함수 리터럴 방식의 차이점

1. this의 값

- 함수 리터럴의 경우 함수를 호출할 때 this의 값이 정해진다.

- 화살표 함수의 경우 함수를 정의할 때 this의 값이 정해진다.

화살표 함수는 call()이나 apply() 메서드를 통해서 this를 바꾸려고 해도 먹히지 않는다.(이미 함수를 정의할 때 this의 값이 정해졌기 때문.)

 

2. arguments 변수의 존재 여부

화살표 함수 내부에는 유사배열 객체인 arguments 변수를 사용할 수 없다.

 

3. 생성자로 사용할 수 없음

new 연산자를 활용하여 생성자로서 사용할 수 없다.

 

4. yield 키워드 사용 여부

yield 키워드를 사용할 수 없기 때문에, 제너레이터로서 사용할 수 없다.

 

[n개의 인수를 배열로 받기] - ES6

  함수 정의시 인수가 들어오는 부분에 ...을 입력하면 배열로 받을 수 있다. 이러한 표기법을 나머지 매개변수(rest parameters)라고 부른다.

e.g.

function(a, b, ...args){

    console.log(a, b, args);

}

이와 같은 나머지 매개변수를 활용하면 화살표 함수에서는 쓸 수 없는 arguments 변수를 대체할 수 있다.

 

[인수의 기본값] - ES6

  함수의 인수의 기본값(초기값)을 할당해줄 수 있다. 인수가 undefined이거나 생략되어 있으면 기본값이 할당되어 함수가 동작되게 된다.

e.g.

function add(a = 1, b = 1){

    console.log(a + b);

}

 

인수를 할당의 우변에 적을 수도 있다.

function add(a = 1, b = a + 1){

    console.log(a + b);

}

 

[이터레이터와 for/of문] - ES6

이터레이션이란 반복 처리 자체를 의미한다.

e.g.

const arr = [1, 2, 3, 4, 5];

arr.forEach( function(value){ console.log(value); } );

 

이터레이터란, 반복처리가 가능한 객체를 말한다.

배열은 Symbol.iterator() 메서드를 가지고 있다. 이를 @@iterator라고 표기하고 이터레이터 심벌이라고 읽는다.

e.g.

const arr = [1, 2, 3, 4, 5];

const iter = arr[Symbol.iterator]();

console.log(iter.next()); //{value: 1, done: false}

console.log(iter.next()); //{value: 2, done: false}

console.log(iter.next()); //{value: 3, done: false}

console.log(iter.next()); //{value: 4, done: false}

console.log(iter.next()); //{value: 5, done: false}

console.log(iter.next()); //{value: undefineddone: true}

 

  이와 같이 Symbol.iterator() 메서드를 가지고 있는 객체를 반복 가능(iterable)한 객체라고 한다. 아래 생성자로 생성한 내장 객체는 Symbol.iterator() 메서드를 가지고 있다.

e.g. Array, String, TypeArray, Map, Set

 

for( const value of "ABC" ) console.log(value);

[제너레이터] - ES6

  이터레이터(반복처리가 가능한 객체)를 생성하는 것을 제너레이터라고 한다. 제너레리터는 결국 함수인데 다음의 특징을 가진다.

1. 반복 가능한 이터레이터를 값으로 반환한다.

2. 작업의 일시정지와 재시작이 가능하며 자신의 상태를 관리한다.

 

  제너레이터는 function* 문으로 정의한 함수이며, 하나 이상의 yield 표현식을 포함한다. yield는 return과 비슷한 역할을 하는 문장이라고 이해해도 나쁘지 않다.

 

function* gen(){

    yield 1;

    yield 2;

    yield 3;

}

 

const iter = gen();

console.log(iter.next()); //{value: 1, done: false}

console.log(iter.next()); //{value: 2, done: false}

console.log(iter.next()); //{value: 3, done: false}

console.log(iter.next()); //{value: undefined, done: true}

 

gen()과 같은 제너레이터로 생성한 객체는 반복 가능한 이터레이터이기 때문에, for/of문을 사용할 수 있다.

 

  이터레이터의 next() 메서드에 인수로 넘긴 값은 제너레이터가 일시적으로 정지하기 직전의 yield 표현식의 값으로 사용된다. 이를 활용해서 제너레이터 내부의 흐름을 변경하는 등 상태를 변경할 수 있다. 제너레이터로 생성한 이터레이터의 return 메서드를 실행하면 해당 인수값을 반환한 후에 제너레이터를 종료시켜 버린다. 제너레이터에서 예외를 던질 수도 있고, yield* 표현식을 통해서 반복 가능한 객체를 반환값으로 지정할 수도 있다. 이때에는 해당 객체의 각 요소를 순서대로 반환한다.

 

[객체]

  기본적으로 객체란, Key-Value pair(쌍)으로 이루어진 것을 의미하며, Value의 형태에 따라서 속성과 메서드로 나뉜다. 다만 자바스크립트에서는 속성과 메서드를 싸잡아서 프로퍼티라고 부르는 것을 알 수 있었다. Value에는 원시자료형을 포함한 모든 자료형이 올 수 있었으며, 함수의 참조를 값(Value)으로 가진 프로퍼티를 특별히 메서드라고 불렀다. 자바스크립트로 이러한 객체를 생성하는 방법은 아래와 같이 있다.

 

1. 객체 리터럴로 생성하는 방법

가장 고전적인 방법이며 정적인 방법이다.

const obj = {name : "itisik", age : 28}

 

2. .생성자로 생성하는 방법

Java에서 Class를 활용하는 것과 유사하며, 동적인 방법이다.

function man(name, age){

    this.name = name;

    this.age = age;

}

const itisik = new man( "itisik", 28 );

 

3. Object.craete()로 생성하는 방법

프로퍼티(속성)의 속성까지 명세하는 방법인데, 이는 이번 챕터에서 배울 예정이다.

const itisik = Object.create(Object.prototype, {

                      name : {

                          value : "itisik",

                          writable : true,

                          enumerable : true,

                          configurable : true

                      },

                      age : {

                          value : 28,

                          writable : true,

                          enumerable : true,

                          configurable : true

                      }

                  })

 

  특정 생성자(function)를 가지고 인스터스(Object)들을 찍어내면, 재사용성이 높다는 장점이 있지만 단점도 있다. 단점은 바로 같은 기능을 하는 메서드들이 모든 인스턴스마다 생성되기 때문에 메모리의 공간을 차지하여 낭비된다는 점이다. 즉 개별적인 속성값은 어쩔 수 없이 각 인스턴스들에 정의해준다 하더라도, 공통적으로 사용하는 메서드의 경우에는 단 하나만 만들어두고 이를 참조하도록 하는 것이 메모리 관리 입장에서 좋은데 그 방법에 대해서 알아보려고 한다.

 

// Circle의 생성자. 각 객체가 가질 고유값인 중심좌표와 반지름만을 선언한다.

function Circle(center, radius) {

    this.center = center;

    this.radius = radius;

}

 

// Circle 객체가 상속 받아서 사용하는 영역인 prototype에 area 메서드를 선언하여 메모리 낭비를 줄인다.

Circle.prototype.area = function(){

    return Math.PI * this.radius * this.radius;

}

 

const c1 = new Circle( {x : 0, y : 0}, 2.0 );

const c2 = new Circle( {x : 1, y : 0}, 3.0 );

const c3 = new Circle( {x : 0, y : -1}, 2.5 );

 

[메서드 체인]

  메서드의 반환값이 객체인 경우, 반환된 객체의 메서드를 다시 사용할 수 있는데 이로 인해서 특이한 표기법이 나타날 수 있는데 이를 메서드 체인이라고 한다.

e.g.

객체.메서드1().메서드2().메서드3()

 

[__proto__] - ES6

  현재 주요 브라우저에서는 모두 지원하는 __proto__  프로퍼티는, 그 객체에게 상속을 해 준 부모 객체를 가리킨다. 다음 코드를 통해서 __proto__ 프로퍼티를 직접 지정해줌으로서 상속관계를 만들 수 있음을 배웠고, this 객체의 경우 객체 자기 자신, 부모, 조부모 식으로 올라가며 해당 값을 찾아 나간다는 사실을 다시 한 번 볼 수 있었다.

e.g.

const objA = {

    name : "Tom",

    sayHello : function(){ console.log( "Hello! " + this.name ); }

};

 

const objB = {

    name : "Huck"

};

 

const objC = {};

 

objB.__proto__ = objA;

objC.__proto__ = objB;

objC.sayHello(); //Hello! Huck

 

  이와 같이 프로토타입 상속을 하는 객체 지향 언어를 가리켜 프로토 타입 기반 객체 지향언어라고 한다.

 

  객체의 프로토타입은 obj.__proto__ 로도 가져올 수 있지만, 지원하지 않는 경우에는 Object.getPrototypeOf(obj) 메서드를 통해서 가져올 수도 있다. 이때, 부모 객체를 불러온다는 사실에 주의하자.

 

  우리가 지금까지 객체를 생성할 때마다 사용해오던 new 키워드의 동작의 내부에도 prototype을 설정(초기화)하는 프로세스가 포함되어 있는 것이다.

 

[constructor]

  constructor는 함수 객체의 참조를 값으로 갖고 있다. 함수객체에 국한하여, prototype이 특정 객체가 상속받은 객체를 가리킨다면, constructor는 특정 객체가 어떤 생성자로부터 생성된 함수인지를 가리킨다.

 

[prototype]

  생성자(Function 객체)가 가진 prototype 프로퍼티 값을 새로운 객체로 교체할 때에는 주의해야 한다. 왜냐하면 constructor 프로퍼티 없이 단순히 프로퍼티만 할당해주는 경우, 인스턴스와 생성자 사이의 연결 고리가 끊어지기 때문이다. 인스턴스와 생성자 사이의 연결고리를 유지하려면 prototype으로 할당할 객체에 constructor 프로퍼티를 정의하고 그 프로퍼티에 생성자의 참조를 대입해야 한다.

e.g.

function Circle(center, radius) {

    this.center = center;

    this.radius = radius;

}

 

const c1 = new Circle( {x : 0, y : 0}, 2.0 );

 

Circle.prototype = {

    constructor : Circle,

    area : function(){ return Math.PI * this.radius * this.radius; }

};

 

const c2 = new Circle( {x : 0, y : 0}, 2.0 );

console.log( c2.constructor ); // Fucntion Circle

console.log( c2 instanceof Circle ); //true

 

c2.area() //12.566370614359172

 

하지만 이렇게 constructor를 지정해준다고 하더라도, 인스턴스의 프로퍼티는 생성되는 시점의 프로토타입으로부터 상속 받기 때문에 아래와 같은 에러가 나게 된다.

 

c1.area() //Uncaught TypeError: c1.area is not a function

 

  만약 뒤늦게 추가한 프로퍼티(메서드)를 사용하고 싶다면, 아래 코드와 같이 기존에 가지고 있던 프로토타입 객체에 프로퍼티만 추가하는 컨셉으로 작성하면 된다.

 

e.g.

function Circle(center, radius) {

    this.center = center;

    this.radius = radius;

}

 

const c3 = new Circle( {x : 0, y : 0}, 2.5 );

 

Circle.prototype.area = function(){

    return Math.PI * this.radius * this.radius;

};

 

c3.area(); //19.634954084936208

 

[프로토 타입 체인 확인]

  instaceof 연산자는 지정한 객체의 프로토타입 체인에 지정한 생성자의 프로토타입 객체가 포함되어 있는지 여부를 판정한다.

 

function func() {};

const obj = new func();

console.log(obj instanceof F); //true

console.log(obj instanceof Object); //true

console.log(obj instanceof Date); //false

 

비슷한 역할을 하는 메서드로 prototype.isPrototyepOf() 메서드가 있다.

console.log(F.prototype.isPrototypeOf(obj)); //true

console.log(Object.prototype.isPrototypeOf(obj)); //true

console.log(Date.prototype.isPrototypeOf(obj)); //false

 

[Object.prototype]

Object.prototype의 메서드는 모든 내장 객체로 전파되며, 모든 인스턴스에서 사용할 수 있다. Object 생성자는 객체를 생성하는 것보다는 일반적인 객체를 조작하기 위한 보편적 메서드와 프로퍼티를 제공하는 것에 의의가 있다.

 

[접근자 프로퍼티]

 지금까지 배운 모든 프로퍼티는 값을 저장하기 위한 프로퍼티로, 이를 데이터 프로퍼티라고 부른다. 가진 값 없이 프로퍼티를 읽거나 쓸 때 호출하는 함수를 값 대신에 지정할 수 있는 프로퍼티를 접근자 프로퍼티라고 부른다. 쉽게 말하면 Java에서 객체에 설정하는 getter메서드와 setter메서드를 의미한다.

 

  아래와 같이 getter와 setter를 정의하여 사용할 수 있다는 사실을 배웠다.

 

const person = {

    _name : "Tom",

    get name() {

        return this._name;

    },

    set name(value){

        const str = value.charAt(0).toUpperCase() + value.substring(1);

        this._name = str;

    }

};

 

[데이터의 캡슐화]

위 코드에 클로저 개념을 활용하면, 외부에서 객체의 _name 프로퍼티에 접근할 수 없도록 만들 수도 있다.

 

const person = (function(){

    let _name : "Tom"; // private 변수

    retrun {

        get name(){

            return _name;

        },

        set name(value){

            const str = value.charAt(0).toUpperCase() + value.substring(1);

            _name = str;

        }

    }

})();

반응형