
在JavaScript中,函數是一個包含屬性和方法的Function類型的物件。而原型(Prototype)就是Function類型物件的一個屬性。
在函數定義時就包含了prototype屬性,它的初始值是一個空物件。在JavaScript中並沒有定義函數的原型類型,所以原型可以是任何類型。
原型是用來保存物件的共享屬性和方法的,原型的屬性和方法並不會影響函數本身的屬性和方法。
// Function類型的屬性->所有函數都具有的屬性console.log(Function.prototype);//[Function]
//定義函數function fn() {
console.log('this is function');
}
//原型的預設值是空物件console.log(fn.prototype);//fn {}
// 函式包含建構函式-> 所有參考型別其實都是建構函式console.log(Number.prototype); //[Number: 0]
console.log(Object.prototype);//{} 透過以下兩種方式可以取得物件的原型,從而設定共享的屬性與方法:
prototype屬性getPrototype (obj)方法。function fn() {
console.log('this is function');
}
//使用存取物件的屬性語法結構console.log(fn.prototype);//fn {}
console.log(fn['prototype']);//fn {}
//Object型別提供getPrototypeOf()方法console.log(Object.getPrototypeOf(fn));//[Function]Object.getOwnPropertyDescriptors()方法用來取得一個物件的所有自身屬性的描述符。
var result = Object.getOwnPropertyDescriptor(Object.prototype,'constructor');
console.log(result) //輸出結果如下:
//{
// value: [Function: Object],
// writable: true,
// enumerable: false,
// configurable: true
// }
constructor是在创建函数的时候自动添加的,指向构造函数本身
透過以下兩種方式可以設定原型的屬性和方法:
建構子.prototype.屬性名稱= 屬性值;建構子.prototype.方法名稱= function(){} ;當我們需要在原型上添加很多很多屬性的時候一遍一遍的去寫
构造函数.prototype.属性名太麻煩了,可以直接修改整個prototype
構造函數.prototype = {
屬性名:屬性值,
方法名稱:function(){}} function foo () {}foo.prototype = {
constructor: foo,
name: 'jam',
age: 18,
address: '北京市'}var fn = new foo()console.log(fn.address) // 北京市每個物件中都會具有一個isPrototypeOf()方法,該方法用來判斷一個物件是否是另一個物件的原型。
範例程式碼如下:
// 以初始化器方式定義物件var obj = {
name:'jam'
}
// 定義建構子function Hero() {}
// 將物件obj賦值給建構子Hero的原型Hero.prototype = obj;
// 透過建構函式建立物件var hero = new Hero();
// isPrototypeOf()方法判斷指定物件是否為另一個物件的原型var result = obj.isPrototypeOf(hero);
console.log(result);//true驗證了
obj物件是hero物件的原型
接下來我們使用一段程式碼來展開對原型鏈的認識:
場景:查找obj物件身上的address屬性js執行的步驟:
1. 會觸發get操作2. 在目前的物件中查找屬性3. 如果沒有找到,這個時候會去原型鏈(__proto__)物件上查找1. 查找到結束2. 沒查找到一直順著原型鏈查找,直到查找到頂層原型(頂層原型是什麼暫時賣個關子) var obj = {
name: 'jam',
age: 19
}
/*
需求:找出obj物件身上的address屬性*/
// 原型鏈一層一層向上查找,如果一直找不到,直到查找到頂層原型結束obj.__proto__ = {}
obj.__proto__.__proto__ = {}
obj.__proto__.__proto__.__proto__ = {
address: '北京市'
}
console.log(obj.address) // 北京市console.log(obj.__proto__.__proto__.__proto__) // { address: '北京市' } 
最終查找到address屬性
那么这里有一个问题,如果一直没有查到,会无穷尽的去查找吗?接下来我们就来了解一下
上面我們說到,順著原型鏈不會無休止的去查找,當查到頂層原型的時候,如果還沒查到就會返回undefined 。
那麼頂層原型是什麼呢?
範例程式碼如下:
var obj = { name: 'jam' }console.log(obj.__proto__) // {}console.log(obj.__proto__.__proto__) // null字面量物件obj的原型是:
{}。{}就是頂層的原型當我們繼續向上打印__proto__時,返回一個null值,就證明上一層就已經是頂層原型瞭
如下圖是針對第一段代碼中缺少的頂層原型做的補充:
顶层原型就是Object.prototype
3.1 那麼什麼地方是原型鏈的盡頭呢?例如第三個物件是否也有原型__proto__屬性呢?
var obj = {name:'jam'}obj.__proto__ = {}obj.__proto__.__proto__ = {}obj.__proto__.__proto__.__proto__ = {}console.log(obj.__proto__.__proto__.__proto__ = {}conto__) // {}我們發現上面印出結果為空对象{}
var obj = {
name: 'jam',
age: 19
}
console.log(obj.__proto__) // {}
console.log(Object.prototype) // {}
console.log(obj.__proto__ === Object.prototype) // true Object是所有類別的父類別所以obj.__proto__其實就是Object.prototype ,
console.log(obj.__proto__ === Object.prototype) // true我們可以看出結果Object.prototype就是頂層原型
{}3.2 那麼我們可能會問: {}原型有什麼特殊的嘛?
console.log(obj.__proto__.__proto__.__proto__.__proto__.__proto__) // null
Object.prototype的結果為空物件{},但它不是空的,只是裡面的屬性不可枚舉而已,例如我們就列印constructor屬性看看<!-- 可以看出是有constructor屬性的,並不是空的-->console.log(Object.prototype.constructor) // [Function: Object] <!-- constructor 指回了Object -->
Object.getOwnPropertyDescriptors()方法來取得Object.prototype中的所有自身屬性的描述符。 console.log(Object.getOwnPropertyDescriptors(Object.prototype)) // 如下長截圖所示

