如何建立對象以及jQuery中建立對象的方式(推薦)

1. 使用對象字面量建立對象 key-value

var cat = {
    name: 'tom',
    info: this.name + ': 1212',
    getName: function() {
        return this.name;
    }
};

注意上例屬性info中,使用了this.name,這裏的this指向window對象,請儘可能避免在定義對象屬性時使用表達式,而將有表達式的內容寫入到函數中。javascript

2.使用new建立對象

var dog = new Object();
dog.name = 'tim';
dog.getName = function() {
    return dog.name;
}
  • 可使用delete刪除對象的屬性和方法
delete dog.name;

在window做用域中,不能使用delete刪除var, function定義的屬性和方法,能夠刪除沒有使用var, function定義的屬性和方法java

3. 工廠模式

在實際使用當中,字面量建立對象雖然頗有用,可是它並不能知足咱們的全部需求,咱們但願可以可以和其餘後臺語言同樣建立一個類,而後聲明類的實例就可以屢次使用,而不用每次使用的時候都要從新建立它,因而,便有了工廠模式的出現。jquery

function person(name) {
    var o = new Object();
    o.name = name;
    o.getName = function() {
        return this.name;
    }
    return o;
}
var person1 = person('rose');
var person2 = person('jake');

這種模式在函數的內部建立了一個空對象,而後逐一添加屬性和方法,最後返回,實現了對象得以複用的目的。可是存在2個很大的問題chrome

  • 沒法識別對象的類型
console.log(person1 instanceof person); // false
  • 每一個對象調用的同名方法其實並不一樣一個方法
console.log(person1.getName == person2.getName); // false

其實就至關於每次聲明對象都被從新建立,只不過寫法上簡單了一點而已。瀏覽器

4. 自定義構造函數

var Person = function(name) {
    this.name = name;
    this.getName = function() {
        return this.name;
    }
}

var person1 = new Person('tom');
var person2 = new Person('tim');

使用var或者function聲明函數均可以,只是我寫例子的時候想到什麼就寫了什麼,這個區別在這裏不是重點函數

和工廠模式相比,自定義構造函數沒有在函數內部顯示的建立和返回對象,而是使用this,固然,看上去簡潔了許多,那麼它解決了工廠模式的什麼問題呢?this

console.log(person1 instanceof Person);  // ture
console.log(person1.getName == person2.getName); //false

從上面代碼能夠看出,對象的類別能夠判斷了,person1就是Person的對象,但是2個同名方法任然不是同一個方法,而是從新建立,其實構造函數內部的實現,能夠將上面的代碼寫成這樣來理解spa

var Person = function(name) {
    var this = {};
    this.name = name;
    this.getName = function() {
        return this.name;
    }
    return this;
}

看上去和工廠模式就有點像,只是this的聲明和返回都是隱式的。下一步,咱們將要介紹關鍵先生prototype

原型指針

原型並無那麼神祕,由於在javascript中,它無處不在。爲了瞭解原型,咱們能夠在chrome瀏覽器的console中,隨意建立一個函數

function a(){}

而後繼續輸入

a.prototype

獲得的結果以下

a {
    constructor: function a(),
    _proto_: Object
}

沒錯,獲得的這個a,就是關鍵先生原型了。每個函數都有一個proportype屬性,他就像一個指針同樣指向它的原型,而每個原型,都有一個contructor屬性,指向他的構造函數。
那麼原型在建立對象中有什麼用呢?
一個例子,千錘百煉,以下

function Person(name) {
     this.name = name;
 }
 Person.prototype.getName = function() {
     return this.name;
 }

 var person1 = new Person('rose');
 var person2 = new Person('Jake');

在這裏,咱們將屬性寫在了構造函數裏,將使人頭疼的方法寫在原型裏,看看上面的問題獲得解決沒有

console.log(person1 instanceof Person); // true
console.log(person1.getName === person2.getName); // true

OK,問題完美解決。

