>웹 프론트엔드 >JS 튜토리얼 >자바스크립트 프로토타입 상속_자바스크립트 기술에 대해 이야기해 봅시다.

자바스크립트 프로토타입 상속_자바스크립트 기술에 대해 이야기해 봅시다.

WBOY
WBOY원래의
2016-05-16 16:31:451229검색

진정한 의미에서 Javascript는 객체지향 언어가 아니며 전통적인 상속 방법을 제공하지 않습니다. 그러나 자체적으로 제공하는 프로토타입 속성을 사용하여 상속을 달성하는 프로토타입 상속 방법을 제공합니다.

프로토타입 및 프로토타입 체인

프로토타입 상속에 대해 이야기하기 전에 프로토타입과 프로토타입 체인에 대해 먼저 이야기해야 합니다. 이는 결국 프로토타입 상속을 구현하는 기반입니다.
Javascript에서 각 함수에는 자신의 프로토타입을 가리키는 프로토타입 속성 프로토타입이 있고 이 함수에 의해 생성된 객체에도 이 프로토타입을 가리키는 __proto__ 속성이 있으며 함수의 프로토타입은 객체이므로 이 객체에도 __proto__는 자신의 프로토타입을 가리키며, Object 객체의 프로토타입에 도달할 때까지 계층별로 더 깊어져 프로토타입 체인을 형성합니다. 아래 그림은 Javascript에서 프로토타입과 프로토타입 체인의 관계를 잘 설명하고 있습니다.

각 함수는 Function 함수에 의해 생성된 개체이므로 각 함수에는 Function 함수의 프로토타입을 가리키는 __proto__ 속성도 있습니다. 여기서 짚고 넘어가야 할 점은 실제로 프로토타입 체인을 형성하는 것은 함수의 프로토타입 속성이 아니라 각 객체의 __proto__ 속성이라는 점인데, 이것이 매우 중요합니다.

프로토타입 상속

기본 모드

코드 복사 코드는 다음과 같습니다.

var 상위 = 함수(){
This.name = '부모' ;
} ;
Parent.prototype.getName = function(){
this.name을 반환하세요 ;
} ;
Parent.prototype.obj = {a : 1} ;

var 하위 = 함수(){
This.name = '자식' ;
} ;
Child.prototype = new Parent() ;

var parent = new Parent() ;
var child = new Child() ;

console.log(parent.getName()) ; //부모
console.log(child.getName()); //자식

이것은 프로토타입 상속을 구현하는 가장 간단한 방법입니다. 상위 클래스의 객체를 하위 클래스 생성자의 프로토타입에 직접 할당하여 하위 클래스의 객체가 상위 클래스의 프로토타입에 있는 속성에 액세스할 수 있도록 하고 부모 클래스 생성자. 이 메소드의 프로토타입 상속 다이어그램은 다음과 같습니다.

이 방법의 장점은 구현이 매우 간단하고 특별한 작업이 필요하지 않은 반면, 하위 클래스가 상위 클래스에서와 동일한 초기화 작업을 수행해야 하는 경우 단점도 분명합니다. 생성자의 경우 하위 클래스에서 수행되어야 합니다. 생성자의 상위 클래스에서 작업을 반복합니다.

코드 복사 코드는 다음과 같습니다.

var 상위 = 함수(이름){
This.name = 이름 || '부모' ;
} ;
Parent.prototype.getName = function(){
this.name을 반환하세요 ;
} ;
Parent.prototype.obj = {a : 1} ;

var Child = 함수(이름){
This.name = 이름 || '자식' ;
} ;
Child.prototype = new Parent() ;

var parent = new Parent('myParent') ;
var child = new Child('myChild') ;

console.log(parent.getName()) ; //myParent
console.log(child.getName()); //myChild

위의 경우에는 name속성만 초기화하면 되는데 계속해서 초기화 작업이 늘어나게 되면 이 방법은 매우 불편합니다. 따라서 다음과 같은 개선된 방법이 있다.

빌드 생성자

코드 복사 코드는 다음과 같습니다.

var 상위 = 함수(이름){
This.name = 이름 || '부모' ;
} ;
Parent.prototype.getName = function(){
this.name을 반환하세요 ;
} ;
Parent.prototype.obj = {a : 1} ;

var Child = 함수(이름){
Parent.apply(this,arguments) ;
} ;
Child.prototype = new Parent() ;

var parent = new Parent('myParent') ;
var child = new Child('myChild') ;

console.log(parent.getName()) ; //myParent
console.log(child.getName()); //myChild

위 메서드는 하위 클래스 생성자에서 Apply를 통해 상위 클래스의 생성자를 호출하여 동일한 초기화 작업을 수행합니다. 이렇게 하면 상위 클래스에서 초기화 작업이 아무리 많이 수행되더라도 하위 클래스도 동일한 작업을 수행할 수 있습니다. 초기화 작업. 하지만 위 구현에는 여전히 문제가 있습니다. 상위 클래스 생성자는 하위 클래스 생성자에서 한 번, 하위 클래스 프로토타입을 할당할 때 한 번 실행됩니다. 따라서 여전히 개선이 필요합니다.

