深入理解js中的面向?qū)ο笈c原型、原型鏈(二)

構(gòu)造對象

先看一些老式的方法:
我們可以拋開類,使用字面量來構(gòu)造一個對象:

var obj1 = {
    nick: 'Byron',
    age: 20,
    printName: function(){
        console.log(obj1.nick);
    }
}
var obj2 = {
    nick: 'Casper',
    age: 25,
    printName: function(){
        console.log(obj2.nick);
    }
}

這樣構(gòu)造出來的對象有兩個明顯的問題。

  • 太麻煩了,每次構(gòu)建一個對象都是復(fù)制一遍代碼
  • 如果想個性化,只能通過手工賦值,使用者必需了解對象詳細(xì)

這兩個問題其實也是我們不能拋開類的重要原因,也是類的作用

優(yōu)化一下 使用函數(shù)做自動化
function createObj(nick, age){
  var obj = {
      nick: nick,
      age: age,
      printName: function(){
            console.log(this.nick);
        }
  };
  return obj;
}

var obj3 = createObj('Byron', 30);
obj3.printName();

我們通過創(chuàng)建一個函數(shù)來實現(xiàn)自動創(chuàng)建的對象的過程,至于個性化通過參數(shù)實現(xiàn),開發(fā)者不必再關(guān)注細(xì)節(jié),只需要傳入指定的參數(shù)即可。
這樣做也有個小問題 !
這種方法解決了構(gòu)造過程復(fù)雜,需要了解細(xì)節(jié)的問題,但是構(gòu)造出來的對象類型都是Object,沒有識別度。
該怎么辦呢?這個時候 構(gòu)造函數(shù) 橫空出世?。?!

構(gòu)造函數(shù)

面向?qū)ο缶幊痰牡谝徊剑褪且蓪ο?。前面說過,對象是單個實物的抽象。通常需要一個模板,表示某一類實物的共同特征,然后對象根據(jù)這個模板生成。

典型的面向?qū)ο缶幊陶Z言(比如 C++Java),都有“類”(class)這個概念。所謂“類”就是對象的模板,對象就是“類”的實例。但是,JavaScript 語言的對象體系,不是基于“類”的,而是基于構(gòu)造函數(shù)(constructor)和原型鏈(prototype)。

JavaScript 語言使用構(gòu)造函數(shù)(constructor)作為對象的模板。所謂”構(gòu)造函數(shù)”,就是專門用來生成實例對象的函數(shù)。它就是對象的模板,描述實例對象的基本結(jié)構(gòu)。一個構(gòu)造函數(shù),可以生成多個實例對象,這些實例對象都有相同的結(jié)構(gòu)。

構(gòu)造函數(shù)就是一個普通的函數(shù),但是有自己的特征和用法。

var Vehicle = function () {
  this.price = 1000;
};

上面代碼中,Vehicle就是構(gòu)造函數(shù)。為了與普通函數(shù)區(qū)別,構(gòu)造函數(shù)名字的第一個字母通常大寫。

構(gòu)造函數(shù)的特點有兩個。

  • 函數(shù)體內(nèi)部使用了this關(guān)鍵字,代表了所要生成的對象實例。
  • 生成對象的時候,必須使用new命令。

new

下面先介紹new命令。


function Person(name) {
    this.name = name
    this.sayName = function() {
        console.log(this.name)
    }
}
var p = new Person('hunger')

以上代碼的執(zhí)行過程如下:

1、執(zhí)行 new Person

  • 創(chuàng)建一個空對象 {},假設(shè)名字是 tmpObj
  • 執(zhí)行 Person 函數(shù),執(zhí)行過程中對 this 操作就是對 tmpObj 進(jìn)行操作
  • 函數(shù)執(zhí)行完后返回剛剛創(chuàng)建的 tmpObj

2、把 tmpObj 賦值給 p (p也指向同一個對象)

在看下面的例子 new的本質(zhì)什什么

function Modal(msg){
    this.msg = msg
}
var modal = new Modal()
  • 創(chuàng)建類的實例。這步是把一個空的對象的 proto 屬性設(shè)置為 Modal.prototype 。
  • 初始化實例。函數(shù) Modal 被傳入?yún)?shù)并調(diào)用,關(guān)鍵字 this 被設(shè)定為該實例。
  • 返回實例, 賦值給 modal

instanceof

instanceof是一個操作符,可以判斷對象是否為某個類型的實例

p1 instanceof Person; // true
p1 instanceof Object;// true

instanceof判斷的是對象

1 instanceof Number; // false

構(gòu)造函數(shù) 原型(prototype)

  • 任何函數(shù)使用new表達(dá)式就是構(gòu)造函數(shù)

  • 每個函數(shù)都自動添加一個名稱為prototype屬性,這是一個對象

  • 每個對象都有一個內(nèi)部屬性__proto__(規(guī)范中沒有指定這個名稱,但是瀏覽器都這么實現(xiàn)的) 指向其類型的prototype屬性,類的實例也是對象,其__proto__屬性指向“類”的prototype。

prototype

十分重要的原型圖


原型圖

通過圖示我們可以看出一些端倪,實例可以通過__prop__訪問到其類型的prototype屬性,這就意味著類的prototype對象可以作為一個公共容器,供所有實例訪問。

抽象重復(fù)

我們剛才的問題可以通過這個手段解決

  • 所有實例都會通過原型鏈引用到類型的prototype

  • prototype相當(dāng)于特定類型所有實例都可以訪問到的一個公共容器

重復(fù)的東西移動到公共容器里放一份就可以了 看以下代碼 理解原型鏈

function Person(nick, age){
    this.nick = nick;
    this.age = age;
}
Person.prototype.sayName = function(){
    console.log(this.nick);
}

var p1 = new Person();
p1.sayName();

這時候我們對應(yīng)的關(guān)系是這樣的


原型鏈

在舉幾個經(jīng)典的例子 有如下代碼 畫出原型圖

function People (name){
  this.name = name;
}

People.prototype.walk = function(){
  console.log(this.name + ' is walking');  
}

var p1 = new People('饑人谷');
var p2 = new People('前端');
image.png

其實和上面的是一樣的

有如下代碼,代碼中并未添加 toString方法,這個方法是哪里來的?畫出原型鏈圖進(jìn)行解釋

function People(){
}
var p = new People()
p.toString()
image.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容