欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JavaScript中的原型prototype完全解析

 更新時(shí)間:2016年05月10日 17:30:37   作者:Asrocky  
這篇文章主要介紹了JavaScript中的原型prototype完全解析,prototype可是js界"一切皆對(duì)象"論調(diào)的重要支撐,講解了__proto__屬性和原型鏈等干貨,需要的朋友可以參考下

   要理解JS中的prototype, 首先必須弄清楚以下幾個(gè)概念
   1. JS中所有的東西都是對(duì)象

   2. JS中所有的東西都由Object衍生而來, 即所有東西原型鏈的終點(diǎn)指向Object.prototype
 

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

   3. JS中構(gòu)造函數(shù)和實(shí)例(對(duì)象)之間的微妙關(guān)系
   構(gòu)造函數(shù)通過定義prototype來約定其實(shí)例的規(guī)格, 再通過 new 來構(gòu)造出實(shí)例, 他們的作用就是生產(chǎn)對(duì)象.
   而構(gòu)造函數(shù)(方法)本身又是方法(Function)的實(shí)例, 因此也可以查到它的__proto__(原型鏈)

   Object       / function F() {} 這樣子的就是構(gòu)造函數(shù)啦, 一個(gè)是JS原生API提供的, 一個(gè)是自定義的
   new Object() / new F()           這樣子的就是實(shí)例啦
   實(shí)例就"只能"查看__proto__來得知自己是基于什么prototype被制造出來的,
   而"不能"再重新定義實(shí)例的prototype妄想創(chuàng)造出實(shí)例的實(shí)例了.

   實(shí)踐出真知, 只有自己動(dòng)手觀察/思考才能真正領(lǐng)悟:

  // 先來看看構(gòu)造函數(shù)到底是什么
  // 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__);

   你可能已經(jīng)暈了, 我們來分解一下。

prototype
   prototype輸出的格式為: 構(gòu)造函數(shù)名 原型
   首先看下Object.prototype輸出了什么?
   Object {} -> 前面的Object為構(gòu)造函數(shù)的名稱, 后面的那個(gè)表示原型, 這里是一個(gè){}, 即一個(gè)Object對(duì)象的實(shí)例(空對(duì)象)
   那么 F {} 我們就明白是什么意思了, F 就是構(gòu)造函數(shù)的名稱, 原型也是一個(gè)空對(duì)象

  // 再來看看由構(gòu)造函數(shù)構(gòu)造出來的實(shí)例
  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__);

   我們?cè)偕钊胍稽c(diǎn), 定義下 F 的原型看看到底會(huì)發(fā)生些什么?

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

   這樣我們就清楚的看到 i 是由 F 構(gòu)造出來的, 原型是 {a: function}, 就是原本的空對(duì)象原型新增了一個(gè) a 方法

   我們?cè)贀Q一種情況, 完全覆蓋 F 的原型會(huì)怎么樣?
   

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

   咦~ 為什么這里表明 i 是由 Object 構(gòu)造出來的? 不對(duì)吧!
   因?yàn)槲覀兺耆珜?F 的prototype覆蓋, 其實(shí)也就是將原型指定為對(duì)象{a: function}, 但這會(huì)造成原本的constructor信息丟失, 變成了對(duì)象{a: function}指定的constructor.
   那么對(duì)象{a: function}的constructor是什么呢?
   因?yàn)閷?duì)象{a: function}其實(shí)就相對(duì)于

  var o = {a: function() {}} // new了一個(gè)Object

   那么o的constructor當(dāng)然是 Object 啦

   我們來糾正下這個(gè)錯(cuò)誤

  function F() {}
  F.prototype = {
    a: function() {}
  }
  // 重新指定正確的構(gòu)造函數(shù)
  F.prototype.constructor = F;
  var i = new F();
  // undefined       F {a: function, constructor: function}
  console.log(i.prototype, i.__proto__);

   現(xiàn)在又能得到正確的原型信息了~

原型鏈

   然后來看看什么原型鏈又是個(gè)什么東西?
   簡(jiǎn)單的來講和OOP中的繼承關(guān)系(鏈)是一樣的, 一層一層往上找, 直至最終的 Object.prototype

2016510172352211.jpg (560×248)

      最最關(guān)鍵的是要弄清楚JS中哪些東西是(實(shí)例)對(duì)象, 這個(gè)簡(jiǎn)單了, JS中所有東西都是對(duì)象!
   再要弄清楚就是任何一個(gè)對(duì)象都是有一個(gè)原型的!

   那么我們來證明一下:
  

  Object // 這是一個(gè)函數(shù), 函數(shù)是 Function 的實(shí)例對(duì)象, 那么就是由 Function 構(gòu)造出來的
  Object.__proto__ == Function.prototype // 那么Object的原型, true
  // 這個(gè)是一個(gè)普通對(duì)象了, 因此屬于 Object 的實(shí)例
  Function.prototype.__proto__ == Object.prototype // true
  // 這已經(jīng)是原型鏈的最頂層了, 因此最終的指向 null
  Object.prototype.__proto__ == null // true

  Function // 這也是一個(gè)函數(shù), 沒錯(cuò)吧!
  Function.__proto__ == Function.prototype // true
  
  function A() {} // 這是一個(gè)自定義的函數(shù), 終歸還是一個(gè)函數(shù), 沒錯(cuò)吧! 
  A.__proto__ == Function.prototype // 任何函數(shù)都是 Function 的實(shí)例, 因此A的原型是?
  var a = new A()
  a.__proto__ == A.prototype // 實(shí)例a是由A構(gòu)造函數(shù)構(gòu)造出來的, 因此a的原型是由A的prototype屬性定義的
  A.prototype.__proto__ == Object.prototype // 普通對(duì)象都是 Object 的示例

