原型鏈

引入從Object和Function開始

Object和Function都做爲JS的自帶函數,Object繼承本身,Funtion繼承本身,Object和Function互相是繼承對方,也就是說Object和Function都既是函數也是對象。函數

1
2
console.log(Function instanceof Object); // true
console.log(Object instanceof Function); // true

 

Object 是 Function的實例,而Function是它本身的實例post

1
2
console.log(Function.prototype); // ƒ () { [native code] }
console.log(Object.prototype); // Object

 

普通對象和函數對象

JavaScript 中,萬物皆對象!但對象也是有區別的。分爲普通對象和函數對象,Object 、Function 是 JS 自帶的函數對象。下面舉例說明ui

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var o1 = {}; 
var o2 =new Object();
var o3 = new f1();

function f1(){};
var f2 = function(){};
var f3 = new Function('str','console.log(str)');

console.log(typeof Object); //function
console.log(typeof Function); //function

console.log(typeof f1); //function
console.log(typeof f2); //function
console.log(typeof f3); //function

console.log(typeof o1); //object
console.log(typeof o2); //object
console.log(typeof o3); //object

在上面的例子中 o1 o2 o3 爲普通對象,f1 f2 f3 爲函數對象。怎麼區分,其實很簡單,凡是經過 new Function() 建立的對象都是函數對象,其餘的都是普通對象。f1,f2,歸根結底都是經過 new Function()的方式進行建立的。Function Object 也都是經過 New Function()建立的。this

函數原型:

在理解原型以前有兩句很重要的話:spa

一、每個函數對象都有一個prototype屬性,可是普通對象是沒有的;
prototype下面又有個construetor,指向這個函數。
二、每一個對象都有一個名爲proto的內部屬性,指向它所對應的構造函數的原型對象,原型鏈基於proto;prototype

prototype屬性

每一個函數都有prototype屬性,默認指向一個Object空對象(稱爲:原型對象)code

1
2
3
function Foo() {
}
console.log(Foo.prototype) //constructor __proto__

 

函數原型鏈
這裏爲何要說是空對象,明明看到有constructor、__proto__兩個屬性,實際上是這個Object對象中沒有咱們所須要的屬性,固然能夠去向原型中添加咱們須要屬性或者方法。對象

嘗試給原型對象添加屬性(通常都是方法)

1
2
3
4
Foo.prototype.method=function () {
console.log("method()")
}
console.log(Foo.prototype)

此時在原型中能夠看到method屬性已經添加到Foo()中去了。
函數原型鏈
如今咱們經過建立實例去訪問咱們剛纔添加的方法繼承

1
2
var fun=new Foo()
fun.method() // 能夠看到返回的是method()

 

能夠看到構造函數跟原型對象是相互引用的關係。ip

這裏須要區分顯示原型和隱式原型

每一個函數function都有一個prototype,即顯示原型(屬性)
每個實例對象都有一個__proto__ 稱爲隱式原型(屬性)
咱們繼續以前的構造函數:

1
2
3
4
5
var fun=new Foo() 
console.log(fun.__proto__)
//對象的隱式原型的值爲其對應構造函數的顯示原型的值
console.log(Foo.prototype===fun.__proto__)//true
fun.method() // 能夠看到返回的是method()

 

能夠看到對象的__proto__屬性在建立對象時是自動添加的,默認值爲構造函數的prototype屬性值。

constructor(構造函數)

在原型對象中有一個屬性constructor,當咱們自定義構造函數以後,其原型對象只會默認取得constructor值,能夠經過該屬性判斷出實例是由哪一個構造函數建立的。

1
console.log(fun.prototype.constructor===Foo) //true

 

那麼構造函數的原型對象是由什麼來建立的呢?

咱們去構造函數中尋找:

1
Foo.prototype.__proto__.constructor // ƒ Object() { [native code] }

 

這樣一來能夠看到構造函數原型鏈的其餘方法,原來是從Object 上繼承來的。
這裏的Object原本就存在,此時咱們能夠追溯到它的原型鏈。

原型鏈

原型鏈(本質上就隱式原型鏈,是用來查找對象的。)
函數原型鏈
在建立函數以前,已經就有了一個Object函數對象,js在加載引擎的時候首先會把這個內置的函數加載進來,而後在去執行咱們的一些方法,訪問一個對象的屬性時,先從自身屬性中查找,找到返回,若是沒有,再沿着__proto__這條鏈上查找,找到返回,若是最終沒找到,返回undefined。

注意事項:

  1. 函數的顯示原型指向的對象默認是Object實例對象(可是Object不知足)

    1
    2
    3
    console.log(Fn.prototype instanceof Object) //true
    console.log(Object.prototype instanceof Object) //false
    console.log(Function.prototype instanceof Object) //true
  2. 全部函數都是Function的實例

    1
    console.log(Function.__proto__===Function.prototype) //true
  3. Object的原型對象是原型鏈的盡頭

    1
    console.log(Object.prototype.__proto__) //null
  4. (1)讀取對象的屬性值時,會自動到原型鏈中查找
    (2)設置對象的屬性時,不會查找原型鏈,若是當前對象中沒有此屬性,直擊添加屬性並設置其值
    (3)方法必定定義在原型中,屬性通常經過構造函數定義在對象自己上,

    1
    2
    3
    4
    5
    6
    7
    8
    9
    function Fn() {
    }

    Fn.prototype.a = 'xxx';
    var fn1 = new Fn()
    console.log(fn1.a, fn1)//xxx
    var fn2 = new Fn()
    fn2.a = 'yy'
    console.log(fn2.a, fn2) //yy

原型鏈屬性問題

通常狀況下,咱們會將屬性直接添加在函數中,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 
function Person(name, age) {
this.name = name;
this.age = age
}

Person.prototype.setName=function (name) {
this.name = name
}

var p1 = new Person('tom', 12)
p1.setName('bob')
console.log(p1)


var p2 = new Person('errol', 12)
p2.setName('charlie')
console.log(p2)

 

方法問題

能夠看實例對象的隱式原型等於構造函數的顯示原型,它們都構造函數都是指向的是Person,因此它們的proto都是相等的。

相關文章
相關標籤/搜索