Typescript之原型物件prototype
文章目錄
- Typescript之原型物件prototype
- 前言
- 一、prototype是什么?
- 1.物件實體的\_\_proto__屬性
- 2.Object.getPrototypeOf()獲取物件實體的\_\_proto__屬性
- 3.例子
- 二、深入了解prototype
- 1.Function 物件
- 2.Function.prototype
- 3.實體
- 4.prototype 和 Object.getPrototypeOf()的區別
- 三、原型鏈的作用
- 繼承
- 1.屬性繼承
- 實體
- 2.方法繼承
- 實體
- 四、原型鏈的性能
- 總結
前言
本文主要描述在Typescript下的原型物件prototype以及獲取原型物件的方法,
提示:以下是本篇文章正文內容,下面案例可供參考
一、prototype是什么?
在JavaScript中,prototype物件是實作面向物件的一個重要機制,每個函式就是一個物件(Function),函式物件都有一個子物件 prototype物件,類是以函式的形式來定義的,prototype表示該函式的原型,也表示一個類的成員的集合,在JavaScript中,當談到繼承時,JavaScript 只有一種結構:物件,在 ES2015/ES6 中引入了 class 關鍵字,但那只是語法糖,JavaScript 仍然是基于原型的,
1.物件實體的__proto__屬性
每個實體物件( object )都有一個私有屬性(稱之為 __proto__ )指向它的建構式的原型物件(prototype),該原型物件也有一個自己的原型物件( __proto__ ) ,層層向上直到一個物件的原型物件為 null,根據定義,null 沒有原型,并作為這個原型鏈中的最后一個環節,
2.Object.getPrototypeOf()獲取物件實體的__proto__屬性
遵循ECMAScript標準,someObject. __proto__ 符號是用于指向 someObject 的原型物件prototype,從 ECMAScript 6 開始 __proto__ 可以通過 Object.getPrototypeOf() 和Object.setPrototypeOf() 訪問器來訪問,這個等同于 JavaScript 的非標準但許多瀏覽器實作的屬性 __proto__,但它不應該與建構式 Function 的 prototype 屬性相混淆,被建構式創建的實體物件的 __proto__ 指向 Function 的 prototype 屬性,Object.prototype 屬性表示 Object 的原型物件,
3.例子
如下例子說明實體dog的原型鏈:即
dog–>Animal.prototype – > Object.prototype – > null
實體物件dog的原型物件__proto__就是指向建構式Animal的prototype屬性,而prototype是一個物件,也擁有內部屬性__proto_,因此指向建構式Object的prototype屬性,所有屬性、方法和行為會層層向上直到一個物件的原型物件為 null為止,
class Animal {
name:string;
constructor(name:string="Animal") {
this.name = name;
}
sayHello(){
console.log("Hello ",this.name);
}
}
console.log(typeof(Animal.prototype)) // object
let dog = new Animal("Dog")
console.log(Object.getPrototypeOf(dog) === Animal.prototype) // true
console.log(Object.getPrototypeOf(Animal.prototype) === Object.prototype) // true
console.log(Object.getPrototypeOf(Object.prototype) === null) // true
二、深入了解prototype
1.Function 物件
每個被函式宣告(function)創建的函式是一個 Function 物件,具有 Function 物件的所有屬性、方法和行為,我們查看Function的宣告檔案,首先它被定義成一個FunctionConstructor的型別的物件型別介面,其中包含2個方法,它們都回傳一個叫做Function型別的物件型別介面,和一個只讀的prototype的Function物件型別介面的屬性,
interface FunctionConstructor {
/**
* Creates a new function.
* @param args A list of arguments the function accepts.
*/
new(...args: string[]): Function;
(...args: string[]): Function;
readonly prototype: Function;
}
declare var Function: FunctionConstructor;
2.Function.prototype
Function.prototype即FunctionConstructor.prototype,是Function物件型別介面,Function物件型別介面也是一個物件Object.幾乎所有 JavaScript 中的物件都是位于原型鏈頂端的 Object 的實體,每個物件Object都定義了一個建構式constructor,
interface Object {
/** The initial value of Object.prototype.constructor is the standard built-in Object constructor. */
constructor: Function;
...
}
/**
* Creates a new function.
*/
interface Function {
/**
* Calls the function, substituting the specified object for the this value of the function, and the specified array for the arguments of the function.
* @param thisArg The object to be used as the this object.
* @param argArray A set of arguments to be passed to the function.
*/
apply(this: Function, thisArg: any, argArray?: any): any;
/**
* Calls a method of an object, substituting another object for the current object.
* @param thisArg The object to be used as the current object.
* @param argArray A list of arguments to be passed to the method.
*/
call(this: Function, thisArg: any, ...argArray: any[]): any;
/**
* For a given function, creates a bound function that has the same body as the original function.
* The this object of the bound function is associated with the specified object, and has the specified initial parameters.
* @param thisArg An object to which the this keyword can refer inside the new function.
* @param argArray A list of arguments to be passed to the new function.
*/
bind(this: Function, thisArg: any, ...argArray: any[]): any;
/** Returns a string representation of a function. */
toString(): string;
prototype: any;
readonly length: number;
// Non-standard extensions
arguments: any;
caller: Function;
}
3.實體
如下例子說明function Animal的原型鏈:即
Animal->Function.prototype – > Object.prototype – > null
實體方法Animal的原型物件__proto__就是指向建構式Function的prototype屬性,而prototype是一個物件,也擁有內部屬性__proto_,因此指向建構式Object的prototype屬性,
class Animal {
name:string;
constructor(name:string="Animal") {
this.name = name;
}
sayHello(){
console.log("Hello ",this.name);
}
}
console.log(typeof Animal); // function
console.log(Object.getPrototypeOf(Animal) === Function.prototype) // true
console.log(Object.getPrototypeOf(Function.prototype) === Object.prototype) // true
console.log(Object.getPrototypeOf(Object.prototype) === null) // true
4.prototype 和 Object.getPrototypeOf()的區別
你可能已經注意到我們的 function Animal 有一個叫做 prototype 的特殊屬性,該特殊屬性可與 JavaScript 的 new 運算子一起使用,對原型物件的參考被復制到新實體的內部__proto__屬性,例如,當執行 var animal = new Animal(); 時,JavaScript(在記憶體中創建物件之后,和在運行函式 Animal() 把 this 指向物件之前)設定 animal.__proto__= Animal.prototype;,然后當您訪問實體的屬性時,JavaScript 首先會檢查它們是否直接存在于該物件上,如果不存在,則會__proto__ 中查找,這意味著你在 prototype 中定義的所有內容都可以由所有實體有效地共享,你甚至可以稍后更改部分 prototype,并在所有現有實體中顯示更改(如果有必要的話),
像上面的例子中,如果你執行 var a1 = new Animal(); var a2 = new Animal(); 那么 a1.sayHello() 事實上會指向 Object.getPrototypeOf(a1).sayHello(),它就是你在 Animal.prototype.sayHello 中定義的內容,也就是說:Object.getPrototypeOf(a1).sayHello == Object.getPrototypeOf(a2).sayHello == Animal.prototype.sayHello(補充:實際上,執行 a1.sayHello() 相當于執行 Object.getPrototypeOf(a1).sayHello.call(a1)==Animal.prototype.sayHello.call(a1))
簡而言之, prototype 是用于類的,而 Object.getPrototypeOf() 是用于實體的(instances),兩者功能一致,
三、原型鏈的作用
繼承
在ES5 的繼承,實質是先創造子類的實體物件this,然后再將父類的方法添加到this上面(Parent.apply(this)),ES6 的繼承機制完全不同,實質是先將父類實體物件的屬性和方法,加到this上面(所以必須先呼叫super方法),然后再用子類的建構式修改this,
class Parent {}
class Child extends Parent {
constructor() {
super();
}
}
注意,super雖然代表了父類Parent的建構式,但是回傳的是子類Child的實體,即super內部的this指的是Child的實體,因此super()在這里相當Parent.prototype.constructor.call(this),子類Child的建構式之中的super(),代表呼叫父類的建構式,這是必須的,否則 JavaScript 引擎會報錯,
原型鏈圖示:

