JavaScript 프로토타입 개념 코드 예제에 대한 심층적인 이해(그림)

黄舟
풀어 주다: 2017-03-11 14:44:57
원래의
1534명이 탐색했습니다.

프로토타입은 JavaScript에서 이해하기 어려운 개념입니다. 프로토타입과 관련된 속성은 많습니다. 객체에는 "[[prototype]]" 속성이 있고, 함수 객체에는 "prototype" 속성이 있으며, 프로토타입 객체에는 "constructor" 속성이 있습니다.

이 글은 프로토타입과 프로토타입에 관련된 속성 관계를 이해하기 위해 작성되었습니다.

이 글을 통해 프로토타입을 명확하게 이해하고, 지금 프로토타입 여정을 시작하실 수 있으실 거라 믿습니다.

프로토타입의 이해

프로토타입 소개에 앞서 먼저 프로토타입이 무엇인지부터 알아볼까요?

JavaScript에서는 객체의 속성 상속도 프로토타입을 통해 실현될 수 있습니다. JavaScript 객체는 모두 "[[Prototype]]" 내부 속성을 포함합니다. 이 속성에 해당하는 것이 객체의 프로토타입입니다.

"[[Prototype]]"은 개체의 내부 속성이므로 직접 액세스할 수 없습니다. 따라서 객체의 프로토타입을 편리하게 보기 위해 Firefox와 Chrome에서는 "__proto__" 비표준 (일부 브라우저에서는 지원되지 않음) 접근자를 제공합니다(ECMA에서는 표준 객체 프로토타입 접근자 "Object.getPrototype( 물체)").

예제 분석

예제를 통해 프로토타입 관련 개념을 살펴보겠습니다.

function Person(name, age){
    this.name = name;
    this.age = age;

    this.getInfo = function(){
        console.log(this.name + " is " + this.age + " years old");
    };
}

var will = new Person("Will", 28);
로그인 후 복사

위 코드에서는 Person 생성자를 통해 will 객체가 생성됩니다. 객체의 윌을 통해 프로토타입에 대해 단계별로 알아봅시다.

1단계: 객체 will의 프로토타입 보기

다음 코드를 통해 객체 will의 프로토타입을 볼 수 있습니다.

console.log(will.__proto__);
console.log(will.constructor);
로그인 후 복사

결과 분석:

  • "Person {}" 개체는 개체의 프로토타입입니다. Chrome에서 이를 확장하면 "Person {}"이 프로토타입 개체로 표시되는 것을 볼 수 있습니다. 또한 "__proto__" 속성(프로토타입의 프로토타입에 해당)이 있습니다.

  • 이 코드에서는 "constructor" 속성도 사용됩니다. JavaScript의 프로토타입 객체 에는 프로토타입을 가리키는 모든 인스턴스를 생성하는 생성자 에 해당하는 "constructor" 속성도 포함되어 있습니다.

    • "constructor" 속성을 통해 객체가 배열 유형인지 확인할 수 있습니다

      function isArray(myArray) {
          return myArray.constructor.toString().indexOf("Array") > -1;
      }
      로그인 후 복사
    • 여기 will 객체 자체에는 "constructor" 속성이 없지만 프로토타입 체인 검색을 통해 will 프로토타입(will.__proto__)의 "constructor" 속성을 찾아 Person 함수를 얻었습니다.

2단계: 객체 프로토타입 보기 will(will.__proto__)

will의 프로토타입 "Person {}"도 객체이므로 "will의 프로토타입 (will.__proto__) 프로토타입"도 확인할 수 있습니다.

다음 코드 실행:

console.log(will.__proto__ === Person.prototype);
console.log(Person.prototype.__proto__);
console.log(Person.prototype.constructor);
console.log(Person.prototype.constructor === Person);
로그인 후 복사

결과 분석:

  • 먼저 "will.__proto__ === Person.prototype"을 살펴보세요. JavaScript에서 각 함수에는 프로토타입 속성이 있습니다. 함수를 생성자로 사용하여 인스턴스를 생성하면 함수의 프로토타입 속성 값이 모든 객체 인스턴스에 프로토타입으로 할당됩니다(즉, 인스턴스의 __proto__ 속성을 설정합니다. ), 즉 모든 인스턴스의 프로토타입은 함수의 프로토타입 속성 을 참조합니다. 생성자의 프로토타입 속성을 이해한 후에는 첫 번째 문장의 결과가 왜 true인지 확실히 이해할 수 있습니다.

    • prototype 속성은 함수 객체에 고유한 속성입니다. 함수 객체가 아닌 경우에는 그러한 속성이 없습니다.

  • "Person.prototype.__proto__" 문을 통해 will 객체 프로토타입의 프로토타입을 얻으면 "Object {}" 객체를 얻게 되며, 나중에 모든 객체를 참조하세요. 프로토타입은 "Object {}" 객체로 추적됩니다.

  • 프로토타입 객체 "Person.prototype"의 "생성자"의 경우 이전 소개에 따르면 Person 함수 자체에 해당합니다.

