1. 程式人生 > >javaScript 建立物件的方式的四種模式探討,this指向問題,以及Jquery中物件的建立

javaScript 建立物件的方式的四種模式探討,this指向問題,以及Jquery中物件的建立

在javaScript中,建立物件一共有四種方式,廢話少說:看程式碼

1  Json格式,字面量方式建立:

    <script>
            var persion ={
                name:"xiaoheng",
                age:20,
                getName:function(){
                    return this.name
                }
            }
            console.log(persion.getName())//可以取到資料   這種稱為JSON型別的資料格式,此時的this指的是window  
            var  aa = new persion();

            console.log(aa.name)     


  //這樣就不可以  因為constructor構造器的並未對次物件引用,這個方法是靜態的。不能產生引用。

        </script>

 

2   使用new建立物件
var dog = new Object();
dog.name = 'tim';
dog.getName = function() {
    return dog.name;
}
  • 可以使用delete刪除物件的屬性和方法
<script>
            var persion = new Object();
            persion.name="xiaoheng";
            persion.age=20;
            persion.number=30;
            //delete persion.name;
            function  displayProp(obj){    //遍歷函式所有的屬性
                var names=""
                for (var name in obj) {
                    names+=name+":"+obj[name]+","
                }
                alert(names)
            };
            displayProp(persion);

        </script>


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

3   工廠模式建立物件。
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個很大的問題

  • 無法識別物件的型別
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');

注意 person1和person2能prototype擴充,他有構造器的作用

用著種方式建立並用prototype,可以實現偽繼承,(相當於對本物件做個擴充);

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

從上面程式碼可以看出,物件的類別可以判斷了,person1就是Person的物件,可是2個同名方法任然不是同一個方法,而是重新建立,其實建構函式內部的實現,可以將上面的程式碼寫成這樣來理解

原型

原型並沒有那麼神祕,因為在javascript中,它無處不在。為了瞭解原型,我們可以在chrome瀏覽器的console中,隨意建立一個函式

function a(){}

然後繼續輸入

a.prototype

得到的結果如下

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

沒錯,得到的這個a,就是關鍵先生原型了。每一個函式都有一個prototype屬性,他就像一個指標一樣指向它的原型,而每一個原型,都有一個constructor屬性,指向他的建構函式。
那麼原型在建立物件中有什麼用呢?
一個例子,千錘百煉,如下

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

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

當然也可以將屬性寫入原型中,但是如果那樣的話,屬性就會如同方法一樣被公用了,因此一般來說,屬性會寫入建構函式之中,方法寫入原型之中。當然,這視情況而定。

如果你想進一步瞭解原型,可以看下圖。

當我們使用new Person時便會建立一個例項,比如這裡的person1與person2,這裡的例項中,會有一個_proto_屬性指向原型。


  • 原型中的查詢機制

當我們使用例項person1呼叫方法person.getName()時,我們首先找的,是看看建構函式裡面有沒有這個方法,如果建構函式中存在,就直接呼叫建構函式的方法,如果建構函式不存在,才回去查詢原型中是否存在該方法

當原型和建構函式中擁有同樣的方法和屬性的時候,建構函式中的被執行。
於是,這裡便會有一個十分重要的概念需要理解,那就是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方法了