>  기사  >  웹 프론트엔드  >  공통 패턴 및 프로토타입 체인 이해

공통 패턴 및 프로토타입 체인 이해

PHP中文网
PHP中文网원래의
2017-06-20 09:37:301755검색

내용:

  • 객체 생성의 여러 모드와 생성 과정

  • 프로토타입 체인 프로토타입 이해, prototype__proto__([ [프로토타입]]) 관계prototype与 __proto__[[Prototype]])的关系

  • 继承的几种实现


1.常见模式与原型链的理解

a.构造函数创建

function Test() {// 
}

流程

  • 创建函数的时候会默认为Test创建一个prototype属性,Test.prototype包含一个指针指向的是Object.prototype

  • prototype默认会有一个constructor,且Test.prototype.constructor = Test

  • prototype里的其它方法都是从Object继承而来


示例
// 调用构造函数创建实例
var instance = new Test()

此处的instance包含了一个指针指向构造函数的原型,(此处的指针在chrome里叫__proto__,也等于[[Prototype]]


示例

b.原型模式
由上我们可以知道,默认创建的prototype属性只拥有constructor和继承至Object的属性,原型模式就是为prototype添加属性和方法

Test.prototype.getName = ()=> {
    alert('name')
}

此时的instance实例就拥有了getName方法,因为实例的指针是指向Test.prototype的

instance.__proto__ === Test.prototype

如下图所示


897RVF]E5@IX$)`IVJ3BOSY.png

这里我们可得知:实例instance与构造函数之间是通过原型prototype来相关联的。

c.组合模式
这种模式我们用的最多,其实也是原型模式的另一种写法,只不过有一点小区别而已

function Test() {}

Test.prototype = {
    getName() {
        alert('name')
    }
}

我们经常会这么直接重写prototype方法,由上我们可知,prototype会默认自带constructor属性指向构造函数本身,那么重写以后呢?

Test.prototype.constructor === Object 
// 而并不等于Test了// 因为重写以后相当于利用字面量方式创建一个实例对象,这个实例的构造函数是指向Object本身的

当然我们也可以手动赋值constructor

Test.prototype = {
    constructor: Test,
    getName() {
        alert('name')
    }
}

那么又会有疑问了constructor要不要有何意义?我觉得constructor意义仅仅是为了来鉴别原型所属的构造函数吧。

当需要获取某个属性的时候,会先从实例中查找,没有就根据指针所指向的原型去查找,依次向上,直到实例的指针__proto__指向为null时停止查找,例如:

// 1 读取nameinstance.name 

// 2 instance.__proto__ === Test.prototypeTest.prototype.name

// 3 Test.prototype.__proto__ === Object.prototypeObject.prototype.name

// 4Object.prototype.__proto__ === null

当找到了这个属性就会直接返回,而不会继续查找,即使这个属性值为null,想要继续查找,我们可以通过delete操作符来实现。

由这里我们自然可以想到Array, Date, Function, String,都是一个构造函数,他们的原型的指针都是指向Object.prototype,它们就像我这里定义的Test一样,只不过是原生自带而已

d.几个有用的方法

  • Object.getPrototypeOf() 获取某个实例的指针所指向的原型

    Object.getPrototypeOf(instance) === Test.prototype
  • hasOwnProperty 判断一个属性是存在于实例中还是存在于原型中,如图所示:


    NY~N}CNR`}8W%4QA$M8LFE4.png
  • in操作符,无论该属性是否可枚举

    'name' in instance  // true
    'getName' in instance // true

    无论属性是在实例中,还是在原型中都返回true,所以当我们需要判断一个属性存在与实例中,还是原型中有2种办法

    // 一种就是使用hasOwnProperty判断在实例中
    // 另一种判断在原型中
    instance.hasOwnProperty('getName') === false && 'getName' in instance === true
  • for ... in

    다양한 상속 구현

    1. 공통 패턴 및 프로토타입 체인의 이해

    a.
    Object.keys(instance)// ["name"]
    🎜 process🎜🎜🎜🎜함수를 생성하면 기본적으로 테스트용 프로토타입 속성이 생성됩니다. Test.prototype에는 Object.prototype🎜을 가리키는 포인터가 포함되어 있습니다. 🎜🎜🎜prototype에는 기본적으로 생성자가 있으며 Test.prototype.constructor = Test🎜🎜🎜🎜프로토타입의 다른 메소드는 Object🎜🎜
🎜🎜🎜Example🎜🎜
// 定义父类function Parent() {}Parent.prototype.getName = ()=> {
    console.log('parent')
}// 实例化父类let parent = new Parent()// 定义子类function Child() {}
Child.prototype = parent 
// 实例化子类let child = new Child()

child.getName() // parent// 此时
child.constructor === parent.constructor === Parent
🎜여기 인스턴스에는 프로토타입을 가리키는 포인터가 포함되어 있습니다. 생성자(여기서 포인터는 크롬에서 __proto__라고 하며 [[Prototype]]와 동일함)🎜🎜🎜🎜Example🎜🎜🎜b. Prototype 모드🎜위에서 프로토타입 속성이 생성되었음을 알 수 있습니다. 기본적으로 Object에서 상속받은 생성자와 속성만 가지고 있습니다. 프로토타입 모드는 프로토타입에 속성과 메서드를 추가하는 것입니다🎜
function Parent(name) {this.name = namethis.colors = ['red']
}
Parent.prototype.getName = function() {console.log(this.name)
}// 实例化父类let parent = new Parent()function Child(age, name) {
    Parent.call(this, name)this.age = age
}
Child.prototype = parent 
// 实例化子类let child = new Child(1, 'aaa')
child.getName() // parent
🎜 이때 인스턴스 인스턴스의 포인터는 Test를 가리키므로 getName 메서드가 있습니다. .prototype🎜
class Parent {
    constructor(name) {this.name = namethis.colors = ['red']
    }
    getName() {
        console.log(this.name)
    }
}class Child extends Parent {
    constructor(age, name) {super(name)
    }
}

