JavaScript之對象建立

1.構造函數模式

JavaScript中的構造函數是經過new調用的,也就是說,經過new關鍵字調用的函數都被認爲是構造函數。javascript

在構造函數的內部,this的指向是新建立的對象Objecthtml

若是構造函數沒有顯式的return表達式,則會隱式的返回新建立的對象——this對象。java

function Foo () {
  this.name = 'rccoder';
}

Foo.prototype.test = function () {
  console.log (this.name);
}

var bar = new Foo();

bar.name;    // rccoder
bar.test();  // rccoder

在構造函數中,顯式的return會影響返回的值,可是僅限於返回的是 一個對象當返回值不是一個對象的時候,實際上會返回一個新建立的對象;當返回值就是一個對象的時候,返回的不是新建立的對象,而是自己就要返回的那個對象瀏覽器

function Foo () {
  return 2;
}

new Foo();    // Foo {} 返回的不是2,而是新建立了一個對象

function Bar () {
  this.name = 'rccoder';
  return {
    foo: 1
  }
}

new Bar();   // Object {foo: 1}  返回要返回的那個對象
(new Bar()).name  // undefined
(new Bar()).foo   // 1

構造函數模式主要有如下幾個特色:閉包

  1. 沒有顯式的返回對象函數

  2. 直接將屬性和方法賦值給this對象ui

  3. 沒有return語句this

經過new關鍵字調用的函數都被認爲是構造函數。.net

new以後產生的是一個新的對象,能夠在new的時候傳遞參數,而且把這個參數的值賦值給this 指針,這樣,傳遞進去的內容,就變成了新產生的對象的屬性或者方法。prototype

爲了讓代碼看起來更加的「優雅」,構造函數的首字母都是大寫。

除此以外,用構造函數產生的實例中,他的原型都會默認的包含一個constructor屬性,會指向構造函數。這樣就可以知道這個對象是從哪兒建立的,也就是說可以區分這個對象的類型了(下面的工廠模式就沒法區分對象的類型)。

function Foo () {
  this.value = 1;
}

test = new Foo ();

test.constructor  == Foo ();

固然,構造函數也是能夠直接執行的,而不是必定要new,直接執行的化「構造函數」中的this指向的就再也不是新產生的對象了(實際上這種狀況下就和普通的函數同樣,並不會產生新的對象),每每在瀏覽器中是window.

構造函數在每次new的時候產生的實例都是從新建立的,所以不一樣實例上的同名函數是不相等的。

function Foo () {
  this.test = function () {

  };
}

var a = new Foo();
var b = new Foo();

a.test === b.test    // false

因此說,構造函數每次new都是產生一個新的實例,而且這個實例的屬性和方法是獨享的。這樣每每形成了一些浪費(屬性是獨有的能夠理解,可是就方法而言,大多數每每是同樣的,這和咱們想要的可能有點差異)。

2.工廠模式

爲了避免去使用new關鍵字,上面提到的構造函數必須顯式的返回。當前這個時候就能夠理解爲不是構造函數了。

function Foo () {
  var value = 1;

  return {
    method: function () {
      return value;
    }
  };

};

Foo.prototype = {
  foo: function () {

  }
};

new Foo();
Foo();

上面加不加new的返回結果是徹底同樣的,都是一個新建立的,擁有method屬性的對象。嗯。若是對閉包有所理解的話,他返回的就是一個閉包!

須要注意的是,上面返回的是一個包含method屬性的自定義對象,因此他並不返回Foo.prototype.

(new Foo()).foo    // undefined
(Foo()).foo        // undefined

按照正常人的思路,通常選擇用new來調用函數老是顯得很奇怪,這也估計就是大多人說不要使用new關鍵字來調用函數的緣由了,由於若是忘記new就會產生難以察覺的錯誤。

嗯,是時候引出工廠模式了:

function Foo () {
  var obj = {};
  obj.value = 'rccoder';

  var privateValue = 2;

  obj.method = function (value) {
    this.value = value;
  };

  obj.getPrivate = function () {
    return privateValue;
  };
  
  return obj;
}

就像上面的代碼同樣,有個工廠,就這樣生產出了一個個的工人。工廠模式解決了多個比較類似的對象重複建立的問題。可是這個建立只單純的一個建立,但並不知道這個對象是從哪裏建立的,也就是說沒法去區分這個對象的類型。

固然還有一些其餘的缺點,好比因爲新建立的對象只是簡單的建立,因此不能共享原型上的方法,若是要實現所謂的繼承,就要從另外的一個對象去拷貝全部的屬性...嗯,他放棄了原型,爲了去防止new帶來的問題。

3.原型模式

在構造函數模式中提到每次new以後建立的新的對象是互相獨立的,是獨享的。

構造函數每次new都是產生一個新的實例,而且這個實例的屬性和方法是獨享的。這樣每每形成了一些浪費(屬性是獨有的能夠理解,可是就方法而言,大多數每每是同樣的,這和咱們想要的可能有點差異)

就最後一句而言,咱們或許能夠這樣寫:

function Foo (value) {
  this.value = value;
  this.method = method;
}

function method () {
  console.log (this.value);
}

這樣把方法去放在外面,在構造函數裏面去調用這個函數,好像就hack的解決了上面的問題。可是這個函數好像就是全局函數了,而且和Foo()看上去並不怎麼愉快的是一家人,談封裝也就有些牽強。

這種去共享方法的問題用prototype看似就能夠解決,畢竟他產生的屬性和方法是全部產生的實例所共享的。

function Foo () {
  ...
};

Foo.prototype.value = 'rccoder';
Foo.prototype.method = function () {
  console.log (this.value);
};

var test = new Foo ();
test.method();    // rccoder

這樣看起來好像是可行的,當須要找某個對象的屬性的時候,每每直接看有沒有這個屬性,沒有的話再按照原型鏈向上尋找,而不是去尋找構造函數。

原型是動態的,因此不要隨便的去修改原型。這個修改後會當即影響實例的結果。

若是咱們有Foo.prototype = Bar.prototype 的寫法,改變這兩個對象任何一個的原型都會影響另一個,這在大多的狀況下是不可取的。

通常狀況下不建議對原型作修改,由於極可能因爲代碼量太多致使維護太困難。

另外,還記得用原型模式的初衷嗎?是要公用方法,而不是公用屬性。純粹的用原型會一樣的公用屬性,這在不少狀況下看起來是很鬱悶的。因此可能須要咱們把原型和構造函數結合起來使用。

4.優雅混合使用構造函數與原型

這或許是比較理想話的使用方法了,用構造函數來區分獨享的屬性,用原型來共享你們都用的方法。

function Foo (value1, value2) {
  this.value1 = value1;
  this.value2 = value2;
}

Foo.prototype.method = function () {
  console.log (this.value1)
};

test1 = new Foo (2, 3);
test1.method();  // 2

test2 = new Foo (4, 5);
test2.method()   // 4

哦,對了,你可能會看見這樣寫上面的代碼

function Foo (value1, value2) {
  this.value1 = value1;
  this.value2 = value2;
}

Foo.prototype = {
  constructor: Foo,
  method: function () {
    console.log (this.value1);
  }
}

test = new Foo (2, 3);
test.method();

別怕,這只是覆蓋了Foo的原型而已,是真的覆蓋到連constructor是誰都不認識了,因此須要手動的是想一下,指向誰呢?正常人的話指向的應該是構造函數吧。

參考資料:

  1. JavaScript密碼花園

  2. JavaScript 原型理解與建立對象應用-於江水的博客

原文連接:http://life.rccoder.net/javascript/1216.html

相關文章
相關標籤/搜索