Prototype和__proto__
每一個(gè)對(duì)象都包含一個(gè)__proto__,指向這個(gè)的對(duì)象的“原型”。
類似的事情是,每一個(gè)函數(shù)都包含一個(gè)prototype,這個(gè)prototype對(duì)象干什么的了?

咱們看看如下代碼,用構(gòu)造函數(shù)來創(chuàng)建一個(gè)對(duì)象(上面是用字面量的形式創(chuàng)建對(duì)象)。

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

試想想,這個(gè)foo對(duì)象的__proto__會(huì)指向什么?

2016510172448163.png (163×68)

一個(gè)包含constructor屬性的對(duì)象?看不太懂沒關(guān)系,把函數(shù)Foo的prototype屬性打印出來,對(duì)比一下就知道了。

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

2016510172512274.png (183×69)

原來,new出來的對(duì)象foo的__proto__就只指向函數(shù)Foo的prototype。

foo.__proto__ --> Foo.prototype

JS這么設(shè)計(jì)有何意義了?回憶下上面說的,在JS的世界中,對(duì)象不是根據(jù)類(模具)創(chuàng)建出來的,而是從原型(另一個(gè)對(duì)象)衍生出來的。

當(dāng)我們執(zhí)行new操作創(chuàng)建一個(gè)新的對(duì)象時(shí),先不深入new操作的具體實(shí)現(xiàn),但有一點(diǎn)我們是肯定的——就是為新對(duì)象的__proto__指向一個(gè)原型對(duì)象。

就剛才這段代碼

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

foo.__proto__到底要指向誰了?你怎么不能指向Foo這個(gè)函數(shù)本身吧,雖然函數(shù)也是對(duì)象,這個(gè)有機(jī)會(huì)會(huì)詳細(xì)講。但如何foo.__proto__指向Foo固然不合適,因?yàn)镕oo是一個(gè)函數(shù),有很多邏輯代碼,foo作為一個(gè)對(duì)象,繼承邏輯處理沒有任何意義,它要繼承的是“原型對(duì)象”的屬性。

所以,每個(gè)函數(shù)會(huì)自動(dòng)生成一個(gè)prototype對(duì)象,由這個(gè)函數(shù)new出來的對(duì)象的__proto__就指向這個(gè)函數(shù)的prototype。

foo.__proto__ --> Foo.prototype

總結(jié)
說了這么多,感覺還是沒完全說清楚,不如上一張圖。我曾經(jīng)參考過其他網(wǎng)友的圖,但總覺得哪里沒說清楚,所以我自己畫了一張圖,如果覺得我的不錯(cuò),請(qǐng)點(diǎn)個(gè)贊!(老子可是費(fèi)了牛勁才畫出來)。

2016510172555695.png (800×600)

咱們就著這張圖,記住如下幾個(gè)事實(shí):

1. 每個(gè)對(duì)象中都有一個(gè)_proto_屬性。

JS世界中沒有類(模具)的概念,對(duì)象是從另一個(gè)對(duì)象(原型)衍生出來的,所以每個(gè)對(duì)象中會(huì)有一個(gè)_proto_屬性指向它的原型對(duì)象。(參考左上角的那個(gè)用字面量形式定義的對(duì)象obj,它在內(nèi)存中開辟了一個(gè)空間存放對(duì)象自身的屬性,同時(shí)生成一個(gè)_proto_指向它的原型——頂層原型對(duì)象。)

2. 每個(gè)函數(shù)都有一個(gè)prototype屬性。

“構(gòu)造函數(shù)”為何叫構(gòu)造函數(shù),因?yàn)樗獦?gòu)造對(duì)象。那么根據(jù)上面第一條事實(shí),構(gòu)造出來的新對(duì)象的_proto_屬性指向誰了?總不能指向構(gòu)造函數(shù)自身,雖然它也是個(gè)對(duì)象,但你不希望新對(duì)象繼承函數(shù)的屬性與方法吧。所以,在每個(gè)構(gòu)造函數(shù)都會(huì)有一個(gè)prototype屬性,指向一個(gè)對(duì)象作為這個(gè)構(gòu)造函數(shù)構(gòu)造出來的新對(duì)象的原型。

3. 函數(shù)也是對(duì)象。

每個(gè)函數(shù)都有一些通用的屬性和方法,比如apply()/call()等。但這些通用的方法是如何繼承的呢?函數(shù)又是怎么創(chuàng)建出來的呢?試想想,一切皆對(duì)象,包括函數(shù)也是對(duì)象,而且是通過構(gòu)造函數(shù)構(gòu)造出來的對(duì)象。那么根據(jù)上面第二條事實(shí),每個(gè)函數(shù)也會(huì)有_proto_指向它的構(gòu)造函數(shù)的prototype。而這個(gè)構(gòu)造函數(shù)的函數(shù)就是Function,JS中的所有函數(shù)都是由Function構(gòu)造出來的。函數(shù)的通用屬性與方法就存放在Function.prototype這個(gè)原型對(duì)象上。

相關(guān)文章

最新評(píng)論