new 연산자와 생성자 함수 파트 링크 : https://ko.javascript.info/constructor-new
'new' 연산자와 생성자 함수를 사용하면 유사한 객체 여러 개를 쉽게 만들 수 있다.
생성자 함수와 일반 함수는 기술적인 부분에서 차이가 없지만 규칙이 다르다.
생성자 함수는 첫글자를 대문자로 쓰고, 반드시 앞에 new 연산자를 붙여 실행한다.
new 생성자를 쓰게 되면
1. 빈 객체를 만들어 this에 할당
2. 함수 본문을 실행하여 this에 새로운 프로퍼티를 추가해 this를 수정
3. this를 암시적으로 반환
이 세 과정으로 실행하게 된다.
<script>
function User(name) {
this.name = name;
this.isAdmin = false;
}
let user = new User("보라");
alert(user.name); // 보라
alert(user.isAdmin); // false
//------------동작과정↓----------------
function User(name) {
// 1.this = {}; (this가 new 생성자를 통해 새로 만들어진 빈 객체를 가리킴)
// 2. 함수 본문이 실행되어 새로운 프로퍼티를 빈 객체가 할당된 this에 추가함
this.name = name;
this.isAdmin = false;
//3. return this; (생성자 함수는 이 this를 반환한다.=> 중요!)
}
</script>
주의!
모든 함수는 new가 붙어 생성자 함수가 될 수 있다!
그리고 이름의 첫 글자가 대문자인 함수는 new를 붙여 실행하는 것이 약속이다!
let user = new User;
이런식으로도 호출할 수 있지만 좋은 스타일은 아니다.익명 생성자 함수로 감싸주는 방식을 사용할 수 있다!
<script>
let user = new function() {
this.name = "John";
this.isAdmin = false;
// 사용자 객체를 만들기 위한 여러 코드.
// 지역 변수, 복잡한 로직, 구문 등의
// 다양한 코드...
};
</script>
위처럼 생성자 함수를 익명 함수로 정의하면 익명 함수이기 때문에 어디에도 저장되지 않는다.
저장되지 않으므로 재사용이 불가능하다.
이렇게 익명 함수로 생성자 함수를 정의하면 재사용은 막으면서 코드를 캡슐화 할 수 있다.
객체의 속성(data fields)과 행위(메서드, methods)를 하나로 묶고,
=> 서로 관련이 있는 속성과 메서드들을 하나의 틀에 담는 것. (ex. 자바의 클래스)
실제 구현 내용 일부를 외부에 감추어 은닉한다.
=> ex) 외부에서 직접 접근이 불가능하고 getter, setter 메서드를 사용하여 접근 가능하도록 제한을 두는 것.
(참고: https://alcohol-dev.tistory.com/1)
이 문법은 자주 쓰이는 문법은 아니다.
new.target 프로퍼티를 사용하면 함수가 new와 함께 호출되었는지 아닌지를 알 수 있다.
일반적인 방법으로 함수를 호출했다면 new.target은 undefined를 반환한다.
함수를 new와 함께 호출한 경우엔 new.target은 함수 자체를 반환해줍니다.
<script>
function User() {
alert(new.target);
}
// 'new' 없이 호출
User(); // undefined
// 'new'를 붙여 호출
new User(); // function User { ... }
</script>
함수 본문에서 new.target을 사용하면 해당 함수가 new와 함께 호출되었는지(in constructor mode) 아닌지(in regular mode)를 확인할 수 있다.
이를 이용하여 new를 이용하지 않고 호출한 함수를 new를 붙여 호출한 함수처럼 바꿔줄 수 있다.
<script>
function User(name) {
if (!new.target) { // new 없이 호출하면
return new User(name); // new를 붙인 함수를 반환
}
this.name = name;
}
let bora = User("보라"); // 'new User'를 쓴 것처럼 호출된 함수로 새 값이 저장된 this가 bora에 할당된다.
alert(bora.name); // 보라
</script>
하지만 아시다시피... new 생성자를 굳이 안 붙이고 위와 같이 작성할 일은 정말정말 거의 없다.
생성자 함수에서는 반환해야 할 것들이 모두 this에 저장되고, this가 자동으로 반환되기 때문에 반환문을 명시적으로 써 줄 필요가 없다.
그럼에도 생성자 함수에 return 문을 쓴다면?
<script>
function BigUser() {
this.name = "원숭이";
return { name: "고릴라" }; // <-- this가 아닌 새로운 객체를 반환함
}
alert( new BigUser().name ); // 고릴라
</script>
하지만 역시나 return 문이 있는 생성자 함수는 거의 쓰이지 않는다.
객체의 프로퍼티에 함수(메서드)를 할당할 수 있는 것처럼 생성자 함수를 통해 만들어진 this에도 메서드를 할당할 수 있다.
<script>
function User(name) {
this.name = name;
this.sayHi = function() {
alert( "제 이름은 " + this.name + "입니다." );
};
}
let bora = new User("이보라");
bora.sayHi(); // 내 이름은 이보라입니다.
/*
bora 에는 {
name: "이보라",
sayHi: function() { ... }
} 객체가 할당 되어있다.
*/
</script>
class 문법 또한 생성자 함수와 같이 복잡한 객체를 만들 수 있는데 이는 추후에 학습한다고 한다.