>  기사  >  웹 프론트엔드  >  JavaScript의 프로토타입에 대한 종합적인 분석

JavaScript의 프로토타입에 대한 종합적인 분석

亚连
亚连원래의
2018-05-21 14:53:471392검색

이 글에서는 주로 JavaScript의 프로토타입에 대한 완전한 분석을 소개합니다. Prototype은 js 세계에서 "모든 것이 객체입니다"라는 주장에 대한 중요한 지원이며, 프로토타입 체인과 이를 필요로 하는 기타 유용한 정보를 설명합니다. 참고하세요

JS의 프로토타입을 이해하려면 먼저 다음 개념을 이해해야 합니다
1. JS의 모든 것은 객체입니다. 2. JS의 모든 것은 Object에서 파생됩니다. 즉, 모든 사물의 프로토타입 체인의 끝점입니다. Object .prototype

  // ["constructor", "toString", "toLocaleString", "valueOf", "hasOwnProperty", "isPrototypeOf", 
   // "propertyIsEnumerable", "__defineGetter__", "__lookupGetter__", "__defineSetter__",
   // "__lookupSetter__"]
   console.log(Object.getOwnPropertyNames(Object.prototype));

3. JS에서 생성자와 인스턴스(객체)의 미묘한 관계

생성자는 프로토타입을 정의하여 인스턴스의 사양에 동의한 다음 new를 통해 인스턴스를 구성합니다.

생성자(메서드) 자체는 메소드(Function)의 인스턴스이므로 해당 __proto__(프로토타입 체인)도 찾을 수 있습니다

Object / function F() {} 이것은 생성자이고 하나는 JS 네이티브 API에서 제공 , 하나는 사용자 정의됨

new Object() / new F() 이것이 인스턴스입니다

인스턴스는 자신이 기반으로 하는 프로토타입을 알기 위해 __proto__를 "만" 볼 수 있고,
인스턴스의 프로토타입을 다음과 같이 재정의할 수 "없습니다".

연습은 진정한 지식을 가져옵니다. 스스로 관찰하고 생각해야만 진정으로 이해할 수 있습니다.

  // 先来看看构造函数到底是什么
  // function Empty() {}  function Empty() {}
  console.log(Function.prototype, Function.__proto__);
  // Object {}          function Empty() {}
  console.log(Object.prototype, Object.__proto__);
  function F() {}
  // F {}              function Empty() {}
  console.log(F.prototype, F.__proto__);

어지러울 수도 있습니다. 분석해 보겠습니다.

prototype

프로토타입 출력 형식은 다음과 같습니다. 생성자 이름 프로토타입 먼저 Object.prototype이 출력하는 내용을 살펴보겠습니다. Object {} -> 여기에는 A {}, 즉 Object 개체(빈 개체)의 인스턴스가 있습니다
그러면 F {}가 무엇을 의미하는지 이해하게 됩니다. F는 생성자의 이름이고 프로토타입도 빈 개체입니다.

  // 再来看看由构造函数构造出来的实例
  var o = new Object(); // var o = {};
  // undefined       Object {}
  console.log(o.prototype, o.__proto__);
  function F() {}
  var i = new F();
  // undefined       F {}
  console.log(i.prototype, i.__proto__);

조금 더 깊이 들어가 F 프로토타입을 정의하여 무슨 일이 일어날지 살펴보겠습니다.

  function F() {}
  F.prototype.a = function() {};
  var i = new F();
  // undefined       F {a: function}
  console.log(i.prototype, i.__proto__);

이렇게 하면 i가 F에서 구성되고 프로토타입이 {a: function}이라는 것을 명확하게 알 수 있습니다. 객체 프로토타입에 새로운 a 메소드가 있습니다

상황을 다시 바꿔서 F의 프로토타입을 완전히 덮으면 어떻게 될까요?

function F() {}
  F.prototype = {
    a: function() {}
  };
  var i = new F();
  // undefined       Object {a: function}
  console.log(i.prototype, i.__proto__);

왜~ i가 Object로 구성되었다고 표시되나요?

왜냐면요! 사실 우리는 F의 프로토타입을 완전히 커버할 것입니다. 프로토타입은 객체 {a: function}으로 지정되지만 이로 인해 원래 생성자 정보가 손실되고 객체 {a: function}에 의해 지정된 생성자가 됩니다.

그래서 객체 {a: function}의 생성자는 무엇입니까?
객체 {a: function}은 실제로

var o = {a: function() {}} // new了一个Object

에 상대적이기 때문에 물론 o의 생성자는 객체입니다

이 오류를 수정해 봅시다

  function F() {}
  F.prototype = {
    a: function() {}
  }
  // 重新指定正确的构造函数
  F.prototype.constructor = F;
  var i = new F();
  // undefined       F {a: function, constructor: function}
  console.log(i.prototype, i.__proto__);

이제 우리는 다음을 얻을 수 있습니다. 올바른 프로토타입 정보를 다시 한번~

프로토타입 체인

그럼 프로토타입 체인이 무엇인지 알아볼까요?

간단히 말하면 OOP의 상속관계(체인)과 같습니다. 최종 Object.prototype


2016510172352211.jpg (560×248) 가장 중요한 것은 JS의 어떤 것이 (인스턴스) 객체인지 알아내는 것입니다. JS의 모든 것은 객체입니다.

