JavaScript - call, apply, bind, 상속, Prototype, class

dev_jubby·2022년 10월 9일
0

JavaScript

목록 보기
5/6
post-thumbnail

📚 복습

function fn(){
	console.log(3)
}
setTimeout(fn, 3000);

위와 같인 코드

setTimeout(function() {
	console.log(3)
}, 3000); 



✨ call, apply, bind

call, apply, bind: 함수 호출 방식과 관계없이 this를 지정할 수 있음


call

call 메서드는 모든 함수에서 사용할 수 있으며, this를 특정값으로 지정할 수 있다.

const jubby = {
  name: "jubby",
};

const ohwa = {
  name: "ohwa",
};

function showThisName() {
  console.log(this.name); // 여기서 this = window
}

showThisName(); // 아무것도 뜨지 않는다.
showThisName.call(jubby); // jubby, this가 jubby가 된다.

function update(birthYear, occupation) {
  	this.birthYear = birthYear;
	this.occupation = occupation;
}       

update.call(jubby, 1998, 'dancer'); // jubby = this, 뒤부터는 매개변수 값.
console.log(jubby);

apply

apply는 함수 매개변수를 처리하는 방법을 제외하면 call과 완전히 같다. call은 일반적인 함수와 마찬가지로 매개변수를 직접 받지만, apply는 매개변수를 배열로 받는다.

const jubby = {
  name: "jubby",
};

const ohwa = {
  name: "ohwa",
};

function showThisName() {
  console.log(this.name); // 여기서 this = window
}

showThisName(); // 아무것도 뜨지 않는다.
showThisName.call(jubby); // jubby, this가 jubby가 된다.

function update(birthYear, occupation) {
  	this.birthYear = birthYear;
	this.occupation = occupation;
}       

update.apply(jubby, [1998, 'dancer']);
console.log(jubby);
const minNum = Math.min(3, 10, 1, 6, 4);
const maxNum = Math.max(3, 10, 1, 6, 4);

console.log(minNum); // 1
console.log(maxNum); // 10


// 배열에선 사용이 안된다.
const nums = [3, 10, 1, 6, 4];
const minNum = Math.min(nums);
const maxNum = Math.max(nums);

console.log(minNum); // NaN
console.log(maxNum); // NaN


// 나머지 매개변수 사용
const minNum = Math.min(...nums);
const maxNum = Math.max(...nums);

console.log(minNum); // 1
console.log(maxNum); // 10


// apply() 사용
const minNum = Math.min.apply(null, nums); // (null,[3,10,1,6,4])
const maxNum = Math.max.apply(null, nums);

console.log(minNum); // 1
console.log(maxNum); // 10

// call() 사용
const minNum = Math.min.call(null, ...nums); // (null,3,10,1,6,4)
const maxNum = Math.max.call(null, ...nums); 

console.log(minNum); // 1
console.log(maxNum); // 10

bind

bind는 함수의 this 값을 영구적으로 바꿀 수 있다.

const jubby = {
  name: "jubby",
};

function update(birthYear, occupation) {
  	this.birthYear = birthYear;
	this.occupation = occupation;
}     

const updateJubby = update.bind(jubby); // 항상 jubby를 this로 갖는다.

updateJubby(1998, 'dancer');
console.log(jubby);
const user = {
  name: "jubby",
  showName: function() {
    console.log(`hello, ${this.name}`);
  },
};

user.showName(); // hello, jubby

let fn = user.showName;

fn(); // hello, 

fn.call(user); // hello, jubby
fn.apply(user); // hello, jubby

let boundfn = fn.bind(user);
boundfn(); // hello, jubby



👩‍👧 상속, Prototype

Prototype

const user = {
  name: "jubby"
}

user.name // jubby

// hasOwnProperty(): 자신이 프로퍼티를 가지고 있는지 확인할 수 있는 메소드
user.hasOwnProperty('name'); // true
user.hasOwnProperty('age'); // false

