第三期:基於紅皮書「建立js對象的6種方法」再討論(1)

這一期所書寫的目的並非爲了介紹建立對象的方法,在紅皮書裏面,例子其實很清楚。在這篇文章中,想討論一下書中細節,並無解釋太清楚的地方。app

工廠模式

//工廠模式書本「
function createPerson(name,age){
    var obj=new Object();
    obj.name=name;
    obj.age=age;
    obj.load=function(){
        console.log(obj.name,obj.age);
    }
    return obj;
}

var toti=createPerson('toti',18);
toti.load();
//」工廠模式書本

工廠模式的一些要點:函數

  • 解決:優化

    • 建立多個類似對象問題this

    • 傳參spa

  • 缺點:不能識別對象類型prototype

  • 優化:通常使用對象字面量寫法,再返回code

  • 思考對象

    • 對象暴露出來,其實也能夠用,可是賦值須要重複。ip

    • 包在一個函數裏面,傳參方便原型鏈

這個模式作了什麼?

  1. 建立一個Object實例對象

  2. 爲實例obj添加屬性、方法

  3. 返回實例obj

至關於使用了一個普通的函數,去讓這個對象能夠複用。

書中:「不能識別對象類型」如何理解?

咱們的實例,當咱們去console.log(toti)的時候。可是工廠模式下,

clipboard.png

咱們去獲取這個實例的時候,只能簡單的判斷它是一個對象。這就是它一個缺點,因而咱們有了後面的構造函數模式

使用對象字面量寫法優化

//工廠模式對象字面量「
function createPerson(name,age){
    var obj={
        name:name,
        age:age,
        load:function(){
            console.log(this.name,this.age);
        }
    }
    return obj;
}

var mary=createPerson('mary',18);
mary.load();
//」工廠模式對象字面量

對象字面量注意問題

this的用法

  • 在obj對象裏面,this.name&this.age指向obj的做用域的name&age

  • 若是不使用this,則name和age,指向createPerson的做用域。

  • 在本例中,使用this.name,name均可以,由於是傳參,因此createPerson和obj兩個做用域變量同樣

構造函數模式

//「 構造函數模式;「 構造函數當作構造函數使用,用new
function Person(name,age){
    this.name=name;
    this.age=age;
    this.load=function(){
        console.log(this.name,this.age);
    }
}

var toti=new Person('toti',18);
toti.load();
console.log(typeof toti);
// 」構造函數模式;」構造函數當作構造函數使用,用new

發生了什麼?

在解釋發生的事情前,咱們要先詳細瞭解下new的做用。在紅皮書中,new的做用,解釋的的確很淺,簡單幾行字,不細讀,很難理清其中的邏輯。

NEW的做用

紅皮書中是這樣解釋的:

  1. 建立一個新對象

  2. 將構造函數的做用域賦值給新對象

  3. 執行構造函數中的代碼

  4. 返回新對象

這幾行文字,一開始我也覺得我讀懂了,可是它實在沒有很好地給讀者說明白new到底怎麼用的。

在這裏,我引用一個我提問中的網友的回答,下面是來自wei0613的回答:

function CO(){
    this.p = 「I’m in constructed object」;
    this.alertP = function(){
        alert(this.p);
    }
}
var o2 = new CO();
var obj  ={};
obj.__proto__ = CO.prototype;
CO.call(obj);
return obj;

他用代碼形式給咱們講解了其中發生了什麼事:

  1. new先建立了一個對象obj

  2. 而後CO函數的原型賦值給對象obj的原型

  3. CO函數再使用call/apply方法把做用域賦給obj

  4. 到這一步才執行構造函數Co裏面的代碼!!!

  5. 返回obj對象

你們可能第一眼,並不能發現我這裏的邏輯和紅皮書的邏輯有什麼區別。首先,紅皮書的邏輯中,很明顯少了原型建立發生在哪一步,若是這裏不清楚,會影響你們理解接下來原型模式和動態原型模式的理解;其次,第4步是最重要的,也就是構造函數的代碼執行,是發生在第4步,call的使用和原型鏈的造成都發生在以前。若是咱們僅僅只有一個大概概念,當咱們遇到對象字面量重寫的時候,咱們極可能發現咱們代碼寫得都沒有問題,可是就是出bug的狀況。這個工做流邏輯你們必定要清楚。

構造函數裏this的做用

在new的時候,所謂的做用域賦給新對象,就是使用了call/apply方法。

  1. Person.apply(obj,arguments);

  2. this就指向了obj對象

  3. 把this的屬性方法一個個添加到obj對象裏面

其餘不是this的屬性和方法,都會被忽略,好比:

function Person(name, age,job){
    this.name=name;
    this.age=age;
    this.load=function(){
        console.log(this.name,this.age);
    }
    var job=job;
}

job就不會被寫入obj對象裏面

clipboard.png

注意,當咱們new Person()的時候,是把函數看成構造函數使用的,可是它也是函數,能夠像普通函數同樣使用

//「 構造函數當作普通函數使用,不用new
function Person(name,age){
    this.name=name;
    this.age=age;
    this.load=function(){
        console.log(this.name,this.age);
    }
}

var mike=Person('mike',18);
//mike.load();會報錯,Person沒有對象返回
window.load();
// 」構造函數當作普通函數使用,不用new

直接使用函數,等於在當前做用域下運行。在這種狀況下,咱們則會把this指向這個環境的做用域,在這裏是window。咱們使用mike.load()會報錯,由於屬性和方法並無添加到mike這個對象中,而是添加到了window這個對象。

若是咱們不用new,也能夠實現this指向咱們想指向的對象

//「 構造函數當作普通函數使用,call,apply
function Person(name,age){
    this.name=name;
    this.age=age;
    this.load=function(){
        console.log(this.name,this.age);
    }
}

var obj={};
Person.call(obj,'mike',18);
obj.load();
// 」構造函數當作普通函數使用,call,apply

這些變式其實都是在充分了解new和this的用法後,天然寫出來的。經過些變式,你們能夠更好理解它們的用法。

相比工廠模式,構造函數模式的優缺點及思考

當咱們console.log(toti):

clipboard.png

咱們回顧工廠模式下,是:

clipboard.png

因此構造函數模式在這裏比工廠模式更好,看到這裏你們應該明白書中:「不能識別對象類型」的問題了。

構造函數模式要點

  • 解決:能夠識別爲一種特定的類型

  • 缺點:構造函數的每一個方法都被實例了一遍

  • 思考:

    • 構造函數開頭大寫,其餘函數開頭小寫

    • 不使用new,至關於普通函數使用,this指向window,爲window添加變量和方法

    • 也可使用call和apply來爲第三方做用域添加

  • 重點:

    • new

    • this

相關文章
相關標籤/搜索