본문 바로가기
Be Smart/JAVA

[Learn.JS] JavaScript-6 (Function Context, Inheritance, Destructuring)

by 반월하 2022. 1. 7.
728x90

Function Context

자바스크립트의 함수는 특정한 컨텍스트에서 실행되며, this 변수를 사용하여 우리는 자바스크립트에 접근할 수 있다.

브라우저의 모든 표준 기능은 Window 컨텍스트에서 실행됩니다. 객체 또는 클래스 아래에 정의된 함수(다른 함수)는 객체가 생성된 객체의 컨텍스트를 사용합니다. 그러나 함수를 실행하기 전이나 실행하는 동안 런타임에 함수의 컨텍스트를 변경할 수도 있습니다.

Binding a method to an object


함수를 객체에 바인딩하고 객체 메소드로 만들기 위해 bind함수를 사용할 수 있습니다. 다음은 간단한 예입니다.

var person = {
		name : "John"
};

function printName()
{
		console.log(this.name);
}

printName()을 호출하려면 함수를 개체 person과 연결해야 합니다. 이렇게 하려면 다음 코드를 사용하여 printName to person 함수의 바인딩 메서드를 만들어야 합니다.

var boundPrintName = printName.bind(person);
boundPrintName();           // prints out "John"

Calling a function with a different context


call 과 apply 함수를 이용해서 객체에 바인딩된 것처럼 호출할 수 있습니다. call과 apply 함수의 차이는 그들이 어떻게 그들의 인수를 수신하는가에 의해서만 나타난다. call함수는 이 인수를 먼저 수신하고, 그 후에 함수의 인수를 수신하는 반면, apply함수는 이 인수를 먼저 수신하고, 함수의 두 번째 인수로서 함수에 전달하기 위한 인수의 배열이다.

예를 들어, call 메서드를 사용하여 printName 로 person을 콘텍스트로 호출합니다.

printName.call(person);            // prints out "John"

call/apply vs bind


call/apply와 bind의 차이점은 bind가 새 함수에서 this의 값이 이제 바인딩된 개체라는 것을 제외하고 이전 함수와 동일한 새 함수를 반환한다는 것이다 call/apply는 this 가 바인딩된 객체로 함수를 호출하지만, this은 반환된 새로운 함수를 반환하거나 원래 함수를 변경하지 않고, this에 대해 다른 값을 갖는 함수를 호출한다.

예를 들면:

var boundPrintName = printName.call(person);    //boundPrintName gets printName's return value (null)
boundPrintName();             //doesn't work because it's not a function, it's null

printName.bind(person);       // returns a new function, but nothing is using it so it's useless
printName();           // throws error because this.name is not defined

call을 bind의 반환 값을 실행하는 것으로 생각하십시오.

예를 들면,

printName.call(person);           //is the same as
printName.bind(person)();         //executes the function returned by bind

아니면 바로 call 바로가기를 돌려주는 바인드를 생각해보세요.

예를 들면,

var boundPrintName = printName.bind(person); // is the same as
var boundPrintName = function()
{
		printName.call(person);
}

Exercise

boundPrintFullName 및 boundPrintDetails라는 사람에게 printFullName 및 printDetails의 바인딩된 복사본을 만듭니다.

var person = {
    firstName : "John",
    lastName : "Smith",
    age : 23
};

function printFullName()
{
    console.log(this.firstName + " " + this.lastName);
}

function printDetails()
{
    console.log(this.firstName + " is " + this.age + " years old");
}

// TODO: create bound copies of printFullName and printDetails.
var boundPrintFullName = function()
{
		printFullName.call(person);
}
var boundPrintDetails = printDetails.bind(person);

boundPrintFullName();
boundPrintDetails();

Inheritance

JavaScript는 프로토타입 기반 상속을 사용합니다. 모든 객체는 prototype을 가지고 있으며, 객체의 메소드가 호출되면 자바스크립트는 프로토타입 객체로부터 실행할 수 있는 적절한 함수를 찾으려고 한다.

The prototype attribute


프로토타입 객체를 사용하지 않고 다음과 같이 개체 Person을 정의할 수 있습니다.

function Person(name, age)
{
		this.name = name;
		this.age = age;

		function describe()
		{
				return this.name + ", " + this.age + " years old.";
		}
}

Person 오브젝트의 인스턴스를 작성할 때, 함수의 모든 구성원과 메소드의 새로운 사본을 작성합니다. 이는 객체의 모든 인스턴스(instance)가 고유한 name 및 age 특성뿐만 아니라 고유한 설명 함수를 갖게 된다는 것을 의미한다.

그러나 Person.prototype 개체를 사용하고 기능을 할당하면 이 개체도 작동합니다.

function Person(name, age)
{
		this.name = name;
		this.age = age;
}

Person.prototype.describe = function()
{
		return this.name + ", " + this.age + " years old.";
}

Person 오브젝트의 인스턴스를 작성할 때 describe 함수의 복사본은 포함되지 않습니다. 대신 오브젝트 메서드를 호출할 때 자바스크립트는 오브젝트 자체에서 먼저 describe 함수를 확인한 다음 prototype 속성을 사용한다.

Inheritance


Person 객체를 만들고 싶다고 하자, 그리고 Person 객체에서 Student 객체가 파생한다.

var Person = function() {};

Person.prototype.initialize = function(name, age)
{
		this.name = name;
		this.age = age;
}

Person.prototype.describe = function()
{
		return this.name + ", " + this.age + " years old.";
}