위에서 볼 수 있듯이 "Person.prototype" 객체와 Person 함수 객체는 "constructor" 및 "prototype" 속성을 통해 서로를 참조합니다(예: 이후 그림 참조) 이 상호 참조 관계).

3단계: 객체의 프로토타입 보기 Object

이전 부분에서 볼 수 있듯이 will의 프로토타입은 프로토타입은 "Object {}" 객체입니다. 실제로 JavaScript에서는 모든 객체의 프로토타입이 "Object {}" 객체로 추적됩니다.

코드를 통해 "Object {}" 개체를 살펴보겠습니다.

console.log(Person.prototype.__proto__ === Object.prototype);
console.log(typeof Object);
console.log(Object);
console.log(Object.prototype);
console.log(Object.prototype.__proto__);
console.log(Object.prototype.constructor);
로그인 후 복사

다음 코드를 통해 확인할 수 있습니다.

  • Object 객체 자체는 함수 객체입니다.

  • Object 함수이기 때문에 프로토타입 속성이 있어야 하므로 "Object.prototype"의 값이 프로토타입 객체 "Object {}"임을 알 수 있습니다.

  • "Object.prototype" 객체의 "constructor" 속성에 접근하면 Object 함수를 얻게 됩니다.

  • 另外,当通过”Object.prototype.__proto__”获取Object原型的原型的时候,将会得到”null”,也就是说”Object {}”原型对象就是原型链的终点了。

Step 4: 查看对象Function的原型

在上面的例子中,Person是一个构造函数,在JavaScript中函数也是对象,所以,我们也可以通过”__proto__”属性来查找Person函数对象的原型。

console.log(Person.__proto__ === Function.prototype);
console.log(Person.constructor === Function)
console.log(typeof Function);
console.log(Function);
console.log(Function.prototype);
console.log(Function.prototype.__proto__);
console.log(Function.prototype.constructor);
로그인 후 복사

结果分析 :

  • 在JavaScript中有个Function对象(类似Object),这个对象本身是个函数;所有的函数(包括Function,Object)的原型(__proto__)都是”Function.prototype”。

  • Function对象作为一个函数,就会有prototype属性,该属性将对应”function () {}”对象。

  • Function对象作为一个对象,就有”__proto__”属性,该属性对应”Function.prototype”,也就是说,”Function.__proto__ === Function.prototype”

  • 对于Function的原型对象”Function.prototype”,该原型对象的”__proto__”属性将对应”Object {}”

对比prototype和__proto__

对于”prototype”和”__proto__”这两个属性有的时候可能会弄混,”Person.prototype”和”Person.__proto__”是完全不同的。

在这里对”prototype”和”__proto__”进行简单的介绍:

  • 对于所有的对象,都有__proto__属性,这个属性对应该对象的原型

  • 对于函数对象,除了__proto__属性之外,还有prototype属性,当一个函数被用作构造函数来创建实例时,该函数的prototype属性值将被作为原型赋值给所有对象实例(也就是设置实例的__proto__属性)

图解实例

通过上面结合实例的分析,相信你一定了解了原型中的很多内容。

但是现在肯定对上面例子中的关系感觉很凌乱,一会儿原型,一会儿原型的原型,还有Function,Object,constructor,prototype等等关系。

现在就对上面的例子中分析得到的结果/关系进行图解,相信这张图可以让你豁然开朗。

对于上图的总结如下:

  • 所有的对象都有”__proto__”属性,该属性对应该对象的原型

  • 所有的函数对象都有”prototype”属性,该属性的值会被赋值给该函数创建的对象的”__proto__”属性

  • 所有的原型对象都有”constructor”属性,该属性对应创建所有指向该原型的实例的构造函数

  • 函数对象和原型对象通过”prototype”和”constructor”属性进行相互关联

通过原型改进例子

在上面例子中,”getInfo”方法是构造函数Person的一个成员,当通过Person构造两个实例的时候,每个实例都会包含一个”getInfo”方法。

var will = new Person("Will", 28);
var wilber = new Person("Wilber", 27);
로그인 후 복사

前面了解到,原型就是为了方便实现属性的继承,所以可以将”getInfo”方法当作Person原型(Person.__proto__)的一个属性,这样所有的实例都可以通过原型继承的方式来使用”getInfo”这个方法了。

所以对例子进行如下修改:

function Person(name, age){
    this.name = name;
    this.age = age;
}

Person.prototype.getInfo = function(){
    console.log(this.name + " is " + this.age + " years old");
};
로그인 후 복사

修改后的结果为:

原型链

因为每个对象和原型都有原型,对象的原型指向对象的父,而父的原型又指向父的父,这种原型层层连接起来的就构成了原型链。