객체에서 프로퍼티를 읽으려고 하는 데 없으면 일단 __proto__ 에서 찾는다.

만약, hasOwnProperty() 메소드가 객체 안에 있으면?

const user = {
  name: "jubby",
  hasOwnProperty: function() {
    console.log('haha');
  }
}

user.hasOwnProperty(); // haha

객체에 그 프로퍼티가 있으면 거기서 탐색을 멈춘다. 없을때만 __proto__에서 프로퍼티를 찾는다.


상속

const car  = {
  wheels: 4,
  drive() {
    console.log("drive..");
  },
};

const bmw = {
  color: "red",
  navigation: 1, 
};

const benz = {
  color: "black",
};

const audi = {
  color: "blue",
};

bmw.__proto__ = car; // car가 bmw의 proto가 된다.
benz.__proto__ = car; // benx는 car의 상속을 받는다.
audi.__proto__ = car;

const x5 = {
  color: "white",
  name: "x5",
};

// 상속은 계속 이어질 수 있다.
x5.__proto__ = bmw; 
> bmw
{color: "red", navigation: 1}
> bmw.color
"red"
> bmw.wheels;
4
> x5.name
"x5"
> x5.navigation
1
> for(p in x5) {
	 console.log(p);
  }
color
name
navigation
wheels
drive
> x5
{color: "white", name: "x5"}
> Object.keys(x5);
["color", "name"]
> Object.values(x5);
["white", "x5"]
> for(p in x5){
	if(x5.hasOwnProperty(p)){
    	console.log('o', p);
    } else {
    	console.log('x', p);
    }
 }
o color
o name
x navigation
x wheels
x drive

생성자 함수 사용하기

const car = {
  wheels: 4,
  drive() {
    console.log("drive..");
  },
};

const Bmw = function (color) {
  this.color = color; 
};

const x5 = new Bmw("red");
const z4 = new Bmw("blue");

x5.__proto__ = car;
z4.__proto__ = car;

위와 같은 코드

const Bmw = function (color) {
  this.color = color; 
};

// 생성자 함수가 생성하는 객체에 `__proto__`를 각각 설정한다는 의미이다. 
Bmw.prototype.wheels = 4;
Bmw.prototype.drive = function () {
  console.log("drive..");
};
Bmw.prototype.navigation = 1;
Bmw.prototype.stop = function () {
  console.log("STOP!");
};

const x5 = new Bmw("red");
const z4 = new Bmw("blue");
>x5.stop();
STOP!

생성자 함수가 새로운 객체를 만들어 낼 때, 그 객체는 생성자의 인스턴스라고 불린다.

instanceof()를 이용해서 객체와 생성자를 구분할 수 있다. 이는 해당 객체가 그 생성자로부터 생성된 것인지를 판단해서 true 혹은 false를 반환한다.

// Bmw를 이용해서 z4를 만들었다, z4는 Bmw의 인스턴스 이다.
> z4 instanceof Bmw
true

생성자(Bmw)를 통해서 만들어진 인스턴스 객체(z4)는 constructor 프로퍼티를 가진다.

// z4의 생성자는 Bmw 이다.
> z4.constructor === Bmw;
true

위 코드를 좀 더 깔끔하게 작성해본다.

const Bmw = function (color) {
  this.color = color; 
};

Bmw.prototype = {
  wheels: 4,
  drive() {
    console.log("drive..");
  },
  navigation: 1,
  stop() {
    console.log("STOP!");
  },
}

const x5 = new Bmw("red");
const z4 = new Bmw("blue");

하지만 이렇게 되면 constructor가 사라진다.

> z4.constructor === Bmw;
false

그렇게 때문에 각각 명시해 주거나 아래와 같이 작성하면 된다.

Bmw.prototype = {
  constructor: Bmw,
  wheels: 4,
  drive() {
    console.log("drive..");
  },
  navigation: 1,
  stop() {
    console.log("STOP!");
  },
}

const Bmw = function (color) {
  this.color = color;
};

