JavaScript의 상속은 다소 이상합니다. 인터페이스 상속은 구현할 수 없으며 프로토타입 상속에만 의존할 수 있습니다.
프로토타입 체인
프로토타입은 객체입니다. 생성자를 통해 생성된 인스턴스에는 프로토타입의 속성과 메서드를 얻기 위해 프로토타입을 가리키는 포인터가 있습니다. 이런 방식으로 인스턴스 객체에는 생성자의 속성 메서드와 프로토타입의 속성 메서드가 있습니다. 그런 다음 상속해야 하는 생성자의 프로토타입을 이 인스턴스에 지정하면 이 인스턴스의 모든 속성 메서드를 가질 수 있습니다. 상속을 달성합니다.
아래 데모 코드를 살펴보세요.
//声明超类,通过构造函数和原型添加有关属性和方法 function Super(){ this.property = true; } Super.prototype.getSuperValue = function() { return this.property; }; //声明子类的构造函数 function SubType() { this.subproperty = false; } //将子类的原型指向超类的实例,得到超类的一切 SubType.prototype = new Super(); SubType.prototype.constructor = SubType; SubType.prototype.getSubValue = function(){ return this.subproperty; }; //由子类创建对象,测试是否继承超类方法和属性 var instance = new SubType(); console.log(instance.getSuperValue());
모든 함수의 기본 프로토타입은 Object의 인스턴스이므로 기본 프로토타입에는 Object.prototype을 가리키는 내부 포인터가 포함됩니다.
프로토타입과 인스턴스 간의 관계를 확인하려면 instanceof 및 isPrototypeOf를 사용하세요.
instance instanceof Object; Object.prototype.isPrototypeOf(instance);
프로토타입 체인을 사용할 때는 메소드를 신중하게 정의해야 합니다. 하위 클래스가 상위 유형의 메서드나 확장을 재정의해야 하는 경우 프로토타입을 대체하는 명령문 뒤에 배치되어야 해당 하위 클래스가 적용됩니다. 또한 프로토타입 체인을 통해 상속을 구현할 때 객체 리터럴을 사용하여 프로토타입 체인을 재정의하는 프로토타입 메서드를 생성할 수 없습니다.
...... SubType.prototype = new Super(); SubType.prototype = { .... };
이것은 새 개체를 가리키는 포인터를 대체하여 프로토타입 체인을 다시 작성합니다.
프로토타입 체인의 상속 방법에는 두 가지 주요 문제가 있습니다.
1. 참조 유형 값을 포함하는 프로토타입에서 모든 인스턴스가 공유합니다.
이전 글에서 언급한 것처럼 참조 유형 값이 포함된 프로토타입 속성은 모든 인스턴스에서 공유됩니다. 한 인스턴스가 수정되면 다른 인스턴스도 그에 따라 변경되므로 생성자에서 속성을 정의해야 합니다. 프로토타입 체인을 통해 상속할 경우 슈퍼 클래스의 속성이 생성자에 정의되어 있든 프로토타입에 정의되어 있든 상관없이 모두 인스턴스 객체가 되어 서브 클래스에 상속되므로 서브 클래스의 인스턴스에 영향을 미칩니다.
2. 하위 유형의 인스턴스를 생성할 때 매개변수는 상위 유형의 생성자에 전달될 수 없습니다.
프로토타입 체인의 상속은 하위 클래스 프로토타입을 슈퍼 클래스의 인스턴스로 직접 가리킵니다. 이 때 매개 변수는 슈퍼 클래스에 전달될 수 있습니다. 그러나 하위 클래스가 인스턴스를 생성할 때 매개 변수는 하위 클래스의 생성자에만 전달될 수 있고 슈퍼 클래스의 생성자에는 전달될 수 없습니다.
따라서 실제 애플리케이션에서는 프로토타입 체인을 단독으로 사용하는 경우가 거의 없습니다.
관련 코딩 사례
프로토타입 속성 식별
function hasPrototypeProperty(object, name) { return name in object && !object.hasOwnProperty(name); }
생성자에서 프로토타입 객체 사용
function Person(name) { this.name = name; } Person.prototype = { constructor: Person, sayName: function () { console.log(this.name); }, toString: function() { } }; var person1 = new Person('Nicholas'); var person2 = new Person('Greg); console.log(person1 instanceof Person); // true console.log(person1.constructor === Person); // true console.log(person1.constructor === Object); // false console.log(person2 instanceof Person); // true console.log(person2.constructor === Person); // true console.log(person2.constructor === Object); // false
객체 상속
var person1 = { name: 'Nicholas', sayName: function () { console.log(this.name); } }; var person2 = Object.create(person1, { name: { configurable: true, enumerable: true, value: 'Greg', writable: true } }); person1.sayName(); // Nicholas person2.sayName(); // Greg console.log(person1.hasOwnProperty('sayName')); // true console.log(person1.isPropertyOf(person2)); // true console.log(person2.hasOwnProperty('sayName')); // false
모듈 모드
var person = (function () { var age = 25; function getAge() { return age; } function growOlder() { age++; } return { name: 'Nicholas', getAge: getAge, growOlder: growOlder }; }());
범위 생성자
function Person(name) { this.name = name; } Person.prototype.sayName = function() { console.log(this.name); }; var person1 = Person('Nicholas'); console.log(person1 instanceof Person); // false console.log(typeof person1); // undefined console.log(name); // Nicholas