본문 바로가기
프로그래밍 개발/JS ES6+

Javascript ES6+ Function 함수

by Jinseok Kim 2020. 12. 14.
반응형

 

 

Function 함수

 

 

 

 

name property

 

function a () { }
console.log(a.name)

//결과:
// a

위 코드는 name 프로퍼티가 함수 a에 붙어 문자열로 'a'가 출력된다. 함수가 name 프로퍼티를 가지고 있기 때문에 가능한 일이다.

 

 

 

 

const b = function () { }
console.log(b.name)

//결과: b

const c = function cc () { }
console.log(c.name)

//결과: cc

const d = () => {}
console.log(d.name)

//결과: d

const e = {
  om1: function () {},
  om2 () {},
  om3: () => {}
}
console.log(e.om1.name, e.om2.name, e.om3.name)

//결과: om1 om2  om3

 

  • 이제는 익명함수를 선언한 변수가 원래 갖고 있지 않은 name 프로퍼티를 붙여 호출해도 자동으로 변수의 이름이 문자열로 도출되도록 되었다. 
  • 에로우 함수는 원래부터 익명함수이기 때문에 익명함수를 선언한 변수의 이름으로 문자열로 도출된다.
  • 기명함수를 선언한 변수는 기명함수가 이름을 가지고 있기 때문에 원래대로 이름을 가지고 있는 기명함수의 이름을 문자열로 도출한다.

 

 

 

function G () {}
G.method1 = function () {}
G.prototype.method2 = function () {}

const g = new G()
console.log(G.method1.name, g.method2.name)

//결과:
//아무것도 안뜬다.
  • 위의 코드 방식은 ES5 형식 코드로 name 프로퍼티를 사용하였지만 ES6+의 새 기능인 name 프로퍼티가 어떤 name을 가져올지 애매하여 값이 호출되지 않는 것이다.
  • 예로 G.method1의 name을 넣을 것인가 아니면 method1의 name을 넣을 것인가 애매해졌기 때문이다.

하지만 아래 코드처럼 ES6+의 기능인 class 변수를 이용하여 줄이면 새로운 name 프로퍼티 기능을 명확하게 잘 사용할 수 있다.

class F {
  static method1 () {}
  method2 () {}
}
const f = new F()
console.log(F.method1.name, f.method2.name)

//결과:
//method1 method2

 

 

 

 

 

 

const g = new Function()
console.log(g.name)

//결과: anonymous

 

  • new 생성 함수를 선언한 변수 g은 보통의 익명함수를 선언한 변수들과 다르게 name을 도출하면 변수 g을 문자열로 도출하는 것이 아닌 new 생성 함수가 만든 인스턴스를 변수 g에 할당하는 것이기에 지금 위의 코드에서는 아직 인스턴스를 만들지 않아 anonymous가 도출된다.

 

 

 

 

const person = {
  _name: 'jinseok',
  get name () {
    return this._name
  },
  set name (v) {
    this._name = v
  }
}
const descriptor = Object.getOwnPropertyDescriptor(person, 'name')
console.log(descriptor.get.name)
console.log(descriptor.set.name)

//결과
//get name
//set name

 

  • 위의 코드 또한 name 프로퍼티를 가져오는 기능을 확인 할 수 있다.
  • getOwnPropertyDescriptor 메소드는 메소드 첫 번째 인자에 들어오는 함수의 name 프로퍼티를 가져오는 역할을 한다고 볼 수 있다.

 

 

 

 

 

 

 

 

 

new.target

 

function Person (name) {
  if (this instanceof Person) {
    this.name = name
  } else {
    throw new Error('new 연산자를 사용하세요.')
  }
}
var p1 = new Person('jinseok')
console.log(p1)
//결과: Person {name: "jinseok"}

var p2 = Person('성훈')
console.log(p2)
//결과: 오류

var p3 = Person.call({}, 'kim')
console.log(p3)
//결과: 오류

var p4 = Person.call(p1, 'kim')
console.log(p4)
//결과: undefind (이때 p1은 Persin {name: "kim"}으로 바뀌어버린다.)

 

  • 위의 코드는 Person의 new 생성 함수의 name 프로퍼티만을 도출하도록 코드를 짠 것이다. 
  • new 생성 함수가 아닌 변수 p2, p3는 정사적으로 Person 함수에서 if문 조건으로 걸러졌다.
  • 하지만 이때 변수 p4을 호출하였을 때 결함적인 오류가 발생한다. p4는 call 메소드의 특징으로 인해 new 생성 함수인 p1 변수를 this로 가지고 와 Person 함수의 if문 조건에 아무문제 없이 실행되버린 것이다.
  • 결국 return 하는게 없는 p4는 결국 undefind가 떠버리고 p1은 call 메소드의 특징으로 name 또한 바뀌어버린다.

