1. 서문 이 글은 Microsoft의 마스터 Scott Allen의 Prototypes and Inheritance in JavaScript를 번역한 것입니다. 이 글은 Prototype이 무엇인지, Prototype을 통해 상속이 가능한 이유에 대한 자세한 분석과 설명을 제공합니다. .JS OO를 이해하는데 가장 좋은 작품 중 하나입니다. 번역이 좋지 않으면 수정해서 추가해주세요.
2. 텍스트 자바스크립트의 객체지향은 다른 언어와 다릅니다. 익히기 전에 익숙한 객체지향 개념을 잊어버리는 것이 좋습니다. JS의 OO는 더 강력하고, 더 논쟁적이고, 더 유연합니다.
1. 클래스와 객체
JS는 전통적인 관점에서 본 객체지향 언어입니다. 속성과 동작은 하나의 객체로 결합됩니다. 예를 들어 JS의 배열은 속성과 메서드(예: push, reverse, pop 등)로 구성된 개체입니다.
var myArray = [1, 2];
myArray.push(3);
myArray.reverse();
myArray.pop();
var length = myArray.length;
문제는 : 이러한 방법(푸시)은 어디에서 왔습니까? 일부 정적 언어(예: JAVA)는 클래스를 사용하여 객체의 구조를 정의합니다. 하지만 JS는 "클래스"가 없는(classless) 언어입니다. 상속할 각 배열에 대해 이러한 메서드를 정의하는 "Array"라는 클래스가 없습니다. JS는 동적이기 때문에 필요할 때 마음대로 객체에 메서드를 추가할 수 있습니다. 예를 들어, 다음 코드는 2차원 공간의 좌표를 나타내는 객체를 정의하고 그 안에 add 메서드가 있습니다.
var point = {
x : 10,
y : 5,
추가: 함수(otherPoint)
{
this.x = otherPoint.x
this.y =
}
};
우리는 모든 포인트 객체에 add 메소드를 갖기를 원합니다. 또한 모든 포인트 개체에 add 메서드를 추가하지 않고도 모든 포인트 개체가 add 메서드를 공유할 수 있기를 바랍니다. 이를 위해서는 프로토타입이 나타나야 합니다.
2. 프로토타입에 대하여
JS의 모든 객체에는 암시적 속성(상태)이 있습니다. 이는 객체의 프로토타입이라고 불리는 다른 객체에 대한 참조입니다. 물론, 그들도 마찬가지입니다. 해당 프로토타입에 대한 참조를 포함합니다. 프로토타입 참조는 암시적이지만 ECMAScript가 구현한 것이므로 객체의 _proto_(Chrome) 속성을 사용하여 이를 얻을 수 있습니다. 개념적으로 이해하면 아래 그림과 같이 객체와 프로토타입의 관계를 생각해 볼 수 있습니다.
개발자로서 우리는 _proto_ 속성 대신 Object.getPrototypeOf 함수를 사용하여 객체의 프로토타입 참조를 볼 것입니다. 이 기사를 작성하는 시점에는 Object.getPrototypeOf 함수가 Chrome, Firefox 및 IE9에서 지원됩니다. 앞으로 더 많은 브라우저가 이미 ECMAScript 표준 중 하나인 이 기능을 지원할 것입니다. 다음 코드를 사용하여 이전에 생성한 myArray와 포인트 개체가 실제로 두 개의 서로 다른 프로토타입 개체를 참조한다는 것을 증명할 수 있습니다.
Object.getPrototypeOf(point) != 객체 .getPrototypeOf(myArray)
나머지 기사에서도 _proto_를 사용하겠습니다. 주로 _proto_가 다이어그램과 문장에서 더 직관적이기 때문입니다. 그러나 이것이 표준이 아니라는 점을 기억하십시오. Object.getPrototypeOf는 객체의 프로토타입을 가져오는 데 권장되는 방법입니다.
2.1 프로토타입이 특별한 이유는 무엇인가요?
우리는 이미 배열의 push 메소드가 myArray의 프로토타입 객체에서 나온다는 것을 알고 있습니다. 그림 2는 Chrome의 스크린샷입니다. Object.getPrototypeOf 메서드를 호출하여 myArray의 프로토타입 개체를 가져옵니다.
그림 2
myArray의 프로토타입 객체에는 시작 코드에서 사용한 push, pop, reverse 등의 여러 메서드가 포함되어 있습니다. . 프로토타입 객체는 push 메소드의 유일한 소유자인데 myArray를 통해 이 메소드를 어떻게 호출합니까?
myArray.push(3)
어떻게 구현되는지 이해하기 위한 첫 번째 단계는 Protytype이 전혀 특별하지 않다는 점을 깨닫는 것입니다. 프로토타입은 객체입니다. 다른 JS 객체와 마찬가지로 이러한 객체에 메서드와 속성을 추가할 수 있습니다. 그러나 동시에 프로토타입은 특별한 대상이기도 합니다.
프로토타입은 다음 규칙 때문에 특별합니다. 객체에 대해 push 메소드를 호출하거나 특정 속성을 읽으려고 JS에 알릴 때 인터프리터(런타임)는 먼저 객체 자체의 메소드나 속성을 찾습니다. . 인터프리터가 메서드(또는 속성)를 찾지 못하면 _proto_ 참조를 따라 객체 프로토타입의 각 멤버를 찾습니다. myArray에서 push 메소드를 호출하면 JS는 myArray 객체에서 push를 찾지 못하고 myArray의 프로토타입 객체에서 push를 찾습니다. 즉, push 메소드가 호출됩니다(그림 3).
그림 3
내가 설명한 동작은 본질적으로 객체 자체가 프로토타입의 모든 메서드와 속성을 상속한다는 것입니다. JS에서 이러한 상속 관계를 구현하기 위해 클래스를 사용할 필요는 없습니다. 즉, JS 객체는 프로토타입에서 속성을 상속받습니다.
그림 3에서는 각 배열 객체가 자체 상태와 멤버를 유지할 수 있음을 보여줍니다. myArray의 길이 속성이 필요한 경우 JS는 프로토타입에서 길이 값을 찾지 않고 myArray에서 길이 값을 찾습니다. 이 기능을 사용하여 메서드를 "재정의"할 수 있습니다. 즉, 재정의해야 하는 메서드(예: 푸시)를 myArray의 자체 객체에 넣을 수 있습니다. 이렇게 하면 프로토타입에서 푸시 메서드를 효과적으로 숨길 수 있습니다.
3. 프로토타입 공유
JS Prototype의 진정한 장점은 여러 객체가 동일한 프로토타입 객체를 참조할 수 있다는 것입니다. 예를 들어 두 개의 배열을 생성합니다.
var myArray = [ 1, 2];var yourArray = [4, 5, 6];
두 배열은 동일한 프로토타입 객체를 공유하며 다음 코드는 true를 반환합니다.
Object.getPrototypeOf(myArray) === Object.getPrototypeOf(yourArray) ;
두 개의 배열에서 push 메소드를 호출하면 JS는 공통 프로토타입에서 push 메소드를 호출합니다.
프로토타입 객체는 JS에서 이러한 상속 기능을 제공하며 메소드 구현을 공유할 수도 있습니다. 프로토타입도 연결되어 있습니다. 즉, 프로토타입은 객체이고, 프로토타입 객체는 다른 프로토타입 객체에 대한 참조를 가질 수도 있습니다. 그림 2에서 볼 수 있듯이 프로토타입의 _proto_ 속성은 null이 아닌 값이며 다른 프로토타입을 가리킵니다. JS가 push 메서드와 같은 멤버 변수를 찾기 시작하면 이러한 프로토타입 개체 참조를 각각 확인합니다. 이 개체를 찾거나 체인의 끝에 도달할 때까지. 이 체인 방법은 JS에서 상속 및 공유의 유연성을 높입니다.
다음으로 질문할 수 있습니다. 사용자 정의 개체의 프로토타입 참조를 어떻게 설정합니까? 예를 들어 이전에 생성한 객체 포인트의 경우 모든 포인트 객체가 상속할 수 있도록 프로토타입 객체에 add 메서드를 어떻게 추가합니까? 이 질문에 답하기 전에 먼저 JS의 함수를 이해해 봅시다.
4. Funciton에 대하여
JS에서는 함수도 객체입니다. 함수에는 JS의 중요한 기능이 많이 있으며 이 기사에서 그 기능을 모두 나열할 수는 없습니다. 하지만 변수에 함수를 할당하거나 함수를 다른 함수의 매개변수로 사용하는 등의 작업은 오늘날 JS 프로그래밍에서 매우 기본적인 방법입니다.
우리가 주의해야 할 점은 함수가 객체이기 때문에 메서드, 속성 및 프로토타입 객체에 대한 참조가 있다는 것입니다. 다음 코드의 의미를 살펴보겠습니다.
// 이는 true를 반환합니다:
typeof (Array) === "function"
// 다음도 마찬가지입니다:
Object.getPrototypeOf(Array) === Object.getPrototypeOf(function () { })
// 이것도 마찬가지입니다:
Array.prototype != null
코드의 첫 번째 줄은 Array가 JS의 함수임을 증명합니다. 나중에 Array 함수를 호출하여 새 배열 개체를 만드는 방법을 살펴보겠습니다.
코드의 두 번째 줄은 모든 배열 객체가 프로토타입을 공유한다는 것을 앞에서 본 것처럼 Array 객체와 함수 객체가 동일한 프로토타입을 참조한다는 것을 증명합니다.
마지막 줄은 Array 함수에 프로토타입 속성이 있음을 증명합니다. 이 프로토타입 속성을 _proto_ 속성과 혼동하지 마십시오. 사용 목적도, 가리키는 대상도 다릅니다.
// true
Array.prototype == Object .getPrototypeOf(myArray)
// also true
Array.prototype == Object.getPrototypeOf(yourArray);
새로운 지식으로 이전 그림을 다시 그립니다.
그림 5
이제 배열 객체를 생성해야 합니다. 방법 중 하나는 다음과 같습니다.
// 새로운 빈 객체 생성
var o = {};
// 배열 객체와 동일한 프로토타입에서 상속
o.__proto__ = Array.prototype>// 이제 임의의 객체를 호출할 수 있습니다. 배열 메소드 중 ...
o.push(3)
위 코드는 좋아 보이지만 모든 JS 환경이 객체의 _proto_ 속성을 지원하는 것은 아니라는 문제가 있습니다. 다행스럽게도 JS에는 새 객체를 생성하고 객체의 _proto_ 속성을 설정하기 위한 표준 내장 메커니즘이 있습니다. 이것이 "new" 연산자입니다.
var o = new Array()
o .push(3);
JS에서 "new" 연산자는 세 가지 중요한 작업을 수행합니다. 첫째, 새로운 빈 객체를 생성합니다. 다음으로, 호출 함수의 프로토타입 속성을 가리키도록 이 새 객체의 _proto_ 속성을 설정합니다. 마지막으로 호출 함수가 실행되고 "this" 포인터가 새 개체를 가리킵니다. 위 두 줄의 코드를 확장하면 다음과 같은 코드가 나옵니다.
var o = {};
o.__proto__ = Array.prototype;
Array.call(o)
o.push(3); 🎜>
함수의 "호출" 메서드를 사용하면 함수를 호출하고 함수의 "this"가 전달된 새 객체를 가리키도록 지정할 수 있습니다. 물론, 우리는 객체 상속을 달성하기 위해 위의 방법을 통해 우리 자신의 객체를 생성하고 싶습니다. 이 함수는 우리에게 친숙한 생성자입니다.
5. 생성자
생성자는 두 개의 고유 식별자가 있는 일반적인 JS 함수 개체입니다:
1. 첫 번째 문자는 대문자로 표시됩니다(식별하기 쉽습니다).
2. 새 연산자 연결을 사용하여 새 객체를 생성합니다.
Array는 생성자입니다. Array 함수는 new와 연결되고 첫 글자는 대문자로 표시됩니다. JS의 Array 함수는 내장되어 있지만 누구나 자신만의 생성자를 만들 수 있습니다. 실제로 이제 포인트 개체에 대한 생성자를 만들 차례입니다.
var Point = function (x , y) {
this.x = x;
this.y = y;
this.add = function(otherPoint) {
this.x = otherPoint.x; y = otherPoint .y;
}
}
var p1 = new Point(3, 4)
var p2 = new Point(8, 6); );
위 코드에서는 new 연산자와 Point 함수를 사용하여 포인트 개체를 구성했습니다. 메모리에서는 그림 6과 같이 최종 결과를 생각할 수 있습니다.
그림 6 이제 문제는 add 메소드가 모든 포인트 객체에 존재한다는 것입니다. 프로토타입에 대해 우리가 알고 있는 정보를 고려하면 Add 메소드에 대한 코드를 모든 객체에 복사할 필요 없이 Point.prototype에 add 메소드를 추가하는 것이 더 나은 선택이 될 것입니다. 이 목적을 달성하려면 Point.prototype 개체를 일부 수정해야 합니다.
코드 복사
this.y = y;
}
Point.prototype.add = 함수(otherPoint) {
this.x = otherPoint.x ;
this.y = otherPoint.y;
}
var p1 = new Point(3, 4)
var p2 = new Point(8, 6); (p2) ;
알겠습니다! 프로토타입을 사용하여 JS에서 상속을 구현했습니다!
6. 요약
이 글이 프로토타입의 안개를 걷어내는 데 도움이 되기를 바랍니다. 물론 이는 강력하고 유연한 프로토타입에 대한 소개일 뿐입니다. 독자들이 스스로 프로토타입에 대해 더 많이 탐색하고 발견할 수 있기를 바랍니다.