코드 복사 코드는 다음과 같습니다.
var 상위 = 함수(이름){
This.name = 이름 || '부모' ;
} ;
Parent.prototype.getName = function(){
this.name을 반환하세요 ;
} ;
Parent.prototype.obj = {a : 1} ;
var Child = 함수(이름){

Parent.apply(this,arguments) ;
} ;
Child.prototype = Parent.prototype ;

var parent = new Parent('myParent') ;

var child = new Child('myChild') ;

console.log(parent.getName()) ; //myParent

console.log(child.getName()); //myChild

이런 방식으로 하위 클래스 생성자에서 상위 클래스의 생성자를 한 번만 실행하면 되며 동시에 상위 클래스의 프로토타입에 있는 속성을 상속받을 수 있습니다. 이는 원래 의도에 더 부합합니다. 재사용이 필요한 콘텐츠를 넣는 프로토타입의 프로토타입에서는 재사용 가능한 콘텐츠만 프로토타입에 상속합니다. 위 메소드의 프로토타입은 다음과 같습니다.

임시 생성자 패턴(성배 패턴)

위의 생성자 패턴을 차용한 마지막 개선 버전에는 여전히 문제가 있습니다. 즉, 상위 클래스의 프로토타입을 하위 클래스의 프로토타입에 직접 할당하는 경우, 즉 하위 클래스의 프로토타입이 수정되는 경우 문제가 발생합니다. , 이 수정 사항은 상위 클래스의 프로토타입에도 영향을 미치므로 상위 클래스 객체는 확실히 모든 사람이 보고 싶어하는 것이 아닙니다. 이 문제를 해결하기 위해 임시 생성자 패턴이 있습니다.

코드 복사 코드는 다음과 같습니다.
var 상위 = 함수(이름){
This.name = 이름 || '부모' ;
} ;
Parent.prototype.getName = function(){
this.name을 반환하세요 ;
} ;
Parent.prototype.obj = {a : 1} ;
var Child = 함수(이름){

Parent.apply(this,arguments) ;
} ;
var F = new Function(){};
F.prototype = 상위.prototype ;
Child.prototype = new F() ;

var parent = new Parent('myParent') ;

var child = new Child('myChild') ;

console.log(parent.getName()) ; //myParent

console.log(child.getName()); //myChild

이 메소드의 프로토타입 상속 다이어그램은 다음과 같습니다.

부모 클래스 프로토타입과 하위 클래스 프로토타입 사이에 임시 생성자 F를 추가하면 하위 클래스 프로토타입과 상위 클래스 프로토타입 사이의 연결이 끊어지므로 하위 클래스 프로토타입이 수정될 때 상위 클래스 프로토타입에는 영향을 주지 않습니다.

나만의 방법

성배 모드는 "자바스크립트 모드"로 끝났는데, 위의 어떤 방법을 사용해도 쉽게 발견되지 않는 문제가 있습니다. 'Parent'의 프로토타입 속성에 obj 객체 리터럴 속성을 추가했지만 한 번도 사용되지 않은 것을 볼 수 있습니다. 성배 모델을 기반으로 다음 상황을 살펴보겠습니다.

코드 복사 코드는 다음과 같습니다.
var 상위 = 함수(이름){
This.name = 이름 || '부모' ;
} ;
Parent.prototype.getName = function(){
this.name을 반환하세요 ;
} ;
Parent.prototype.obj = {a : 1} ;
var Child = 함수(이름){

Parent.apply(this,arguments) ;
} ;
var F = new Function(){};
F.prototype = 상위.prototype ;
Child.prototype = new F() ;

var parent = new Parent('myParent') ;

var child = new Child('myChild') ;

console.log(child.obj.a) //1

console.log(parent.obj.a) //1
child.obj.a = 2;
console.log(child.obj.a) //2
console.log(parent.obj.a) //2

위 상황에서 자식 객체 obj.a를 수정하면 상위 클래스의 프로토타입에 있는 obj.a도 수정되어 공유 프로토타입과 동일한 문제가 발생하게 됩니다. 이러한 상황은 child.obj.a에 액세스할 때 프로토타입 체인을 따라 부모 클래스의 프로토타입을 찾은 다음 obj 속성을 찾은 다음 obj.a를 수정하기 때문에 발생합니다. 다음 상황을 살펴보겠습니다.

코드 복사 코드는 다음과 같습니다.

var 상위 = 함수(이름){
This.name = 이름 || '부모' ;
} ;
Parent.prototype.getName = function(){
this.name을 반환하세요 ;
} ;
Parent.prototype.obj = {a : 1} ;