1.屬性繼承
JavaScript 物件是動態的屬性“包”(指其自己的屬性),JavaScript 物件有一個指向一個原型物件的鏈,當試圖訪問一個物件的屬性時,它不僅僅在該物件上搜尋,還會搜尋該物件的原型,以及該物件的原型的原型,依次層層向上搜索,直到找到一個名字匹配的屬性或到達原型鏈的末尾,
實體
// 讓我們從一個函式里創建一個物件o,它自身擁有屬性a和b的:
let f = function () {
this.a = 1;
this.b = 2;
}
/* 這么寫也一樣
function f() {
this.a = 1;
this.b = 2;
}
*/
let o = new f(); // {a: 1, b: 2}
// 在f函式的原型上定義屬性
f.prototype.b = 3;
f.prototype.c = 4;
// 不要在 f 函式的原型上直接定義 f.prototype = {b:3,c:4};這樣會直接打破原型鏈
// o.[[Prototype]] 有屬性 b 和 c
// (其實就是 o.__proto__ 或者 o.constructor.prototype)
// o.[[Prototype]].[[Prototype]] 是 Object.prototype.
// 最后o.[[Prototype]].[[Prototype]].[[Prototype]]是null
// 這就是原型鏈的末尾,即 null,
// 根據定義,null 就是沒有 [[Prototype]],
// 綜上,整個原型鏈如下:
// {a:1, b:2} ---> {b:3, c:4} ---> Object.prototype---> null
console.log(o.a); // 1
// a是o的自身屬性嗎?是的,該屬性的值為 1
console.log(o.b); // 2
// b是o的自身屬性嗎?是的,該屬性的值為 2
// 原型上也有一個'b'屬性,但是它不會被訪問到,
// 這種情況被稱為"屬性遮蔽 (property shadowing)"
console.log(o.c); // 4
// c是o的自身屬性嗎?不是,那看看它的原型上有沒有
// c是o.[[Prototype]]的屬性嗎?是的,該屬性的值為 4
console.log(o.d); // undefined
// d 是 o 的自身屬性嗎?不是,那看看它的原型上有沒有
// d 是 o.[[Prototype]] 的屬性嗎?不是,那看看它的原型上有沒有
// o.[[Prototype]].[[Prototype]] 為 null,停止搜索
// 找不到 d 屬性,回傳 undefined
2.方法繼承
JavaScript 并沒有其他基于類的語言所定義的“方法”,在 JavaScript 里,任何函式都可以添加到物件上作為物件的屬性,函式的繼承與其他的屬性繼承沒有差別,包括上面的“屬性遮蔽”(這種情況相當于其他語言的方法重寫)
實體
var o = {
a: 2,
m: function(){
return this.a + 1;
}
};
console.log(o.m()); // 3
// 當呼叫 o.m 時,'this' 指向了 o.
var p = Object.create(o);
// p是一個繼承自 o 的物件
p.a = 4; // 創建 p 的自身屬性 'a'
console.log(p.m()); // 5
// 呼叫 p.m 時,'this' 指向了 p
// 又因為 p 繼承了 o 的 m 函式
// 所以,此時的 'this.a' 即 p.a,就是 p 的自身屬性 'a'
四、原型鏈的性能
在原型鏈上查找屬性比較耗時,對性能有副作用,這在性能要求苛刻的情況下很重要,另外,試圖訪問不存在的屬性時會遍歷整個原型鏈,
遍歷物件的屬性時,原型鏈上的每個可列舉屬性都會被列舉出來,要檢查物件是否具有自己定義的屬性,而不是其原型鏈上的某個屬性,則必須使用所有物件從 Object.prototype 繼承的 hasOwnProperty 方法,
下面給出一個具體的例子來說明它:
class A {
name:string
constructor(name:sting="defalut"){
this.name = name
}
}
let a = new A()
console.log(a.hasOwnProperty('name'));
// true
console.log(a.hasOwnProperty('abc'));
// false
console.log(a.hasOwnProperty('efg'));
// false
注意:檢查屬性是否為 undefined 是不能夠檢查其是否存在的,該屬性可能已存在,但其值恰好被設定成了 undefined,hasOwnProperty 和Object.keys()是 JavaScript 中唯處理屬性并且不會遍歷原型鏈的方法,
總結
文章介紹了建構式原型物件prototype,以及被它創建的實體物件(instance)的__proto__屬性,獲取實體__proto__屬性的方法Object.getPrototypeOf(),解釋了兩者的區別以及使用,當實體查找某個屬性和方法時候,就根據__proto__屬性鏈條層層向上查詢,直到null,但是遍歷整個原型鏈會產生性能問題,使用hasOwnProperty()和Object.keys()判斷屬性是否在改物件實體中,這樣就避免了遍歷整個原型鏈,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/253100.html
標籤:python
下一篇:爬取糗事百科段子 + 資料可視化
