簡單有趣的原型語法

2021-08-14 06:23:19 字數 3602 閱讀 1452

前些天偶然看到了乙個有趣的原型語法,這種方法稍微簡化了咱們給原型物件新增方法和屬性的書寫過程,而且非常清新,給人一種一目了然的感覺,在這裡欣喜地和大家分享一下

先來看看我們傳統的新增原型物件的屬性、方法的方式:

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, 所以constructorundefined是理所當然

既然我們對原型物件重寫了,那它缺少什麼咱們補上來就可以了唄:

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...