1. 程式人生 > >JS重點整理之JS原型鏈徹底搞清楚

JS重點整理之JS原型鏈徹底搞清楚

物件

要清楚原型鏈,首先要弄清楚物件:

  • 普通物件
    • 最普通的物件:有__proto__屬性(指向其原型鏈),沒有prototype屬性。
    • 原型物件(person.prototype 原型物件還有constructor屬性(指向建構函式物件))
  • 函式物件:
    • 凡是通過new Function()建立的都是函式物件。
                          擁有__proto__、prototype屬性(指向原型物件)。

                          FunctionObjectArrayDateString、自定義函式

                          特例: Function.prototype(
是原型物件,卻是函式物件,下面會有解釋)
函式物件
function f1(){};
var f2 = function(){};
var f3 = function("n1","n2","return n1+n2");

console.log(typeof f1);  //function
console.log(typeof f2);  //function
console.log(typeof f3);   //function
console.log(typeof Object);   //function
console.log(typeof Array);   //function
console.log(typeof String);   //function
console.log(typeof Date);   //function
console.log(typeof Function);   //function

Array是函式物件,是Function的例項物件,Array是通過newFunction創建出來的。因為Array是Function的例項,所以Array.__proto__ ===  Function.prototype

普通物件
var o1 = new f1(); 
var o2 = {};       
var o3 = new Object(); 

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

原型物件

        每建立一個函式都會有一個prototype屬性,這個屬性是一個指標,指向一個物件(通過該建構函式建立例項物件的原型物件)。原型物件是包含特定型別的所有例項共享的屬性和方法。原型物件的好處是,可以讓所有例項物件共享它所包含的屬性和方法。         第一塊中有提到,原型物件屬於普通物件。Function.prototype是個例外,它是原型物件,卻又是函式物件,作為一個函式物件,它又沒有prototype屬性。
function person(){};

console.log(typeof person.prototype) //Object
console.log(typeof Object.prototype) // Object
console.log(typeof Function.prototype) // 特殊 Function
console.log(typeof Function.prototype.prototype) //undefined 函式物件卻沒有prototype屬性

解釋:

     functionperson(){};

        其實原型物件就是建構函式的一個例項物件。person.prototype就是person的一個例項物件。相當於在person建立的時候,自動建立了一個它的例項,並且把這個例項賦值給了prototype。

 function person(){};
var temp = new person();
person.prototype = temp;

function Function(){};
var temp = new Function();
Function.prototype = temp; //由new Function()產生的物件都是函式物件

        從一張圖看懂原型物件、建構函式、例項物件之間的關係

function Dog(){};

Dog.prototype.name = "小黃";
Dog.prototype.age =  13;
Dog.prototype.getAge = function(){
	return this.age;
}

var dog1 = new Dog();
var dog2 = new Dog();

dog2.name = "小黑";
console.log(dog1.name); // 小黃 來自原型
console.log(dog2.name); // 小黑 來自例項


//圖中的一些關係
dog1.__proto__ === Dog.prototype

Dog.prototype.__proto__ === Object.prototype //繼承Object 下面原型鏈說

dog1.__proto__.__proto__ === Object.prototype

Dog.prototype.constructor === Dog 

Dog.prototype.isPrototypeOf(dog1)

獲取物件的原型
dog1.__proto__  //不推薦
Object.getPrototypeOf(dog1) === Dog.prototype   //推薦


原型鏈

原型鏈是實現繼承的主要方法。

先說一下繼承,許多OO語言都支援兩張繼承方式:介面繼承、實現繼承。

|- 介面繼承:只繼承方法簽名

|- 實現繼承:繼承實際的方法

由於函式沒有簽名,在ECMAScript中無法實現介面繼承,只支援實現繼承,而實現繼承主要是依靠原型鏈來實現。

原型鏈基本思路:

利用原型讓一個引用型別繼承另一個引用型別的屬性和方法。

每個建構函式都有一個原型物件,原型物件都包含一個指向建構函式想指標(constructor),而例項物件都包含一個指向原型物件的內部指標(__proto__)。如果讓原型物件等於另一個型別的例項,此時的原型物件將包含一個指向另一個原型的指標(__proto__),另一個原型也包含著一個指向另一個建構函式的指標(constructor)。假如另一個原型又是另一個型別的例項……這就構成了例項與原型的鏈條。

原型鏈基本思路(圖解):


舉例說明:

function animal(){
	this.type = "animal";
}
animal.prototype.getType = function(){
	return this.type;
}

function dog(){
	this.name = "dog";
}
dog.prototype = new animal();

dog.prototype.getName = function(){
	return this.name;
}

var xiaohuang = new dog();
//原型鏈關係
xiaohuang.__proto__ === dog.prototype
dog.prototype.__proto__ === animal.prototype
animal.prototype.__proto__ === Object.prototype
Object.prototype.__proto__ === null

圖解:


詳細圖


從xiaohuang這個例項,看出整個鏈條

總結:

Xiaohuang這個Dog的例項物件繼承了Animal,Animal繼承了Object。