let child = new Child(1, 'aaa')
child.getName() // parent
🎜아래 그림과 같이 🎜🎜 🎜🎜897RVF]E5@IX$)`IVJ3BOSY .png🎜🎜🎜여기서 프로토타입 프로토타입을 통해 인스턴스와 생성자가 관련되어 있음을 알 수 있습니다. 🎜🎜c. 조합 모드🎜우리는 이 모드를 가장 많이 사용합니다. 실제로는 약간의 차이만 있을 뿐 프로토타입 모드를 작성하는 또 다른 방법입니다.🎜rrreee🎜위에서 우리는 프로토타입 방법을 직접 다시 작성하는 경우가 많습니다. , 프로토타입에는 기본적으로 생성자 자체를 가리키는 생성자 속성이 있으므로 다시 작성한 후에는 어떻게 되나요? 🎜rrreee🎜물론 생성자를 수동으로 할당할 수도 있습니다🎜rrreee🎜그러면 다시 질문이 생길 것입니다. 생성자의 의미는 무엇입니까? 생성자의 의미는 단지 프로토타입이 속한 생성자를 식별하는 것 뿐이라고 생각합니다. 🎜🎜특정 속성을 가져와야 할 경우 먼저 인스턴스에서 검색합니다. 그렇지 않은 경우 포인터가 가리키는 프로토타입에 따라 검색한 다음 인스턴스 포인터 __proto__까지 올라갑니다. code>는 null을 가리키고 검색을 중지합니다. 예: 🎜rrreee🎜 이 속성이 발견되면 검색을 계속하지 않고 직접 반환됩니다. 검색을 계속하려면 다음을 사용할 수 있습니다. <code>삭제 연산자. 🎜🎜여기서 우리는 자연스럽게 Array, Date, Function, String이 모두 생성자이고 해당 프로토타입 포인터가 Object.prototype을 가리킨다고 생각할 수 있습니다. >Test 여기서 정의했습니다. 🎜🎜d. 인스턴스 포인터가 가리키는 특정 프로토타입을 가져오는 몇 가지 유용한 메서드 🎜🎜🎜🎜Object.getPrototypeOf() 🎜rrreee🎜🎜🎜hasOwnProperty는 그림에 표시된 대로 속성이 인스턴스에 있는지 아니면 프로토타입에 있는지 확인합니다. 🎜🎜🎜🎜🎜NY~N}CNR`}8W%4QA$M8LFE4.png🎜🎜🎜🎜🎜 > 연산자, 속성이 열거 가능한지 여부 🎜rrreee🎜는 속성이 인스턴스에 있든 프로토타입에 있든 true를 반환하므로 속성이 인스턴스에 있는지 프로토타입에 있는지 확인해야 할 때 🎜rrreee🎜에는 2가지 메서드가 있습니다. 🎜🎜for... in 연산자는 동일하지만 열거 가능한 속성만 나열됩니다. ie8 버전의 버그는 속성이 열거 가능한지 여부입니다. 예를 들어 나열됩니다🎜🎜 🎜🎜🎜D(%S__GN8404{H9X6PW$DVK.png🎜🎜


name是在实例中定义的,getName是在原型中定义的

  • Object.keys()则不一样,它返回一个对象上所有可枚举的属性,仅仅是该实例中的

    Object.keys(instance)// ["name"]
  • e.总结
    以上讨论了构造函数,原型和实例的关系:

    • 每个构造函数都有原型对象

    • 每个原型对象都有一个constructor指针指向构造函数

    • 每个实例都有一个__proto__指针指向原型

    2.继承

    其实是一个道理,这里我们不难想到,将Child.prototype指向parent实例,就是利用原型实现的继承,而为了每个实例都拥有各自的colors和name,也就是基础属性,在Child的构造函数中call调用了Parent的构造函数,相当于每次实例化的时候都初始化一遍colors和name,而不是所有实例共享原型链中的colors和name。

    继承的实质是利用构造函数的原型 = 某个构造函数的实例,以此来形成原型链。例如

    // 定义父类function Parent() {}Parent.prototype.getName = ()=> {
        console.log(&#39;parent&#39;)
    }// 实例化父类let parent = new Parent()// 定义子类function Child() {}
    Child.prototype = parent 
    // 实例化子类let child = new Child()
    
    child.getName() // parent// 此时
    child.constructor === parent.constructor === Parent

    a.最经典的继承模式

    function Parent(name) {this.name = namethis.colors = [&#39;red&#39;]
    }
    Parent.prototype.getName = function() {console.log(this.name)
    }// 实例化父类let parent = new Parent()function Child(age, name) {
        Parent.call(this, name)this.age = age
    }
    Child.prototype = parent 
    // 实例化子类let child = new Child(1, &#39;aaa&#39;)
    child.getName() // parent

    这里会让我想到ES6中的class继承

    class Parent {
        constructor(name) {this.name = namethis.colors = [&#39;red&#39;]
        }
        getName() {
            console.log(this.name)
        }
    }class Child extends Parent {
        constructor(age, name) {super(name)
        }
    }
    
    let child = new Child(1, &#39;aaa&#39;)
    child.getName() // parent

    위 내용은 공통 패턴 및 프로토타입 체인 이해의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

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