Javascript創建對象的幾種方式

引言

本文主要介紹Javascript創建對象的幾種方式:單體模式、工廠模式、構造函數模式和原型鏈模式

一、單體模式

對象數據類型的作用:把描述同一個事物(同一個對象)的屬性和方法放在一個內存空間下,起到了分組的作用。這樣不同事物之間的屬性即使屬性名相同,相互也不會發生衝突。我們把這種分組編寫代碼的模式叫做"單體模式”,在下面例子中,我們把person1或person2也叫做"命名空間”。

var person1={
name:"尹華芝",
age:18
};
var person2={
name:"吳曉波",
age:28
}

二、工廠模式

單體模式雖然解決了分組的作用,但是不能實現批量的生產,屬於手工作業模式。接下來我們介紹的“工廠模式”,就可以實現對象創建批量生產。

那什麼是“工廠模式”呢?簡單來說,就是把實現同一件事情的相同的代碼放到一個函數中,以後如果在想實現這個功能,不需要從新的編寫這些代碼來了,只需要執行當前的函數即可,即函數的封裝。函數封裝的好處----“低耦合高內聚”,即減少頁面中的冗餘代碼,提高代碼的重複利用率!

function createJsPerson(name,age){
var obj=new Object();
obj.name=name;
obj.age=age;
obj.writeJs=function (){
   console.log("my name is"+ this.name +'i can write JS');
};
return obj;
}
var p1=createJsPerson("王小波",48);
p1.writeJs();
var p2=createJsPerson("尹華芝",18);
p2.writeJs()

三、構造函數模式

使用工廠方法創建的對象,使用的構造函數都是Object,所創建的對象都是Object這個類型,就導致我們無法區分出多種不同類型的對象。構造函數模式的目的就是爲了創建一個自定義類,並且創建這個類的實例。構造函數模式中擁有了類和實例的概念,並且實例和實例之間是相互獨立的,即實例識別。

構造函數就是一個普通的函數,創建方式和普通函數沒有區別,不同的是構造函數習慣上首字母大寫。另外就是調用方式的不同,普通函數是直接調用,而構造函數需要使用new關鍵字來調用

構造函數的執行流程:

1.立刻創建一個新的對象

2.將新建的對象設置爲函數中this

3.逐行執行函數中的代碼

4.將新建的對象作爲返回值返回

    function Person(name, age, gender) {
        this.name = name
        this.age = age
        this.gender = gender
        this.sayName = function () {
            alert(this.name);
        }
    }
    var per = new Person("孫悟空", 18, "男");
    function Dog(name, age, gender) {
        this.name = name
        this.age = age
        this.gender = gender
    }
    var dog = new Dog("旺財", 4, "雄")
    console.log(per);//當我們直接在頁面中打印一個對象時,事件上是輸出的對象的toString()方法的返回值
    console.log(dog);

image

使用instanceof可以檢查一個對象是否是一個類的實例。所有的對象都是Object的後代,所以任何對象和Object左instanceof檢查時都會返回true。

console.log(per instanceof Person);//true
console.log(dog instanceof Person);//false
console.log(dog instanceof Object);//true

注意點

per1和per2都是Person這個類的實例,所以都擁有sayName這個方法,但是不同實例之間的方法是不一樣的。在類中給實例增加的屬性(this.xxx=xxx)屬於當前實例的私有的屬性,實例和實例之間是單獨的個體,所以私有的屬性之間是不相等的。

var per1 = new Person("孫悟空",18,"男");
var per2 = new Person("豬八戒",28,"男");
per1.sayName();
per2.sayName();
console.log(per1.sayName === per2.sayName);//false

每創建一個Person構造函數,在Person構造函數中,爲每一個對象都添加了一個sayName方法,也就是說構造函數每執行一次就會創建一個新的sayName方法。這樣就導致了構造函數執行一次就會創建一個新的方法,執行10000次就會創建10000個新的方法,而10000個方法都是一摸一樣的,這是完全沒有必要,完全可以使所有的對象共享同一個方法。

function Person(name, age , gender){
    this.name = name;
    this.age = age;
    this.gender = gender;
    this.sayName = fun;
}
function fun(){
    alert("Hello大家好,我是:"+this.name);
};

將函數定義在全局作用域這種方法,雖然會提升性能,但污染了全局作用域的命名空間,而且定義在全局作用域中也很不安全。那麼有沒有更好的辦法呢?

四、原型鏈模式

我們所創建的每一個函數,解析器都會向函數中添加一個屬性prototype,這個屬性對應着一個對象,這個對象就是我們所謂的原型對象。如果函數作爲普通函數調用prototype沒有任何作用。當函數以構造函數的形式調用時,它所創建的對象中都會有一個隱含的屬性,指向該構造函數的原型對象,我們可以通過__proto__來訪問該屬性

        function MyClass() {}
    var mc = new MyClass()
    console.log(mc.__proto__ === MyClass.prototype)//true

原型對象就相當於一個公共的區域,所有同一個類的實例都可以訪問到這個原型對象,我們可以將對象中共有的內容,統一設置到原型對象中。當我們訪問對象的一個屬性或方法時,它會先在對象自身中尋找,如果有則直接使用,如果沒有則會去原型對象中尋找,如果找到則直接使用。如果沒有則去原型的原型中尋找,直到找到Object對象的原型,Object對象的原型沒有原型,如果在Object原型中依然沒有找到,則返回undefined

    function MyClass() {}
    MyClass.prototype.a = 123;
    MyClass.prototype.sayHello = function () {
      alert("hello");
    };
    var mc = new MyClass()
    console.log(mc.a)//123
    console.log("a" in mc);//true
    //使用in檢查對象中是否含有某個屬性時,如果對象中沒有但是原型中有,也會返回true
    console.log(mc.hasOwnProperty("age"));//false
    //使用對象的hasOwnProperty()來檢查對象自身中是否含有該屬性
    console.log(mc.__proto__.hasOwnProperty("hasOwnProperty"));//false
    console.log(mc.__proto__.__proto__.hasOwnProperty("hasOwnProperty"));//true
    console.log(mc.__proto__.__proto__.__proto__);//null

以後我們創建構造函數時,可以將這些對象共有的屬性和方法,統一添加到構造函數的原型對象中,這樣不用分別爲每一個對象添加,也不會影響到全局作用域,就可以使每個對象都具有這些屬性和方法了

function Person(name, age , gender){
    this.name = name;
    this.age = age;
    this.gender = gender;
}
Person.prototype.sayName = function () {
    alert("Hello大家好,我是:" + this.name);
}   
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章