前些天偶然看到了乙個有趣的原型語法,這種方法稍微簡化了咱們給原型物件新增方法和屬性的書寫過程,而且非常清新,給人一種一目了然的感覺,在這裡欣喜地和大家分享一下
先來看看我們傳統的新增原型物件的屬性、方法的方式:
function
person
() person.prototype.name = 'han';
person.prototype.age = 21;
person.prototype.job = 'student';
person.prototype.showname = function
() ;
這**本身沒什麼問題,但是一連串的prototype
不免讓人感覺亢長擁擠,那有什麼辦法可以讓我們的**更簡潔易讀點呢?
其實很簡單,我們用乙個包含所有屬性和方法的物件字面量來書寫這個原型物件,可以有更好的辨別度:
function
person
() person.prototype =
};
是不是感覺清新多了,**量減少的同時,條理也更加清晰,但這種方式有什麼問題嗎?
答案是有的,這種方式的確有問題,最明顯的,是這種方式重寫了原型物件,使得在此之前通過person
建構函式建立的例項物件,和原型物件之間的聯絡斷開了。產生的後果是:之前建立的例項,無法共享到此時原型物件上的屬性和方法如:
function
person
() // 例項化
let person = new person();
// 都顯示為 undefined
console.log(person.name, person.age, person.job)
// 報錯
person.showname();
person.prototype =
};
例項物件的__proto__
指向建構函式的prototype
,即原型物件,這是他例項化出來的同時自動產生的,他其實就是乙個指標,指向乙個位址。
後續給原型物件重新賦值,相當於改變了原型物件的位址指向。但是例項化出來的物件的__proto__
的指向並不會更改,所以導致在重寫原型物件之前建立的例項化物件,因為位址指向的不同,無法獲取到重寫後的屬性和方法
簡單的表示以下這個過程:
// 例項化物件
let person = new person();
person.__proto__ === person.prototype; // true
// 重寫這個原型物件,位址發生改變
person.prototype = {};
person.__proto__ === person.prototype; // false
所以,這個簡寫的原型語法,如果想確保每個例項化物件都可以共享到它裡面的屬性和方法的話,就需要把例項化的過程寫在重寫原型物件之後
那除了這個問題之外,還有什麼其他的問題嗎?
的確還有,而且還挺嚴肅的,咱們看一下:
console.log(person.constructor === person); // false
console.log(person.constructor); // undefined
等等,我的建構函式不是person
嗎?怎麼沒有了?
這的確是乙個讓人意外的事情,但是仔細一想,一切好像是理所當然。constructor
存放在哪?熟悉原型鏈的同學都知道,當然是在原型物件中。我們的這種寫法是對原型物件的重寫,而重寫的屬性中並沒有constructor
, 所以constructor
為undefined
是理所當然
既然我們對原型物件重寫了,那它缺少什麼咱們補上來就可以了唄:
person.prototype =;
這下應該沒問題了吧:
console.log(person.constructor); // person
不錯,問題好像解決了。。。
等等,好像??這麼說還有?看看以下**:
function
person
() person.prototype = ;
let person = new person();
for (let key in person)
// 返回結果如下:
// constructor: f person() {}
// name: han
// age: 21
沒錯,問題就是使用for in
迴圈遍歷所有可訪問、可列舉的屬性時,constructor
也被遍歷出來了。可是正常情況下是不會被遍歷出來的
這是因為,我們這樣直接在原型物件上定義的屬性,他們的資料屬性中的[[enumerable]]
屬性的值會預設為true
。它用來控制對應屬性是否可以通過for-in
遍歷迴圈返回出來
這裡可能很多人都懵了,這個是個什麼東西?
在 ecmascript 中,定義了一些內部才用的特性,描述了屬性的各種特徵。它分為兩種:資料屬性和訪問器屬性。咱們上面說的屬於資料屬性,它有4個描述其行為的特性:
以上是對資料屬性的簡單概括,感興趣的同學可以專門去了解一下,這裡僅用於小小的說明
所以,它[[enumerable]]
設定為false
就可以使其無法被遍歷到了 ,從而解決問題。而設定這個特殊的屬性,需要用到特殊的方法:object.defineproperty()
,這個方法接收三個引數:
最後咱們可以寫成:
function
person
() person.prototype = ;
// 設定 constructor: person 的同時,遮蔽掉他的可列舉屬性
object.defineproperty(person.prototype, 'constructor', );
這樣,通過for-in
遍歷,也無法返回出constructor
屬性了,這樣全部問題得以解決。
可以看的出來,新的原型寫法,雖然簡單明瞭,但是問題多多,需要我們手動的解決其本身存在的 bug。但好在修復問題的過程也並不複雜,需要注意的問題也只是:把例項化物件的過程,放在重寫原型物件之後。
所以,當需要在原型物件中新增較多的屬性和方法時,我們需要更加清晰的看到各個結構,這種方法在此時是一種不錯的選擇。
今天是2023年的第一天,祝大家在新的一年裡順順利利!!
更簡單的原型語法
function person person.prototype.name nicholas person.prototype.age 29 person.prototype.job software engineer person.prototype.sayname function 前面例子中每...
簡單原型語法和原型動態性
function student student.prototype 簡單原型寫法本質上完全重寫了預設的prototype物件,因此construtor屬性也就變成了新物件的constructor屬性,指向了object建構函式,不再指向student函式。通過constructor已經無法確定物件...
js原型 原型鏈以及原型繼承簡單闡述
原型是物件資料型別所有,原型又分為顯式原型和隱式原型 語法糖形式 class person getname getage const person newperson 張三 18 person.getname 張三 person.getage 18 建構函式形式 function person na...