var Student = function() {};
Student.prototype = new Person();

Student.prototype.learn = function(subject)
{
		console.log(this.name + " just learned" + subject);
}

var me = new Student();

me.initialize("John", 25);
me.learn("Inheritance");

이 예에서 볼 수 있듯이 initialize 메서드는 Person에 속하고 learn 메서드는 Student에 속하며, 두 방법 모두 me 객체의 일부입니다.

자바스크립트에는 상속을 할 수 있는 여러 가지 방법이 있는데, 이것도 그 중 하나일 뿐입니다.

Exercise

Person 클래스에서 파생된 Teacher 이라는 객체를 만들고 teach라는 메서드를 구현하여 subject라는 문자열을 수신하고 다음을 출력합니다.

 [teacher's name] is now teaching [subject]
var Person = function() {};

Person.prototype.initialize = function(name, age)
{
    this.name = name;
    this.age = age;
}

// TODO: create the class Teacher and a method teach
var Teacher = function() {};
Teacher.prototype = new Person();

Teacher.prototype.teach = function(subject)
{
		console.log(this.name + " is now teaching " + subject);
}

var him = new Teacher();

him.initialize("Adam", 45);
him.teach("Inheritance");

Destructuring

Desturucturing은 ES6의 특징이다, JS에서 만들어지는 반복적인 과정이나 변수 배정을 더 쉽고 깔끔하게 보여준다.

Destructuring을 통해 우리는 array나 객체의 깊은 레벨에서 부터 데이터를 보다 간결한 문법으로 불러올 수 있으며, 같은 작업에서 이 추출된 데이터에 다른 이름을 부여할 수 있습니다.

자바 스크립트에선 더 쉬운 방법으로 표현할 수 있다.

// 이 객체를 생각해보면
const person = {
		head: {
				eyes: 'x',
				mouth: {
						teeth: 'x',
						tongue: 'x'
				}
		},
		body : {
				shoulders: 'x',
				chest: 'x',
				arms: 'x',
				hands: 'x',
				legs: 'x'
		}
};

// 우리가 head를 얻고 싶으면, 이전 방식은:
let head = person.head;

// ES6 Destructuring
let { head } = person;

// 같은 줄에 함수가 선언되고 다른 이름을 부여하고 싶다면
let { head : myHead } = person;

// 그래서 우리는 이렇게 할 수 있다.
console.log(myHead);         //  '{ eyes, mouth: {...} }'가 출력된다.

Array 에선

let numbers = ['2', '3', '7'];

// 이전 방식
let two = numbers[0];
let three = numbers[1];

// ES6 Destructuring
let [two, three] = numbers;

// 마찬가지로 다른 이름도 부여할 수 있다.
let [two: positionZero, three: positionOne] = numbers;

console.log(positionZero)          // prints '2'
console.log(positionOne)           // prints '3'

함수 인자에서도 마찬가지로 적용할 수 있다.

// 이전 방식
function getHeadAndBody(person) {

		let headAnadBody = {
				head : person.head,
				body : person.body
		}

		return headAnadBody;
}

// ES6 Destructuring
function getHeadAndBody({ head, body }) {
		return { head, body }
}

// arrow 함수 사용시
let getHeadAndBody = ({ head, body }) => { head, body };

경고: Destructuring을 할 때 주의하세요 객체의 인자들이 어떻게 나올지 확실치 않을 땐 undefined 에러가 안 일어나기 위해 이전 방식을 사용하는 게 더 낫습니다.

ES6 Discructuring을 사용하여 함수에 기본 파라미터를 제공하는 동안 이러한 유형의 오류를 방지할 수 있으므로 수신된 파라미터에 의존하지 않고 속성이 존재하는지 확인할 수 있습니다.

// head 와 body가 어떤 형태로 있는지 확신할 수 없습니다..
// 이제 실수 매개변수가 내부에 해당 속성을 가지고 있지 않으면 head 또는 body 가 
// ''와 같을 것이라고 확신합니다.
function getHeadAndBody({ head = '', body = '' }) {
		return { head, body }
}

원하는 만큼 깊게 destructure 할 수 있습니다. 항상 그 속성이 존재하는지 생각하세요.

// Deep destructuring
let computer = {
    processor : {
        transistor : {
            silicon : {
                thickness  : '9nm'
                      }
                }
        }
}

let {
        processor:
        {
            transistor: {
                silicon: {
                    thickness
				}
			}
		}
} = computer;

// Making it cleaner
let { thickness: intelli9Thickness } = computer.processor.transistor.silicon;

console.log(intelli9Thickness)              // prints '9nm'

Exercise

legs 속성을 가져오고 destructuring를 사용하여 numbers 에 세 번째 위치 안에 있는 데이터를 가져옵니다. myLegs와 thirdPosition의 이름을 각각 알려주세요.

const person = {
    head: {
        eyes: 'x',
        mouth: {
            teeth: 'x',
            tongue: 'x'
        }
    },
    body: {
        shoulders: 'x',
        chest: 'x',
        arms: 'x',
        hands: 'x',
        legs: 'x'
    }   
};

const numbers = ['2', '3', '4'];

// TODO: Destructure here.
let {legs : myLegs} = person.body;
let [onePosition, secondPosition, thirdPosition] = numbers;

// or...
// const { legs: myLegs } = person.body;
// const [,,thirdPosition] = numbers;

console.log(myLegs);
console.log(thirdPosition);
728x90

댓글