固然也能夠將屬性寫入原型中,可是若是那樣的話,屬性就會如同方法同樣被公用了,所以通常來講,屬性會寫入構造函數之中,方法寫入原型之中。固然,這視狀況而定。

若是你想進一步瞭解原型,能夠看下圖。

當咱們使用new person時便會建立一個實例,好比這裏的person1與person2,這裏的實例中,會有一個_proto_屬性指向原型。

  • 原型中的查找機制

當咱們使用實例person1調用方法person.getName( )時,咱們首先找的,是看看構造函數裏面有沒有這個方法,若是構造函數中存在,就直接調用構造函數的方法,若是構造函數不存在,纔回去查找原型中是否存在該方法

function Cat(name) {
    this.name = name;
    this.age = 12;
    this.getName = function() {
        return 'constructor';
    }
}
Cat.prototype.name = 'proto name';
Cat.prototype.getName = function() {
    return this.name;
}

var tim = new Cat('Tim');
console.log(tim.name);  // tim
console.log(tim.getName());  // constructor

能夠看到上例中,當原型和構造函數中擁有一樣的方法和屬性的時候,構造函數中的被執行。
因而,這裏便會有一個十分重要的概念須要理解,那就是this的指向問題。
在整個建立對象的過程中,this到底指向誰?

function Person(name) {
     this.name = name;
    //  this.getName = function() {
    //      return 'constructor';
    //  }
 }
 Person.prototype.getName = function() {
     return this.name;
 }
 Person.prototype.showName = function() {
     return this.getName();
 }

var rose = new Person('rose');
console.log(rose.showName()); //rose

其實在new執行時,構造函數中的this與原型中的this都被強行指向了new建立的實例對象。

若是須要寫在原型上的方法不少的話,還能夠這樣來寫,讓寫法看上去更加簡潔

Person.prototype = {
    constructor: Person,
    getName: function(){},
    showName: function(){},
    ...
}

constructor:Person這一句必不可少,由於{}也是一個對象,當使用Person.prototype = { }時,至關於從新定義了prototype的指向,所以手動修正{}的constructor屬性,讓他成爲Person的原型。

5. jQuery中建立對象是如何實現的?

其實經過上面方式,使用構造函數聲明實例的專屬變量和方法,使用原型聲明公用的實例和方法,已是建立對象的完美解決方案了。但是惟一的不足在於,每次建立實例都要使用new來聲明。這樣未免太過麻煩,若是jquery對象也這樣建立,那麼你就會看到一段代碼中有無數個new,但是jQuery僅僅只是使用了$('xxxx')便完成了實例的建立,這是如何作到的呢?

仍是按照慣例,先貼代碼。

(function(window, undefined) {
     var Person = function(name) {
         return new Person.fn.init(name);
     }

     Person.prototype = Person.fn = {
         constructor: Person,

         init: function(name) {
             this.name = name;
             return this;
         },

         getName: function() {
             return this.name;
         }
     }
     Person.fn.init.prototype = Person.fn;
     window.Person = window.$ = Person;
 })(window);

 console.log($('tom').getName());

一步一步來分析

  • 首先爲了不變量污染,使用了函數自執行的方式。這種方式讓javascript代碼具有了模塊的特性,所以大多數js庫都會這樣作
(function(){
    ...
})()

傳入window參數,是爲了讓jquery對象在外window中能夠被訪問,所以有以下一句代碼

window.Person = window.$ = Person;

這樣咱們就能夠直接使用"$"來調用Person構造函數

  • 關鍵問題在於,真正的構造函數並非Person,而是Person原型中的init方法。其中的複雜關係,咱們藉助下圖來分析瞭解,表達能力實在有限,也不知道如何才能表達的更加簡潔易懂。

當外部調用$( ).getName( )時,函數內部的執行順序以下

new Person.fn.init()
// 而init的原型,經過下面一句指向了Person的原型
Person.fn.init.prototype = Person.fn;
// 因而就能夠調用原型中的getName方法了
相關文章
相關標籤/搜索