中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

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

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

深入了解js原型模式

發布時間:2020-10-19 04:33:13 來源:腳本之家 閱讀:161 作者:伯爵213 欄目:web開發

一、什么是原型模式

在js中,創建對象的方式有工廠模式和構造函數模式等; 而構造函數模式最大的問題在于:構造函數中的每個方法都需要在實例對象中重新創建一遍,不能復用,所以為了解決這一個問題,就需要使用原型模式來創建對象。
原型模式是把所有實例共享的方法和屬性放在一個叫做prototype(原型)的屬性中 ,在創建一個函數時都會有個prototype屬性, 這個屬性是一個指針,指向一個對象,是通過調用構造函數而創建的那個對象實例的原型對象。

// 構造函數
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.理解原型對象

無論什么時候,只要創建了一個新函數,就會根據一組特定的規則為該函數創建一個prototype屬性,這個屬性指向函數的原型對象,在默認的情況下,所有的原型對象都自動獲得一個constructor(構造函數)屬性,這是一個指針,指向prototype屬性所在的函數。創建了自定義的構造函數之后,其原型對象默認只會取得constructor屬性;其他的方法則是從Object繼承來的。

當調用構造函數創建一個新實例對象后,該實例的內部將包含一個指針[[Prototype]],指向構造函數的原型對象。這個連接存在于實例和構造函數的原型對象之間,而不是存在實例和構造函數之間。

每當代碼讀取某個對象的某個屬性時,都會執行一次搜索,目標是具有給定名字的屬性。搜索首先從對象實例本身開始。如果在實例中找到了就返回該屬性的值,沒有找到,則繼續搜索指針指向的原型對象,在原型對象中查找具有給定名字的屬性,如果在原型對象中找到了這個屬性,就返回該屬性的值。

雖然可以通過實例訪問保存在原型中的值,但不能通過實例對象重寫原型中的值,如果在實例中添加一個在原型中的同名屬性,該屬性會自動屏蔽原型中的屬性,但是不會修改原型中的屬性,只會阻止訪問原型中的屬性,通過delete操作符則可以完全刪除實例屬性,使得可以重新訪問原型中的屬性。

2.原型與in操作符

hasOwnProperty()方法可以檢測一個屬性是否存在于實例對象中,

// 構造函數
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操作符的使用可以分為兩類,單獨使用和在for-in循環使用,在單獨使用時,in操作符會在通過對象能夠訪問給定屬性時返回true,無論該屬性存在于實例中還是原型中。

// 構造函數
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

同時使用hasOwnProperty()方法和in操作符,可以確定該屬性時在原型上還是在存在于對象中。

// 構造函數
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循環時,返回的是所有能夠通過對象訪問的、可枚舉的屬性,其中即包含存在于實例中的屬性,也包含與存在原型中的屬性。

let o = {
name: 'san',
age: 14,
};
for(let key in o) {
console.log(key);
}

要取得對象上所有可枚舉的實例屬性,可以使用Object.keys()方法,接收一個對象作為參數,返回一個包含所有可枚舉屬性的字符串數組。

如果想得到所有實例屬性。無論是否可枚舉,都可以使用Object.getOwnPropertyNames()方法。

3.更簡單的原型語法

為了減少不必要的輸入和從視覺上更好的封裝原型的功能,常見的做法是用一個包含所有屬性和方法的對象字面量來重寫整個原型對象。

// 構造函數
function Person() {};
Person.prototype = {
sayHi: function() {
console.log(hi);
},
name: '張三',
};

通過這個方式會導致原型對象中的constructor屬性不在指向Person了。如果constructor的值真的很重要,可以像下面這樣特意將它設置回適當的值。

// 構造函數
function Person() {};
Person.prototype = {
constructor: Person,
sayHi: function() {
console.log(hi);
},
name: '張三',
};

但是通過這種方式會導致對象的[[Enumerable]]特性被設置為ture,默認情況下,constructor屬性時不可枚舉的,可以通過Object.defineProperty()解決這個問題。

// 構造函數
function Person() {};
Person.prototype = {
sayHi: function() {
console.log(hi);
},
name: '張三',
};
Object.defineProperty(Person.prototype, "constructor", {
enumerable: false,
value: Person
}

4.原型的動態性

當對原型對象所做的任何修改都能夠立即從實例上反應出來。

function Person() {};
var friend = new Person();
Person.prototype.sayHi = function() {
console.log('hi');
};
friend.sayHi(); // hi

但是如果是重寫整個原型對象,那么情況就不一樣了。調用構造函數時會為實例添加一個指向最初原型的[[prototype]]指針,而把原型修改為另外一個對象 就相當于切斷了構造函數與最初原型之間的聯系。 實例中的指針僅指向原型,而不是指向構造函數。

// 構造函數
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

創建了一個Person的實例,然后又重寫了其原型對象。但是在使用sayHi()時發生了錯誤,這個時候實例所指向的原型對象是一個新的對象。重寫原型對象切斷了現有原型與之前已經存在的對象實例直接的聯系。所以報錯了。

5.原生對象的原型

原型模式的重要性不僅體現在創建自定義類型方面,就連所有原生的引用類型,都采用這種模式,所有的原生引用類型(Object、Array、String)等,都在其構造函數的原型上定義了方法。可以像修改自定義對象的原型一樣修改原生對象的原型。

二、原型模式的缺點

對于包含引用類型值的屬性來說,所有實例在默認的情況下都會取得相同的屬性值。

// 構造函數
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的原型對象中,所以person1對friends的修改也會通過person2反應出來,但是實例對象一般都是要有屬于自己的全部屬性,正因為如此,很少有人單獨使用原型模式來創建對象。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

临安市| 延吉市| 苏尼特右旗| 平山县| 开化县| 平和县| 伊春市| 赞皇县| 曲靖市| 玛多县| 内乡县| 滨州市| 石柱| 合作市| 西畴县| 潮州市| 黄石市| 根河市| 新密市| 遵化市| 荣成市| 南和县| 扶沟县| 吴堡县| 鄂伦春自治旗| 乌审旗| 托克托县| 泗阳县| 彰武县| 双鸭山市| 福鼎市| 光山县| 海安县| 昌平区| 房山区| 沙雅县| 县级市| 鹤壁市| 乌审旗| 子长县| 昌邑市|