對象的創建方式-簡單整理

1、工廠模式
用函數封裝,以特定的接口創建對象。
如下所示:顯示的創建一個Object對象,然後返回該對象即可;
1-1、具體實現
function createPerson (name) {
    var obj = new Object();// 實例話一個Object對象
    obj.name = name;
    obj.getName = () => {
        console.log(this.name);
    };
    return obj;
}
var p1 = createPerson('zhangsan');
var p2 = createPerson('lisi');
1-2、優缺點
優點:可以無數次的調用這個函數,創建多個不同的對象;
缺點:無法確定一個對象的具體類型,即屬於那個函數;
2、構造函數模式
2-1、具體實現
function Person (name) {
    this.name = name;
    this.getName = () => {
        console.log(this.name);
    }
}
var p1 = new Person ('zhangsan');
var p2 = new Person ('lisi');
2-2、與工廠模式的不同
1>、沒有顯示的創建Object對象;
2>、將屬性賦給來this對象;
3>、沒有return語句;
4>、函數的首字母大寫
5>、創建對象時,使用來【new】關鍵字
2-3、new運算符

2-3-1、作用

創建一個用戶定義的對象類型的實例,或者具有構造函數的內置對象的實例。

2-3-2、執行new Person(…)時,發生的事情

1>、一個繼承自Person.prototype的新對象被創建;
2>、使用指定的參數調用構造函數Person,並將this對象綁定到新創建的實例對象上。
3>、由構造函數返回的對象就是new表達式的結果。如果構造函數沒有顯示的返回一個對象,則使用步驟1創建的對象。

2-3-3、使用new操作符創建對象實例的步驟

1>、創建一個新對象;
2>、將構造函數的作用域賦給新對象;
3>、執行構造函數中的代碼;
4>、返回新對象

2-3-4、其他

1>、每創建一個實例對象,該對象就會有一個constructor屬性,指向Person;
2>、所有的對象都繼承自Object對象,所以實例對象也是Object對象的實例;

2-3-5、問題

使用這種方式實例話對象時,每個方法都要在實例上重新創建一遍,會創建多個重複的方法,浪費內存。
#解決方法
定義全局的函數,將實例對象的屬性指向這個函數的引用,避免了創建多個相同的function。
function Person (name) {
    this.name = name;
    this.getName = getName;
}
function getName () {
    console.log(this.name);
}
3、原型模式
3-1、具體實現
function Person () {}

Person.prototype.name = 'zhangsan';
Person.prototype.getName = function () {
    console.log(this.name);
}
// p1與p2的對象的name屬性的值是一樣的
var p1 = new Person ();
var p2 = new Person ();
3-2、原型對象prototype
1>、每一個函數都有一個prototype屬性,這個屬性是一個指針,指向原型對象。
2>、用途:包含可以由特定類型的所有實例共享的屬性和方法。
3>、原型對象有一個constructor屬性,即:Person.prototype.constructor,constructor指向函數體,即Person函數。
3-3、與構造函數的不同
該對象的所有屬性和方法是所有實例共享的,所以上述的p1和p2共享同一個name屬性
3-4、實例對象、構造函數和原型之間的關係
1>、function Person(){}是一個函數對象,它本身有一個prototype屬性,該屬性是一個指針,指向Person對象的原型對象;
2>、Person.prototype對象有一個constructor屬性,也是一個指針,指向Person函數體;
3>、var p = new Person();創建了一個對象實例,該實例有一個__proto__屬性,該屬性指向Person的原型對象【Person.prototype】;
4>、可以得出:實例對象與構造函數之間沒有直接的關係,他們是通過Person的原型對象【Person.prototype】建立的聯繫;
4>、每一個構造函數都是new Function()的實例,所以Person.__proto__指向Function的prototype;
5>、原型對象最初只包含constructor屬性,而該屬性也是共享的,所以可以通過對象實例訪問,即p.constructor;
6>、實例中的指針僅指向原型,不指向構造函數。
3-5、原型中常用的方法
1>、isPrototypeOf():判斷實例與構造函數是否存在某種關聯;
    用法:Person.prototype.isPrototypeOf(p);
2>、getPrototypeOf():獲取指定實例對象的原型對象;
    用法:Object.getPrototypeOf(p);
3>、hasOwnProperty():檢測一個屬性是存在於實例中,還是存在於原型中;【該方法繼承自Object】
4>、in操作符:只要屬性能夠被訪問到,就返回true,無論是在實例對象上被訪問,還是在原型對象上被訪問;
5>、Object.getOwnPropertyNames():獲取所有實例屬性,包括不可枚舉的constructor屬性;
6>、Object.keys():獲取對象上的所有可枚舉屬性;返回一個包含所有可枚舉屬性的字符串數組
3-6、原型中屬性的訪問
可以通過對象實例訪問保存在原型中的值,但卻不能通過對象實例重寫原型中的值;
當爲對象實例添加一個屬性時,這個屬性就會屏蔽原型對象中保存的同名屬性;即:添加這個屬性只會阻止我們訪問原型對象中的屬性,但是不會修改那個屬性;
即使將實例對象上的屬性設置爲null,也不會修改原型對象上的屬性;除非使用delete操作符刪除這個屬性。
3-7、問題
在原型對象上添加屬性和方法時,只能一個一個的添加;
#解決方法:使用字面量語法重寫原型對象
重寫原型對象,會導致構造函數的constructor的指向發生變化,需要重新指明constructor的指向。
重設constructor屬性會導致它的enumerable被設置爲true,而原生的constructor屬性是不可枚舉的。
function Person () {}
Person.prototype = {
    constructor: Person,
    name: 'zhangsan',
    getName: function () {
        console.log(this.name);
    }
}
var p = new Person ();
3-8、重寫原型對象
重寫原型對象,對象實例化必須在重寫原型對象之後;
重寫原型對象斷開了實例與原型對象之間的關係;
#錯誤的實現
function Person () {}
var p = new Person ();
Person.prototype = {
    constructor: Person,
    name: 'zhangsan',
    getName: function () {
        console.log(this.name);
    }
}
p.getName();// 調用函數的時候會報錯
4、構造函數模式與原型模式的組合
代碼實現:
function Person (name, age) {
    this.name = name;
    this.age = age;
}
Person.prototype = {
    constructor: Person,
    getName: function () {
        console.log(this.name);
    }
}
var p = new Person ('zhangsan', 24);// 屬性不共享,方法共享
5、動態原型模式
代碼實現
判斷語句只有在初始化構造函數時纔會執行;
在動態原型模式中,不能使用對象字面量的方式重寫原型;
只需使用if語句檢查其中的一個屬性或方法即可。
function Person (name, age) {
    this.name = name;
    this.age = age;
    
    if(typeof this.sayName != 'function') {
        Person.prototype.sayName = function () {
            console.log(this.name);
        }
    }
}
var p = new Person ('zhangsan', 24);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章