ES6문법 특징을 정리했는데 몇 가지 특징만 나열하면 끝날 줄 알았는데 몇 가지는 내용이 많아질 거 같아서 따로 빼서 정리하려고 한다. 화살표 함수를 쓰기까지 꽤 오랜시간이 걸렸다. this 바인딩에 관해서도 부족했고, scope관련해서도 더욱 보완했다. 그래도 부족한감은 있지만 이번 기회에 제대로 채워보리라하고 다짐한다.
먼저 자바스크립트에서 함수는 정확히 어떻게 표현할까?
다음과 같이 3가지가 있다고 한다.
함수 선언문 방식으로 정의한 함수는 function
키워드와 아래 내용으로 구성된다고 한다.
()
괄호로 감싸고 ,
콤마로 분리한다고 한다. 다른언어와 차이점으로는 매개변수 타입을 지정하지 않는다고 한다. 이 때문에 함수 내에서 매개변수의 타입을 체크할 필요가 있다고 한다.{}
중괄호로 감싸고, return
문으로 결과값을 반환할 수 있다고 한다. //function declaration
function dog(n){
return n + ' dog(s)'
}
자밬스크립트 함수는 일급 객체(first-class object)
라고 한다. 그리고 특징은 다음과 같다고 한다.
위 특징을 이용하여 함수 리터럴 방식으로 함수를 정의하고 변수에 할당 할수 있는데, 이것을 함수 표현식이라고 한다.
리터럴 표기법이란?
변수를 선언함과 동시에 그값을 지정해주는 표기법을 말한다고 한다.
참조사이트 : 리터럴(Literal)이란?
//function expression
var dog = function(n){
return n + ' dog(s)'
}
함수표현식 방식으로 정의한 함수는 함수명을 생략할 수 있다고 한다. 이런 함수를 익명함수(anonymous function)
이라고 한다. 일반적으로 함수명을 생략한다고 한다.
// 기명 함수 표현식(named function expression)
var dog = function Dog(n){
return n + ' dog(s)'
}
// 익명 함수 표현식(anonymous function expression)
var dog = function(n){
return n + ' dog(s)'
}
console.log(dog(2)) //'2 dog(s)'
console.log(Dog(2)) // ReferenceError: Dog is not defined
위 예에서 에러가 났는데 그 이유는 표현식에서 사용한 함수명은 외부코드에서는 접근이 불가능하기 때문이라고 한다. 함수는 일급객체이기 때문에 변수에 할당할 수 있는데, 이 변수는 함수명이 아니라 함수를 가르키는 참조값을 저장하게 된다고 한다. 그렇기 때문에 함수 호출시 함수명이 아니라 함수를 가르키는 변수명을 사용하여야 한다고 한다. 사실 함수 선언문도 함수 표현식과 동일하게 함수 리터럴 방식으로 정의 된 것이라고 한다.
//function delaration
function dog(n){
return n + ' dog(s)'
}
//함수 선언문도 결국 자바스크립트 엔진에서 표현식으로 형태가 변경하기 때문에 접근이 가능했던 것이다.
var dog = function dog(n){
return n + ' dog(s)'
}
위 에서 함수 선언문 또한 기명 함수 표현식으로 변환 되므로 결국 함수 리터럴 방식이라는 것을 알았다.
이는 결국 내장 함수인 Function
생성자 함수로 함수를 생성한 것을 단축 시킨 short-hand(축약법) 이라고 한다.
Function 생성자 함수는 Function.prototype.constructor 프로퍼티로 접근할 수 있다고 한다.(Prototype에 대한 velog)
new Function ([arg1[, arg2[, ...argN]],] functionBody)
let dog = new Function('n', `return n + ' dog(s)'`)
console.log(dog(10)) // '10 dog(s)'
앞에서 함수생성의 3가지 특징과 결국 함수는 Function
생성자 함수로 생성한다는 것을 알았다. 그렇다면 화살표 함수는 어떤 특징을 갖고 있을까?
//function declaration
function abplus(a, b){
return a + b
}
//arrow function
const abplus=(a, b)=>{
return a+b
}
//매개 변수가 없는 경우
const dog=()=>{}
//매개 변수가 한 개인 경우, 소괄호 생략 가능
x => {}
//매개 변수가 여러개인 경우, 소괄호 생략 불가능
(a, b) => {}
//single line block
//한줄의 구문이라면 중괄호를 생략가능하다. 암묵적 return이 가능하다고 한다.
const abplus=(a, b)=> a + b
//객체 반환 시 소괄호를 사용한다고 한다.
const abplus=(a, b)=>({a: a +b})
const abplus=(a, b)=>{return {a: a +b}}
//destructuring assignment(구조분해할당)도 가능하다고 한다.
let abplus = ([a,b] = [1, 2], {x: c} = {x: a + b}) => a + b + c
abplus() //6
//default function parameter 사용도 가능하다고 한다.
let abplus = (a = 1, b = 2) => a + b
console.log(abplus()) //3
console.log(abplus(4)) //6
console.log(abplus(3,3)) //6
// Rest parameter(나머지 매개변수)사용도 가능하다고 한다.
let abc = (a,b,...c) => {
console.log(`1: ${a}, 2: ${b}`)
console.log(`3 ~ : ${c}`)
}
console.log(abc(1,2,3,4,5,6))
//'1: 1, 2: 2'
//'3 ~ : 3,4,5,6'
function 키워드로 생성한 일반함수와 화살표 함수의 가장 큰 차이점은 this
라고 한다.
자바스크립트는 함수 호출 방식에 의해 this
가 바인딩할 객체를 동적으로 결정된다고 한다.
콜백 함수 내부의 this
는 전역객체(window)를 가르킨다고 한다. (참고 : this와 바인딩?
)
function Dog(x){
this.x = x
}
Dog.prototype.dognameArr = function(arr){
return arr.map(function(el){
return `${el} ${this.x}~!`
})
}
let dog = new Dog('hello')
console.log(dog.dognameArr(['ddoddo', 'bbobbi']))
//[ 'ddoddo undefined~!', 'bbobbi undefined~!' ]
위 경우는 new 연산자를 사용하여 this
는 생성된 인스턴스 객체에 바인딩 되었다고 한다. 그러나 생성자 함수인 'dognameArr' 안에 있는 반환 값인, map메소드 안에 있는 콜백함수에서의 this
는 그렇지 않다고 한다. 이유는 생성자 함수와 객체의 메소드를 제외한 모든 함수(내부 함수, 콜백 함수 포함) 내부의 this
는 전역객체를 가리키기 때문이라고 한다.
위 문제를 해결하기 위해서는 3가지 방법이 있다고 한다.
//1. 변수 할당
function Dog(x){
this.x = x
}
Dog.prototype.dognameArr = function(arr){
let abc = this // abc에 할당(?)
return arr.map(function(el){
return `${el} ${abc.x}~!`
})
}
let dog = new Dog('hello')
console.log(dog.dognameArr(['ddoddo', 'bbobbi']))
//[ 'ddoddo hello~!', 'bbobbi hello~!' ]
// 2. map - thisArg 사용
unction Dog(x){
this.x = x
}
Dog.prototype.dognameArr = function(arr){
return arr.map(function(el){
return `${el} ${this.x}~!`
},this) //map method에서 thisArg 지정
}
let dog = new Dog('hello')
console.log(dog.dognameArr(['ddoddo', 'bbobbi']))
//[ 'ddoddo undefined~!', 'bbobbi undefined~!' ]
// 3. bind
function Dog(x){
this.x = x
}
Dog.prototype.dognameArr = function(arr){
return arr.map(function(el){
return `${el} ${this.x}~!`
}.bind(this)) // bind method 사용
}
let dog = new Dog('hello')
console.log(dog.dognameArr(['ddoddo', 'bbobbi']))
//[ 'ddoddo undefined~!', 'bbobbi undefined~!' ]
화살표 함수에서 this
는 일반 함수와 달리 정적으로 결정된다고 한다. 화살표 함수의 this
는 언제나 상위 스코프의 this
를 가르킨다고 한다. 이를 lexical this라고 한다. (마치 렉시컬 스코프와 유사하다고 한다.)
function Dog(x){
this.x = x
}
Dog.prototype.dognameArr = function(arr){
//상위 스코프인 dognameArr 메소드 내의 this를 가르킨다고 한다.
return arr.map(el => `${el} ${this.x}~!`)
}
let dog = new Dog('hello')
console.log(dog.dognameArr(['ddoddo', 'bbobbi']))
// [ 'ddoddo hello~!', 'bbobbi hello~!' ]
화살표 함수는 call, apply, bind 메소드를 사용하여 this를 변경할 수 없다고 한다.
x = 1
let abc1 = function(){return this.x}
let abc2 = () => this.x
console.log(abc1.call({x : 10})) //10
console.log(abc2.call({x : 10})) //1
화살표 함수에서 this는 lexical scope를 따르기 때문에 콜백함수로는 편리할 수도 있다. 하지만 오히려 혼란을 불러 일으킬수도 있다고 하니 주의해야할 경우를 짚어보자.
const Dog = {
name: 'ddoddo',
sayHello: () => console.log(`Hello ${this.name}`)
}
Dog.sayHello() //'Hello undefined'
이 경우 메소드를 정의한 화살표 함수 내부의 this
는 메소드를 소유한 객체를 가르키지 않고 상위 스코프인 전역객체를 가르킨다고 한다.
const Dog ={
name: 'ddoddo',
sayHello: function(){
console.log(`Hello ${this.name}`)
}
}
Dog.sayHello() //'Hello ddoddo'
prototype에 할당 하는 함수의 경우도 동일한 문제가 발생한다고 한다.
const Dog = {
name: 'ddoddo'
}
Object.prototype.sayHello = () => console.log(`Hello ${this.name}`)
Dog.sayHello() //'Hello undefined'
생성자 함수인 경우 생성자 함수는 prototype property를 가지고, prototype property가 가르키는 constructor를 사용한다고 한다. 하지만 화살표 함수인 경우 prototype property를 가지고 있지 않다고 한다.
let dog = () => {}
let dog1 = new dog() // 'TypeError: dog is not a constructor'
이번 개념은 너무 오래 걸렸다ㅠ. 짚어봐야할 개념도 많았지만, 한 번에 본다고 이해가 되지 않는 부분도 있어서 복습에 시간을 많이 할애 했다. 그 만큼 얻은 것도 많다.
[화살표 함수, MDN, 2022년06월28일 접속]
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Functions/Arrow_functions
[함수, poiemaweb, 2022년06월28일 접속]
https://poiemaweb.com/js-function#1-%ED%95%A8%EC%88%98-%EC%A0%95%EC%9D%98
[리터럴(Literal)이란?, velog, 2022년06월28일 접속]
https://velog.io/@pjeeyoung/%EB%A6%AC%ED%84%B0%EB%9F%B4
[화살표 함수, poiemaweb, 2022년07월03일 접속]
https://poiemaweb.com/es6-arrow-function