在”理解JavaScript的作用域链“一文中,已经介绍了标识符和属性通过作用域链和原型链的查找。

这里就继续看一下基于原型链的属性查找。

属性查找

当查找一个对象的属性时,JavaScript 会向上遍历原型链,直到找到给定名称的属性为止,到查找到达原型链的顶部(也就是 “Object.prototype”), 如果仍然没有找到指定的属性,就会返回 undefined。

看一个例子:

function Person(name, age){
    this.name = name;
    this.age = age;
}

Person.prototype.MaxNumber = 9999;
Person.__proto__.MinNumber = -9999;

var will = new Person("Will", 28);

console.log(will.MaxNumber);
// 9999
console.log(will.MinNumber);
// undefined
로그인 후 복사

在这个例子中分别给”Person.prototype “和” Person.__proto__”这两个原型对象添加了”MaxNumber “和”MinNumber”属性,这里就需要弄清”prototype”和”__proto__”的区别了。

“Person.prototype “对应的就是Person构造出来所有实例的原型,也就是说”Person.prototype “属于这些实例原型链的一部分,所以当这些实例进行属性查找时候,就会引用到”Person.prototype “中的属性。

属性隐藏

当通过原型链查找一个属性的时候,首先查找的是对象本身的属性,如果找不到才会继续按照原型链进行查找。

这样一来,如果想要覆盖原型链上的一些属性,我们就可以直接在对象中引入这些属性,达到属性隐藏的效果。

看一个简单的例子:

function Person(name, age){
    this.name = name;
    this.age = age;
}

Person.prototype.getInfo = function(){
    console.log(this.name + " is " + this.age + " years old");
};

var will = new Person("Will", 28);
will.getInfo = function(){
    console.log("getInfo method from will instead of prototype");
};

will.getInfo();
// getInfo method from will instead of prototype
로그인 후 복사

对象创建方式影响原型链

会到本文开始的例子,will对象通过Person构造函数创建,所以will的原型(will.__proto__)就是”Person.prototype”。

同样,我们可以通过下面的方式创建一个对象:

var July = {
    name: "July",
    age: 28,
    getInfo: function(){
        console.log(this.name + " is " + this.age + " years old");
    },
}

console.log(July.getInfo());
로그인 후 복사

当使用这种方式创建一个对象的时候,原型链就变成下图了,July对象的原型是”Object.prototype”也就是说对象的构建方式会影响原型链的形式。

hasOwnProperty

“hasOwnProperty”是”Object.prototype”的一个方法,该方法能判断一个对象是否包含自定义属性而不是原型链上的属性,因为”hasOwnProperty” 是 JavaScript 中唯一一个处理属性但是不查找原型链的函数。

相信你还记得文章最开始的例子中,通过will我们可以访问”constructor”这个属性,并得到will的构造函数Person。这里结合”hasOwnProperty”这个函数就可以看到,will对象并没有”constructor”这个属性。

从下面的输出可以看到,”constructor”是will的原型(will.__proto__)的属性,但是通过原型链的查找,will对象可以发现并使用”constructor”属性。

“hasOwnProperty”还有一个重要的使用场景,就是用来遍历对象的属性。

function Person(name, age){
    this.name = name;
    this.age = age;
}

Person.prototype.getInfo = function(){
    console.log(this.name + " is " + this.age + " years old");
};

var will = new Person("Will", 28);

for(var attr in will){
    console.log(attr);
}
// name
// age
// getInfo

for(var attr in will){
    if(will.hasOwnProperty(attr)){
        console.log(attr);
    }
}
// name
// age
로그인 후 복사

总结

本文介绍了JavaScript中原型相关的概念,对于原型可以归纳出下面一些点:

  • 所有的对象都有”[[prototype]]”属性(通过__proto__访问),该属性对应对象的原型

  • 所有的函数对象都有”prototype”属性,该属性的值会被赋值给该函数创建的对象的”__proto__”属性

  • 所有的原型对象都有”constructor”属性,该属性对应创建所有指向该原型的实例的构造函数

  • 函数对象和原型对象通过”prototype”和”constructor”属性进行相互关联

还有要强调的是文章开始的例子,以及通过例子得到的一张”普通对象”,”函数对象”和”原型对象”之间的关系图,当你对原型的关系迷惑的时候,就想想这张图(或者重画一张当前对象的关系图),就可以理清这里面的复杂关系了。

通过这些介绍,相信一定可以对原型有个清晰的认识。

위 내용은 JavaScript 프로토타입 개념 코드 예제에 대한 심층적인 이해(그림)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
최신 이슈
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿
회사 소개 부인 성명 Sitemap
PHP 중국어 웹사이트:공공복지 온라인 PHP 교육,PHP 학습자의 빠른 성장을 도와주세요!