var Child = 함수(이름){
Parent.apply(this,arguments) ;
} ;
var F = new Function(){};
F.prototype = 상위.prototype ;
Child.prototype = new F() ;

var parent = new Parent('myParent') ;
var child = new Child('myChild') ;

console.log(child.obj.a) //1
console.log(parent.obj.a) //1
child.obj.a = 2;
console.log(child.obj.a) //2
console.log(parent.obj.a) //2

여기서 중요한 문제가 있습니다. 객체가 프로토타입의 속성에 액세스할 때 프로토타입의 속성은 객체에 대해 읽기 전용입니다. 즉, 하위 객체는 obj 객체를 읽을 수 있지만 수정할 수는 없습니다. 참조이므로 자식이 obj를 수정해도 프로토타입의 obj에는 영향을 미치지 않습니다. 단지 자신의 개체에 obj 속성을 추가하고 상위 클래스 프로토타입의 obj 속성을 덮어쓰는 것뿐입니다. 자식 개체가 obj.a를 수정하면 먼저 프로토타입의 obj에 대한 참조를 읽습니다. 이때 child.obj와 Parent.prototype.obj는 동일한 개체를 가리키므로 자식이 obj.a를 수정하면 됩니다. 영향을 미치게 됩니다. Parent.prototype.obj.a의 값은 상위 클래스의 개체에 영향을 줍니다. AngularJS에서 $scope 중첩의 상속 방법은 Javascript의 프로토타입 상속으로 구현됩니다.
위의 설명에 따르면, 서브클래스 객체에서 접근하는 프로토타입이 부모 클래스 프로토타입과 동일한 객체라면 위와 같은 상황이 발생하므로, 부모 클래스 프로토타입을 복사한 후 서브클래스 프로토타입에 할당하면 됩니다. 따라서 하위 클래스가 프로토타입의 속성을 수정하면 상위 클래스 프로토타입의 복사본만 수정되고 상위 클래스 프로토타입에는 영향을 주지 않습니다. 구체적인 구현은 다음과 같습니다.

코드 복사 코드는 다음과 같습니다.

var deepClone = 함수(소스,대상){
소스 = 소스 || {} ;
var toStr = Object.prototype.toString ,
arrStr = '[객체 배열]' ;
for(소스의 var i){
If(source.hasOwnProperty(i)){
            var 항목 = 소스[i] ;
If(항목 유형 === '객체'){
target[i] = (toStr.apply(item).toLowerCase() === arrStr) : [] ? {} ;
                  deepClone(항목,대상[i]);                                                                     }그밖에{
                  deepClone(item,target[i]) ;
            }
}
}
복귀대상 ;
} ;
var 상위 = 함수(이름){
This.name = 이름 || '부모' ;
} ;
Parent.prototype.getName = function(){
this.name을 반환하세요 ;
} ;
Parent.prototype.obj = {a : '1'} ;
var Child = 함수(이름){

Parent.apply(this,arguments) ;
} ;
Child.prototype = deepClone(Parent.prototype) ;

var child = new Child('child') ;

var parent = new Parent('부모') ;

console.log(child.obj.a) //1

console.log(parent.obj.a) //1
child.obj.a = '2' ;
console.log(child.obj.a) //2
console.log(parent.obj.a) //1

위의 모든 고려 사항을 바탕으로 Javascript 상속의 구체적인 구현은 다음과 같습니다. 여기서는 Child와 Parent가 모두 함수인 경우만 고려합니다.

코드 복사 코드는 다음과 같습니다.

var deepClone = 함수(소스,대상){
    출처 = 출처 || {} ;
    var toStr = Object.prototype.toString ,
        arrStr = '[객체 배열]' ;
    for(소스의 var i){
        if(source.hasOwnProperty(i)){
            var 항목 = 소스[i] ;
            if(항목 유형 === '객체'){
                target[i] = (toStr.apply(item).toLowerCase() === arrStr) : [] ? {} ;
                deepClone(item,target[i]) ;   
            }그밖에{
                deepClone(item,target[i]) ;
            }
        }
    }
    복귀 대상 ;
} ;

var 확장 = 함수(부모,자식){
    아이 = 아이 || 함수(){} ;
    if(부모 === 정의되지 않음)
        아이를 돌려보내세요 ;
    //借用父类构造函数
    하위 = 함수(){
        Parent.apply(this,argument) ;
    } ;
    //통합深拷贝继承父类원원型   
    Child.prototype = deepClone(Parent.prototype) ;
    //중형 생성자属性
    Child.prototype.constructor = 자식 ;
} ;

总结

더 많은 내용이 있고 Javascript가 中实现继承是十分灵活多样的이며 并没有一种最好的这么, 需要根据는 서로 다른 방식으로 사용되지 않습니다. ,最重要的是要理解Javascript中实现继承的원본, 也就是원형 및 원본 유형 链的问题,只要理解了这些,自己实现继承就可以游刃有余。

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.