이 같은 문제를 해결하기 위해 new.target이라는 기능을 사용하면 문제가 해결된다.

function Person (name) {
  console.dir(new.target)
  if (new.target !== undefined) {
    this.name = name
  } else {
    throw new Error('new 연산자를 사용하세요.')
  }
}

const p1 = new Person('jinseok')
console.log(p1)
//결과: Person {name: "jinseok"}

const p4 = Person.call(p1, 'kim')
console.log(p4)
//결과: 오류

 

  • new.target은 그냥 보면 new라는 것에 target이라는 프로퍼티가 존재할 것 처럼 생겼지만 그냥 new 생성 함수를 인식하는 기능만을 가진 변수 자체로 보면 된다.
  • 위의 결과를 보면 new 생성 함수를 가진 p1 변수를 호출한 결과 Person의 name 프로퍼티 값이 정상적으로 도출되었다.
  • 하지만 p4를 호출하였을때 call 메소드로 new 생성 함수를 가진 변수 p1을 this로 바꾸어도 생성자 Person 함수에서 new.target에 인해 p4는 new 생성 함수 자체가 아니기에 통과되지 않고 그대로 오류를 도출하게 되었다.

 

 

 

 

function Person (name) {
  this.name = name
  return 10;
}
function Android (name) {
  const res = Person.call(this, name)
  console.log(res)
}
const p1 = new Android('jinseok')

//결과: 10

 

  • 위 코드와 같이 p1의 name은 안나오고 값 10이 나오게 된다.
  • 왜냐하면 .call 메소드로 부른 Pereon 함수는 그 함수 자체를 통째로 가져왔기 때문에 Person 함수 안의 리턴 값 10이 나온 것이다.

 

아래와 같이 new.target으로 new 생성 함수만을 인식하게 하고 명확하게 Person 함수인지 아닌지도 구별하게 하면서 아무리 생성 함수지만 원하지 않은 생성 함수 또한 확실히 구별하게끔 하였다.

function Person (name) {
  console.log(new.target)
  if (new.target === Person) {
    this.name = name
  } else {
    throw new Error('Person 생성자함수를 new로 호출해')
  }
}
function Android (name) {
  Person.call(this, name)
}
const p2 = new Android('jinseok')

//결과: 오류 (new로 생성한 함수 Android는 Person 함수가 아니기 때문이다.)

 

 

 

 

 

function Person (name) {
  const af = n => {
    this.name = n
    console.log(new.target)
  }
  af(name)
}
const p1 = new Person('jinseok')
const p2 = Person('kim')

//결과:
//ƒ Person (name) {
// const af = n => {
//this.name = n
//console.log(new.target)
// }
//af(name)
//}
//
//undefined
  • 에로우 함수는 특성상 this 뿐만 아니라 new 생성 함수 또한 바인딩하지 않는다는 특성을 가지고 있다.
  • 그래서 결과를 보면 에로우 함수안에 있는 this와 호출한 new.target이 안된다는 걸 알고 외부로 넘어가 Person 함수 자체를 불러오는 것을 알 수 있다.

 

 

 

 

 

 

 

 

함수선언문과 스코프 주의

 

a(); // 오류

if(true){
function a (){}
}

a(); // 결과 나옴

 

  • 위의 결과를 보면 a();에 위치에 따라 오류가 나고 오류가 안나고 그러는데 이는 ES5에서는 위치에 상관없이 되었지만 ES6+로 업그레이드 되면서 위의 코드와 같이 코드를 시원찮게 작성한다면 오류가 발생한다. 
  • 크롬, 파이어폭스 등등 브라우저마다 이 문제를 해결 되게 하는 곳도 있고 안되는 곳도 있어 매우 불안정하고 불확실하다.
  • 즉 궁극적인 문제는 함수 선언문 function 자체의 문제이다.

 

 

해결방안

 

1. ES6+ 부터는 function과 같은 함수 선언문을 쓰지 않고 Arrow function을 써라.

2. 객체 또한 메소드 축약형으로 쓴다.

3. 생성자 함수를 쓸 때는 class 을 쓴다.

 

 

 

 

 

 

 

 

 

 

 

 

 

반응형

댓글