const x5 = new Bmw("red");
> x5
{color: "red"}
> x5.color = "black";
> x5.color
"black"

위와 같이 color 값이 변경되지 않게 하려면 아래와 같이 작성한다.

const Bmw = function (color) {
  const c = color;
  this.getColor = function () {
    console.log(c);
  };
};

const x5 = new Bmw("red");

값을 얻을 수 있는 방법만 있고, 바꿀 수 있는 방법은 없다.

> x5.getColor();
red



📟 class

ES6에 추가된 스펙이다.

const User = function (name, age) {
  this.name = name;
  this.age = age;
  this.showName = function() {
    console.log(this.name);
  };
};

/* showName() Prototype으로 바꾸기 */
User.prototype.showName = function() {
  console.log(this.name);
};

/* class 사용 */
Class User2 {
  	// constructor(): 객체를 만들어주는 생성자 메서드
	constructor(name, age) { 
    	this.name = name;
        this.age = age;
    }
	showName() { // prototype에 저장된다.
    	console.log(this.name);
    }
}

const jubby = new User('jubby', 25);
const ohwa = new User2('ohwa', 28); // new를 통해 호출하면 constructor가 자동으로 실행된다.

new를 사용하지 않으면?

const jubby = User('jubby', 25); 
const ohwa = User2('ohwa', 28); // error가 발생한다.

for..in문은 prototype이 포함된 프로퍼티들을 다 보여주고, 객체가 가지고 있는 프로터피만 판별하기 위해서는 hasOwnProperty()를 사용해야 한다. 하지만 class의 메서드는 for..in 문에서 제외된다.

const User = function (name, age) {
  this.name = name;
  this.age = age;
  this.showName = function() {
    console.log(this.name);
  };
};

/* class 사용 */
Class User2 {
  	// constructor(): 객체를 만들어주는 생성자 메서드
	constructor(name, age) { 
    	this.name = name;
        this.age = age;
    }
	showName() { // prototype에 저장된다.
    	console.log(this.name);
    }
}

const jubby = new User('jubby', 25); 
const ohwa = new User2('ohwa', 28); 

for(const p in jubby) {
  console.log(jubby); // name age showName
}

for(const p in ohwa) {
  console.log(ohwa); // name age
}

class의 상속

extends 키워드를 사용한다.

class Car {
  constructor(color) {
    this.color = color;
    this.wheels = 4;
  }
  drive() {
    console.log("drive..");
  }
  stop() {
    console.log("STOP!");
  }
};

class Bmw extends Car() {
  park() {
    console.log("PARK");
  }
};

const z4 = new Bmw("blue");

class의 method overriding

class Car {
  constructor(color) {
    this.color = color;
    this.wheels = 4;
  }
  drive() {
    console.log("drive..");
  }
  stop() {
    console.log("STOP!");
  }
};

class Bmw extends Car() {
  park() {
    console.log("PARK");
  }
  stop() { // overriding
    console.log("OFF");
  }
};

const z4 = new Bmw("blue");
z4.stop(); // 'OFF' 출력

부모 메서드를 그대로 사용하면서 확장하고 싶다면, super().method명 사용.

class Bmw extends Car() {
  park() {
    console.log("PARK");
  }
  stop() { // overriding
    super.stop();
    console.log("OFF");
  }
};

const z4 = new Bmw("blue");
z4.stop();
// STOP!
// OFF

constructor overriding, 자식 생성자에서 this를 사용하기 전에 부모 생성자를 반드시 먼저 호출해야 한다.

class Bmw extends Car() {
  // 부모 생성자의 인수와 동일한 인수를 넣어줘야 한다.
  constructor(color) {
    super(color); // 항상 부모 생성자를 먼저 실행해주어야 한다.
    this.navigation = 1;
  }
  park() {
    console.log("PARK");
  }
};

const z4 = new Bmw("blue");
profile
신입 개발자 쥬비의 기술 블로그 입니다.

0개의 댓글