알아내야 할 또 다른 점은 모든 객체가 객체라는 것입니다.


그럼 증명해 보겠습니다. 다음을 살펴보세요.

  Object // 这是一个函数, 函数是 Function 的实例对象, 那么就是由 Function 构造出来的
  Object.__proto__ == Function.prototype // 那么Object的原型, true
  // 这个是一个普通对象了, 因此属于 Object 的实例
  Function.prototype.__proto__ == Object.prototype // true
  // 这已经是原型链的最顶层了, 因此最终的指向 null
  Object.prototype.__proto__ == null // true

  Function // 这也是一个函数, 没错吧!
  Function.__proto__ == Function.prototype // true
  
  function A() {} // 这是一个自定义的函数, 终归还是一个函数, 没错吧! 
  A.__proto__ == Function.prototype // 任何函数都是 Function 的实例, 因此A的原型是?
  var a = new A()
  a.__proto__ == A.prototype // 实例a是由A构造函数构造出来的, 因此a的原型是由A的prototype属性定义的
  A.prototype.__proto__ == Object.prototype // 普通对象都是 Object 的示例

Prototype 및 __proto__

모든 개체에는 이 개체의 "프로토타입"을 가리키는 __proto__가 포함되어 있습니다. 비슷한 점은 모든 함수에 프로토타입이 포함되어 있다는 것입니다. 이 프로토타입 개체는 무엇을 합니까?
생성자를 사용하여 객체를 생성하는 다음 코드를 살펴보겠습니다(위는 리터럴 형태로 객체를 생성하는 것입니다).

function Foo(){};
var foo = new Foo();
console.log(foo.__proto__);

생각해 보세요. 이 foo 객체의 __proto__는 무엇을 가리키는 걸까요?

2016510172448163.png (163×68)생성자 속성을 포함하는 객체인가요? 잘 이해하지 못해도 상관없습니다. Foo 함수의 프로토타입 속성을 출력해서 비교해 보세요.

function Foo(){};
var foo = new Foo();
console.log(foo.__proto__);
console.log(Foo.prototype);
console.log(foo.__proto__ === Foo.prototype);

2016510172512274.png (183×69)새 개체 foo의 __proto__는 Foo 함수의 프로토타입만 가리키는 것으로 나타났습니다.

foo.__proto__ --> Foo.prototype

JS의 이런 디자인은 무슨의미인가요? 위에서 말한 내용을 기억해 보세요. JS 세계에서는 객체가 클래스(몰드)를 기반으로 생성되지 않고 프로토타입(다른 객체)에서 파생됩니다.

새 개체를 생성하기 위해 새 작업을 수행할 때 새 작업의 구체적인 구현을 다루지는 않지만, 우리가 확신하는 한 가지는 새 개체의 __proto__가 프로토타입 개체를 가리킨다는 것입니다.

이 코드만

function Foo(){};
var foo = new Foo();

foo.__proto__到底要指向谁了?你怎么不能指向Foo这个函数本身吧,虽然函数也是对象,这个有机会会详细讲。但如何foo.__proto__指向Foo固然不合适,因为Foo是一个函数,有很多逻辑代码,foo作为一个对象,继承逻辑处理没有任何意义,它要继承的是“原型对象”的属性。

所以,每个函数会自动生成一个prototype对象,由这个函数new出来的对象的__proto__就指向这个函数的prototype。

foo.__proto__ --> Foo.prototype

总结说了这么多,感觉还是没完全说清楚,不如上一张图。我曾经参考过其他网友的图,但总觉得哪里没说清楚,所以我自己画了一张图,如果觉得我的不错,请点个赞!(老子可是费了牛劲才画出来)。

2016510172555695.png (800×600)

咱们就着这张图,记住如下几个事实:

1. 每个对象中都有一个_proto_属性。

JS世界中没有类(模具)的概念,对象是从另一个对象(原型)衍生出来的,所以每个对象中会有一个_proto_属性指向它的原型对象。(参考左上角的那个用字面量形式定义的对象obj,它在内存中开辟了一个空间存放对象自身的属性,同时生成一个_proto_指向它的原型——顶层原型对象。)

2. 每个函数都有一个prototype属性。

“构造函数”为何叫构造函数,因为它要构造对象。那么根据上面第一条事实,构造出来的新对象的_proto_属性指向谁了?总不能指向构造函数自身,虽然它也是个对象,但你不希望新对象继承函数的属性与方法吧。所以,在每个构造函数都会有一个prototype属性,指向一个对象作为这个构造函数构造出来的新对象的原型。

3. 函数也是对象。

每个函数都有一些通用的属性和方法,比如apply()/call()等。但这些通用的方法是如何继承的呢?函数又是怎么创建出来的呢?试想想,一切皆对象,包括函数也是对象,而且是通过构造函数构造出来的对象。那么根据上面第二条事实,每个函数也会有_proto_指向它的构造函数的prototype。而这个构造函数的函数就是Function,JS中的所有函数都是由Function构造出来的。函数的通用属性与方法就存放在Function.prototype这个原型对象上。

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

全面分析JavaScript面向对象概念中的Object类型与作用域(附有示例)

在javascript中创建对象的各种模式解析(图文教程)

详细解读JavaScript设计模式开发中的桥接模式(高级篇)

위 내용은 JavaScript의 프로토타입에 대한 종합적인 분석의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

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