new F()
와 같은 생성자 함수를 사용하여 새 개체를 만들 수 있다는 점을 기억하세요.
F.prototype
이 객체인 경우 new
연산자는 이를 사용하여 새 객체에 대한 [[Prototype]]
설정합니다.
참고:
JavaScript는 처음부터 프로토타입 상속을 가졌습니다. 이는 언어의 핵심 기능 중 하나였습니다.
하지만 옛날에는 직접 접근할 수 없었습니다. 안정적으로 작동하는 유일한 것은 이 장에서 설명하는 생성자 함수의 "prototype"
속성이었습니다. 그래서 아직도 이를 사용하는 스크립트가 많이 있습니다.
여기서 F.prototype
은 F
의 "prototype"
이라는 일반 속성을 의미합니다. "프로토타입"이라는 용어와 비슷하게 들리지만 여기서는 실제로 이 이름을 가진 일반 속성을 의미합니다.
예는 다음과 같습니다.
동물 = { 먹는다: 사실이다 }; 함수 토끼(이름) { this.name = 이름; } Rabbit.prototype = 동물; let Rabbit = new Rabbit("흰 토끼"); // 토끼.__proto__ == 동물 경고(rabbit.eats); // 진실
Rabbit.prototype = animal
설정은 말 그대로 다음과 같이 나타납니다. " new Rabbit
이 생성되면 해당 [[Prototype]]
animal
에 할당합니다."
결과 사진은 다음과 같습니다.
그림에서 "prototype"
은 가로 화살표로 일반 속성을 의미하고, [[Prototype]]
은 세로로 animal
로부터 rabbit
의 상속을 의미합니다.
F.prototype
new F
시간에만 사용됩니다.
F.prototype
속성은 new F
호출될 때만 사용되며, 새 객체의 [[Prototype]]
할당합니다.
생성 후 F.prototype
속성이 변경되면( F.prototype = <another object>
) new F
에 의해 생성된 새 개체는 [[Prototype]]
과 같은 다른 개체를 갖게 되지만 이미 존재하는 개체는 이전 개체를 유지합니다.
우리가 제공하지 않더라도 모든 함수에는 "prototype"
속성이 있습니다.
기본 "prototype"
은 함수 자체를 다시 가리키는 유일한 속성 constructor
가진 객체입니다.
이와 같이:
함수 토끼() {} /* 기본 프로토타입 Rabbit.prototype = { 생성자: 토끼 }; */
우리는 그것을 확인할 수 있습니다:
함수 토끼() {} // 기본적으로: // Rabbit.prototype = { 생성자: Rabbit } 경고( Rabbit.prototype.constructor == 토끼 ); // 진실
당연히 아무것도 하지 않으면 [[Prototype]]
통해 모든 토끼가 constructor
속성을 사용할 수 있습니다.
함수 토끼() {} // 기본적으로: // Rabbit.prototype = { 생성자: Rabbit } let Rabbit = new Rabbit(); // {constructor: Rabbit}에서 상속됨 경고(rabbit.constructor == 토끼); // true(프로토타입에서)
constructor
속성을 사용하면 기존 생성자와 동일한 생성자를 사용하여 새 개체를 만들 수 있습니다.
여기처럼:
함수 토끼(이름) { this.name = 이름; 경고(이름); } let Rabbit = new Rabbit("흰 토끼"); let Rabbit2 = new Rabbit.constructor("검은 토끼");
이는 객체가 있는데 어떤 생성자가 사용되었는지 모르거나(예: 제3자 라이브러리에서 가져온 것임) 동일한 종류의 또 다른 생성자를 만들어야 할 때 편리합니다.
하지만 아마도 "constructor"
에 대해 가장 중요한 것은…
...JavaScript 자체는 올바른 "constructor"
값을 보장하지 않습니다.
예, 함수의 기본 "prototype"
에 존재하지만 그게 전부입니다. 나중에 무슨 일이 일어날지는 전적으로 우리에게 달려 있습니다.
특히 기본 프로토타입을 전체적으로 교체하면 그 안에 "constructor"
없습니다.
예를 들어:
함수 토끼() {} Rabbit.prototype = { 점프: 사실 }; let Rabbit = new Rabbit(); 경고(rabbit.constructor === 토끼); // 거짓
따라서 올바른 "constructor"
유지하기 위해 전체를 덮어쓰는 대신 기본 "prototype"
에 속성을 추가/제거하도록 선택할 수 있습니다.
함수 토끼() {} // Rabbit.prototype을 완전히 덮어쓰지 않음 //그냥 추가하자 Rabbit.prototype.jumps = 참 // 기본 Rabbit.prototype.constructor가 유지됩니다.
또는 constructor
속성을 수동으로 다시 만듭니다.
Rabbit.prototype = { 점프: 사실, 생성자: 토끼 }; // 이제 생성자를 추가했기 때문에 생성자도 정확합니다.
이번 장에서는 생성자 함수를 통해 생성된 객체에 대해 [[Prototype]]
을 설정하는 방법을 간략하게 설명했습니다. 나중에 이를 기반으로 하는 고급 프로그래밍 패턴을 살펴보겠습니다.
모든 것이 매우 간단합니다. 몇 가지 참고 사항만 있으면 명확하게 알 수 있습니다.
F.prototype
속성( [[Prototype]]
과 혼동하지 마세요)은 new F()
가 호출될 때 새 객체의 [[Prototype]]
설정합니다.
F.prototype
의 값은 객체이거나 null
이어야 합니다. 다른 값은 작동하지 않습니다.
"prototype"
속성은 생성자 함수에 설정되고 new
로 호출될 때만 이러한 특수 효과를 갖습니다.
일반 객체에서 prototype
특별한 것이 아닙니다.
사용자 = { 이름: "존", 프로토타입: "Bla-bla" // 마법이 전혀 없습니다. };
기본적으로 모든 함수에는 F.prototype = { constructor: F }
가 있으므로 "constructor"
속성에 액세스하여 객체의 생성자를 얻을 수 있습니다.
중요도: 5
아래 코드에서는 new Rabbit
생성한 다음 해당 프로토타입을 수정해 봅니다.
처음에는 다음 코드가 있습니다.
함수 토끼() {} Rabbit.prototype = { 먹는다: 사실이다 }; let Rabbit = new Rabbit(); 경고(rabbit.eats); // 진실
문자열을 하나 더 추가했습니다(강조). 이제 alert
무엇이 표시되나요?
함수 토끼() {} Rabbit.prototype = { 먹는다: 사실이다 }; let Rabbit = new Rabbit(); Rabbit.prototype = {}; 경고(rabbit.eats); // ?
...그리고 코드가 다음과 같다면(한 줄로 대체) 어떻게 될까요?
함수 토끼() {} Rabbit.prototype = { 먹는다: 사실이다 }; let Rabbit = new Rabbit(); Rabbit.prototype.eats = 거짓; 경고(rabbit.eats); // ?
그리고 이렇게 (한 줄 대체)?
함수 토끼() {} Rabbit.prototype = { 먹는다: 사실이다 }; let Rabbit = new Rabbit(); Rabbit.eats 삭제; 경고(rabbit.eats); // ?
마지막 변형:
함수 토끼() {} Rabbit.prototype = { 먹는다: 사실이다 }; let Rabbit = new Rabbit(); Rabbit.prototype.eats 삭제; 경고(rabbit.eats); // ?
답변:
true
.
Rabbit.prototype
에 할당하면 새 개체에 대해 [[Prototype]]
이 설정되지만 기존 개체에는 영향을 주지 않습니다.
false
.
객체는 참조로 할당됩니다. Rabbit.prototype
의 개체는 중복되지 않으며 여전히 Rabbit.prototype
과 rabbit
의 [[Prototype]]
에서 참조하는 단일 개체입니다.
따라서 하나의 참조를 통해 내용을 변경하면 다른 참조를 통해 볼 수 있습니다.
true
.
모든 delete
작업은 객체에 직접 적용됩니다. 여기서 delete rabbit.eats
rabbit
에서 eats
속성을 제거하려고 시도하지만 해당 속성이 없습니다. 따라서 수술은 아무런 효과가 없습니다.
undefined
.
eats
속성은 프로토타입에서 삭제되어 더 이상 존재하지 않습니다.
중요도: 5
생성자 함수에 의해 생성된 임의의 객체 obj
있다고 상상해 보십시오. 어떤 객체인지는 모르지만 이를 사용하여 새 객체를 생성하고 싶습니다.
우리 그렇게 할 수 있나요?
let obj2 = new obj.constructor();
이러한 코드가 올바르게 작동하도록 하는 obj
에 대한 생성자 함수의 예를 들어보세요. 그리고 그것이 잘못 작동하게 만드는 예입니다.
"constructor"
속성이 올바른 값을 가지고 있다고 확신하는 경우 이러한 접근 방식을 사용할 수 있습니다.
예를 들어 기본 "prototype"
건드리지 않으면 다음 코드가 확실히 작동합니다.
함수 사용자(이름) { this.name = 이름; } let user = new User('John'); user2 = new user.constructor('Pete'); 경고(사용자2.이름); // 피트 (일했어요!)
User.prototype.constructor == User
이기 때문에 작동했습니다.
...하지만 누군가가 User.prototype
덮어쓰고 User
참조하기 위해 constructor
다시 만드는 것을 잊어버리면 실패할 것입니다.
예를 들어:
함수 사용자(이름) { this.name = 이름; } User.prototype = {}; // (*) let user = new User('John'); user2 = new user.constructor('Pete'); 경고(사용자2.이름); // 한정되지 않은
user2.name
이 undefined
이유는 무엇입니까?
new user.constructor('Pete')
작동 방식은 다음과 같습니다.
먼저 user
에서 constructor
를 찾습니다. 아무것도 아님.
그런 다음 프로토타입 체인을 따릅니다. user
의 프로토타입은 User.prototype
이고 constructor
도 없습니다(왜냐하면 올바르게 설정하는 것을 "잊었기" 때문입니다!).
체인 위로 더 올라가면 User.prototype
은 일반 객체이고 프로토타입은 내장 Object.prototype
입니다.
마지막으로 내장 Object.prototype
의 경우 내장 Object.prototype.constructor == Object
있습니다. 그래서 사용됩니다.
마지막으로 let user2 = new Object('Pete')
.
아마도 그것은 우리가 원하는 것이 아닐 것입니다. 우리는 new Object
아닌 new User
만들고 싶습니다. 이것이 누락된 constructor
의 결과입니다.
(궁금하신 경우를 대비해 new Object(...)
호출은 인수를 객체로 변환합니다. 이는 이론적인 것입니다. 실제로는 아무도 값이 있는 new Object
호출하지 않으며 일반적으로 우리는 new Object
사용하지 않습니다. 물건을 만드는 것).