溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

js中創(chuàng)建對(duì)象方式----原型模式

發(fā)布時(shí)間:2020-08-01 20:30:37 來(lái)源:網(wǎng)絡(luò) 閱讀:2708 作者:伯爵213 欄目:web開(kāi)發(fā)

一、什么是原型模式

在js中,創(chuàng)建對(duì)象的方式有工廠模式和構(gòu)造函數(shù)模式等; 而構(gòu)造函數(shù)模式最大的問(wèn)題在于:構(gòu)造函數(shù)中的每個(gè)方法都需要在實(shí)例對(duì)象中重新創(chuàng)建一遍,不能復(fù)用,所以為了解決這一個(gè)問(wèn)題,就需要使用原型模式來(lái)創(chuàng)建對(duì)象。
原型模式是把所有實(shí)例共享的方法和屬性放在一個(gè)叫做prototype(原型)的屬性中 ,在創(chuàng)建一個(gè)函數(shù)時(shí)都會(huì)有個(gè)prototype屬性, 這個(gè)屬性是一個(gè)指針,指向一個(gè)對(duì)象,是通過(guò)調(diào)用構(gòu)造函數(shù)而創(chuàng)建的那個(gè)對(duì)象實(shí)例的原型對(duì)象。

 // 構(gòu)造函數(shù)
    function Person() {};

    // 原型屬性prototype
    Person.prototype.name = '張三';
    Person.prototype.sayName = function() {
        console.log(this.name);
    };

    let person1 = new Person();
    person1.sayName(); //張三
    let person2 = new Person();
    person2.sayName(); // 張三

    console.log(person1.sayName == person2.sayName); //true
  1. 理解原型對(duì)象

    無(wú)論什么時(shí)候,只要?jiǎng)?chuàng)建了一個(gè)新函數(shù),就會(huì)根據(jù)一組特定的規(guī)則為該函數(shù)創(chuàng)建一個(gè)prototype屬性,這個(gè)屬性指向函數(shù)的原型對(duì)象,在默認(rèn)的情況下,所有的原型對(duì)象都自動(dòng)獲得一個(gè)constructor(構(gòu)造函數(shù))屬性,這是一個(gè)指針,指向prototype屬性所在的函數(shù)。創(chuàng)建了自定義的構(gòu)造函數(shù)之后,其原型對(duì)象默認(rèn)只會(huì)取得constructor屬性;其他的方法則是從Object繼承來(lái)的。
    當(dāng)調(diào)用構(gòu)造函數(shù)創(chuàng)建一個(gè)新實(shí)例對(duì)象后,該實(shí)例的內(nèi)部將包含一個(gè)指針[[Prototype]],指向構(gòu)造函數(shù)的原型對(duì)象。這個(gè)連接存在于實(shí)例和構(gòu)造函數(shù)的原型對(duì)象之間,而不是存在實(shí)例和構(gòu)造函數(shù)之間。
    每當(dāng)代碼讀取某個(gè)對(duì)象的某個(gè)屬性時(shí),都會(huì)執(zhí)行一次搜索,目標(biāo)是具有給定名字的屬性。搜索首先從對(duì)象實(shí)例本身開(kāi)始。如果在實(shí)例中找到了就返回該屬性的值,沒(méi)有找到,則繼續(xù)搜索指針指向的原型對(duì)象,在原型對(duì)象中查找具有給定名字的屬性,如果在原型對(duì)象中找到了這個(gè)屬性,就返回該屬性的值。
    雖然可以通過(guò)實(shí)例訪(fǎng)問(wèn)保存在原型中的值,但不能通過(guò)實(shí)例對(duì)象重寫(xiě)原型中的值,如果在實(shí)例中添加一個(gè)在原型中的同名屬性,該屬性會(huì)自動(dòng)屏蔽原型中的屬性,但是不會(huì)修改原型中的屬性,只會(huì)阻止訪(fǎng)問(wèn)原型中的屬性,通過(guò)delete操作符則可以完全刪除實(shí)例屬性,使得可以重新訪(fǎng)問(wèn)原型中的屬性。

  2. 原型與in操作符

    hasOwnProperty()方法可以檢測(cè)一個(gè)屬性是否存在于實(shí)例對(duì)象中,
    // 構(gòu)造函數(shù)
    function Person() {
    this.age = 16;
    };
    Person.prototype.name = "張三";
    let person1 = new Person();
    console.log(person1.hasOwnProperty('name')); // false
    console.log(person1.hasOwnProperty('age')); // true

    in操作符的使用可以分為兩類(lèi),單獨(dú)使用和在for-in循環(huán)使用,在單獨(dú)使用時(shí),in操作符會(huì)在通過(guò)對(duì)象能夠訪(fǎng)問(wèn)給定屬性時(shí)返回true,無(wú)論該屬性存在于實(shí)例中還是原型中。
    // 構(gòu)造函數(shù)
    function Person() {}
    Person.prototype.name = 'zhang';
    let person1 = new Person();
    console.log('name' in person1); // true
    person1.age = 14;
    console.log('age' in person1); // true
    同時(shí)使用hasOwnProperty()方法和in操作符,可以確定該屬性時(shí)在原型上還是在存在于對(duì)象中。
    // 構(gòu)造函數(shù)
    function Person() {}
    function hasPrototypeProperty(object, name) {
    return !object.hasOwnProperty(name) && (name in object);
    }
    Person.prototype.name = "張三";
    let person = new Person();
    console.log(hasPrototypeProperty(person, 'name')); // true
    console.log(hasPrototypeProperty(person, 'age')); // false
    使用for-in循環(huán)時(shí),返回的是所有能夠通過(guò)對(duì)象訪(fǎng)問(wèn)的、可枚舉的屬性,其中即包含存在于實(shí)例中的屬性,也包含與存在原型中的屬性。
    let o = {
    name: 'san',
    age: 14,
    };
    for(let key in o) {
    console.log(key);
    }
    要取得對(duì)象上所有可枚舉的實(shí)例屬性,可以使用Object.keys()方法,接收一個(gè)對(duì)象作為參數(shù),返回一個(gè)包含所有可枚舉屬性的字符串?dāng)?shù)組。
    如果想得到所有實(shí)例屬性。無(wú)論是否可枚舉,都可以使用Object.getOwnPropertyNames()方法。

  3. 更簡(jiǎn)單的原型語(yǔ)法

    為了減少不必要的輸入和從視覺(jué)上更好的封裝原型的功能,常見(jiàn)的做法是用一個(gè)包含所有屬性和方法的對(duì)象字面量來(lái)重寫(xiě)整個(gè)原型對(duì)象。
    // 構(gòu)造函數(shù)
    function Person() {};
    Person.prototype = {
    sayHi: function() {
    console.log(hi);
    },
    name: '張三',
    };
    通過(guò)這個(gè)方式會(huì)導(dǎo)致原型對(duì)象中的constructor屬性不在指向Person了。如果constructor的值真的很重要,可以像下面這樣特意將它設(shè)置回適當(dāng)?shù)闹怠?br/>// 構(gòu)造函數(shù)
    function Person() {};
    Person.prototype = {
    constructor: Person,
    sayHi: function() {
    console.log(hi);
    },
    name: '張三',
    };
    但是通過(guò)這種方式會(huì)導(dǎo)致對(duì)象的[[Enumerable]]特性被設(shè)置為ture,默認(rèn)情況下,constructor屬性時(shí)不可枚舉的,可以通過(guò)Object.defineProperty()解決這個(gè)問(wèn)題。
    // 構(gòu)造函數(shù)
    function Person() {};
    Person.prototype = {
    sayHi: function() {
    console.log(hi);
    },
    name: '張三',
    };
    Object.defineProperty(Person.prototype, "constructor", {
    enumerable: false,
    value: Person
    }

  4. 原型的動(dòng)態(tài)性

    當(dāng)對(duì)原型對(duì)象所做的任何修改都能夠立即從實(shí)例上反應(yīng)出來(lái)。
    function Person() {};
    var friend = new Person();
    Person.prototype.sayHi = function() {
    console.log('hi');
    };
    friend.sayHi(); // hi

    但是如果是重寫(xiě)整個(gè)原型對(duì)象,那么情況就不一樣了。調(diào)用構(gòu)造函數(shù)時(shí)會(huì)為實(shí)例添加一個(gè)指向最初原型的[[prototype]]指針,而把原型修改為另外一個(gè)對(duì)象 就相當(dāng)于切斷了構(gòu)造函數(shù)與最初原型之間的聯(lián)系。 實(shí)例中的指針僅指向原型,而不是指向構(gòu)造函數(shù)。
    // 構(gòu)造函數(shù)
    function Person() {};
    var friend = new Person();
    Person.prototype = {
    constructor: Person,
    sayHi: function() {
    console.log(hi);
    }
    };
    friend.sayHi(); // Uncaught TypeError: friend.sayHi is not a function

    創(chuàng)建了一個(gè)Person的實(shí)例,然后又重寫(xiě)了其原型對(duì)象。但是在使用sayHi()時(shí)發(fā)生了錯(cuò)誤,這個(gè)時(shí)候?qū)嵗赶虻脑蛯?duì)象是一個(gè)新的對(duì)象。重寫(xiě)原型對(duì)象切斷了現(xiàn)有原型與之前已經(jīng)存在的對(duì)象實(shí)例直接的聯(lián)系。所以報(bào)錯(cuò)了。

  5. 原生對(duì)象的原型

    原型模式的重要性不僅體現(xiàn)在創(chuàng)建自定義類(lèi)型方面,就連所有原生的引用類(lèi)型,都采用這種模式,所有的原生引用類(lèi)型(Object、Array、String)等,都在其構(gòu)造函數(shù)的原型上定義了方法。可以像修改自定義對(duì)象的原型一樣修改原生對(duì)象的原型。

二、原型模式的缺點(diǎn)

對(duì)于包含引用類(lèi)型值的屬性來(lái)說(shuō),所有實(shí)例在默認(rèn)的情況下都會(huì)取得相同的屬性值。
// 構(gòu)造函數(shù)
function Person() {};
// 原型屬性prototype
Person.prototype = {
constructor: Person,
friends: ['張三', '李四'],
}
let person1 = new Person();
let person2 = new Person();
person1.friends.push('王五');
console.log(person1.friends); // ["張三", "李四", "王五"]
console.log(person2.friends); // ["張三", "李四", "王五"]

由于friends存在于Person的原型對(duì)象中,所以person1對(duì)friends的修改也會(huì)通過(guò)person2反應(yīng)出來(lái),但是實(shí)例對(duì)象一般都是要有屬于自己的全部屬性,正因?yàn)槿绱?,很少有人單?dú)使用原型模式來(lái)創(chuàng)建對(duì)象。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI