1. 程式人生 > >面試 | JS高階---原型到原型鏈(一看就懂)

面試 | JS高階---原型到原型鏈(一看就懂)

文中關於一些概念說了可能三到四邊,或者更多,希望是加深大家的印象,希望理解,重要的概念說三遍,只要你能理解,我囉嗦五次都願意。

建構函式建立物件

==========咱們先來一個栗子=======

function Person(name){    
	//建構函式
	this.name=name; 
} 
Person.prototype.printName=function( ){   
	//原型物件
	alert(this.name); 
} 
var person1=new Person('ming');  //例項化物件
console.log(person1.__proto__);  //這裡自己列印一下,印象更深刻
console.log(person1.constructor);//
console.log(Person.prototype);
var person2=new Person('Frank');


//列印的結果

// {printName: ƒ, constructor: ƒ}
// ƒ Person(name){    
	//建構函式           
	// this.name=name; 
}
// {printName: ƒ, constructor: ƒ}

person1.__proto__ == Person.prototype  // true
 
   (注意這裡的大小寫)Person的例項person1中包含了name屬性,同時也自動生成一個__proto__屬性,該屬性指向Person的prototype,可以訪問到prototype內定義的printName方法,大概就  是這個樣子的:每個JavaScript函式都有prototype屬性,這個屬性引用了一個物件,這個物件就是**原型物件**。**原型物件初始化的時候是空的**,我們可以在裡面自定義任何屬性和方法,這些方法和屬性都將被該**建構函式**所建立的物件**繼承**。(下一篇我會更詳細的介紹原型,很暴力的)

例項是通過建構函式建立的。例項一創造出來就具有constructor屬性(指向建構函式)和proto

屬性(指向原型物件),

建構函式中有一個prototype屬性,這個屬性是一個指標,指向它的原型物件

原型物件內部也有一個指標(constructor屬性)指向建構函式:Person.prototype.constructor = Person;

例項可以訪問原型物件上定義的屬性和方法。

在這裡person1和person2就是例項,prototype是他們的原型物件。

 

function 才有prototype  ,原型物件有__proto__

//========再來一個栗子===========

function Animal(name){    //建構函式

    this.name = name; //設定物件屬性
}

Animal.prototype.behavior = function() {  

    console.log("this is a "+this.name);

}
var Dog = new Animal("dog");
var Cat = new Animal("cat");

Dog.behavior();//通過Dog物件直接呼叫behavior方法  // "this is a dog"

Cat.behavior(); //"this is a cat"

console.log(Dog.behavior==Cat.behavior);// true;

//總結:建構函式的prototype上定義的方法確實可以通過物件直接呼叫到,而且程式碼是共享的。prototype屬性指向Animal物件。

================= 開始正題 ==================

-1-Prototype:

每個函式都有一個prototype屬性,也只有函式有該屬性。

function Person(){ }

Person.prototype.name = 'name';

var person1 = new Person();

var person2 = new Person();

console.log(person1.name) // name

console.log(person2.name) //name

問:那這個函式的prototype屬性到底指向的是什麼呢?是這個函式的原型嗎?

|    |
|    |

總結:函式的prototype屬性指向了一個物件,這個物件正是呼叫該建構函式而建立的例項的原型,也就是這個例子中的person1和person2的原型。

問:什麼是原型?每一個JavaScript物件(null除外)在建立的時候就會與之關聯另一個物件,這個物件就是我們所說的原型,每一個物件都會從原型”繼承”屬性。

-2-proto

怎麼表示例項與例項原型,也就是person和Person.prototype之間的關係呢,這時候我們就要講到第二個屬性:

proto。每一個JavaScript物件(除了null)都具有的一個屬性,叫proto,這個屬性會指向該物件的原型。

function Person( ){

}

var person = new Person();

console.log(person.__proto__ === Person.prototype); //true

問:既然例項物件和建構函式都可以指向原型,那麼原型是否有屬性指向建構函式或者例項呢?

-3-  constructor

沒有指向例項的,因為一個建構函式可以生成多個例項,但是原型指向建構函式是有的,這就要講到第三個屬性:construcotr,每個原型都有一個constructor屬性指向關聯的建構函式

為了驗證這一點,我們可以嘗試:

function Person() {

}

console.log(Person === Person.prototype.constructor); //true

精華----綜上總結:(看清大下寫)

function Person() {  //建構函式

}

var person = new Person(); //例項物件 

console.log(person.__proto__ == Person.prototype) //true

console.log(Person.prototype.constructor == Person) //true // 順便學習一個ES5的方法,可以獲得物件的原型
console.log(Object.getPrototypeOf(person) === Person.prototype) //true

例項與原型

當讀取例項的屬性時,如果找不到,就會查詢與物件關聯的原型中的屬性,如果還查不到,就去找原型的原型,一直找到最頂層為止。

======舉個例子:=========

function Person() {

}

Person.prototype.name = 'name';

var person = new Person();

person.name = 'name of this person';

console.log(person.name) // name of this person

delete person.name;

console.log(person.name) // name

我設定了person的name屬性,所以我可以讀取到為’name of this person’,當我刪除了person的name屬性時,讀取person.name,從person中找不到就會從person的原型也就是person.proto == Person.prototype中查詢,幸運的是我們找到了為’name’,但是萬一還沒有找到呢?哪原型的原型又是什麼呢?

在前面,我說過原型也是一個物件,既然是物件,我就可以用最原始的方式建立它,那就是

var obj = new Object();//字面兩建立物件new一個

obj.name = 'name';

console.log(obj.name) // name

所以原型物件是通過Object建構函式生成的,結合之前所講,例項的proto指向建構函式的prototype,

原型鏈

問:Object.prototype的原型是什麼?

null,嗯,對就是null,所以查到Object.prototype就可以停止查找了

補充,最後,補充和糾正本文中一些不嚴謹的地方:

首先是constructor,

function Person() {

}

var person = new Person();

console.log(person.constructor === Person); // true

當獲取person.constructor時,其實person中並沒有constructor屬性,當不能讀取到constructor屬性時,會先從person的原型也就是Person.prototype中讀取,正好原型中有該屬性,所以

person.constructor === Person.prototype.constructor

後續補充

經典面試題目

 

//有關原型與原型鏈的面試題目
{
	function Fn(){
		this.x = 100;
		this.y = 200;
		this.getX = function () {
			console.log(this.x);
		}
	}
	Fn.prototype.getX = function () {
		console.log(this.x);
	};
	Fn.prototype.getY = function () {
		console.log(this.y);
	};
	var f1 = new Fn;
	var f2 = new Fn;
	console.log(f1.getX === f2.getX);	//false
	console.log(f1.getY === f2.getY);	//true
	console.log(f1.__proto__.getY === Fn.prototype.getY);	//true
	console.log(f1.__proto__.getX === f2.getX);		//false
	console.log(f1.__proto__.getX === Fn.prototype.getX);	//true
	console.log(f1.constructor);	//Fn
	console.log(Fn.prototype.__proto__.constructor);	//Object
	f1.getX();	//100	this=>f1
	f1.__proto__.getX();	//undefined 	this=>f1.__proto__(Fn.prototype)
	f2.getY();	//200	this=>f2
	Fn.prototype.getY();	//undefined	this=>f2.__proto__(Fn.prototype)
}
// false,true,true,false,true,Fn,Object,100,undefined,200,undefined

 

 

// 原型、原型鏈
{
	function fun(){
		this.a = 0;
		this.b = function(){
			console.log(this.a);
		}
	}
	fun.prototype = {
		b: function(){
			this.a = 20;
			console.log(this.a);
		},
		c: function(){
			this.a = 30;
			console.log(this.a);
		}
	}
	var my_fun = new fun();
	my_fun.b();	//私有方法	this=>my_fun
	console.log(my_fun.a);
	my_fun.c();	//公有方法	this=>my_fun this.a = 30(將私有屬性a修改為30)
	console.log(my_fun.a);
	var my_fun2 = new fun();
	console.log(my_fun2.a);
	my_fun2.__proto__.c();	//this=>my_fun2.__proto__ 當前例項通過原型鏈在類的共有屬性上增加了一個a:30
	console.log(my_fun2.a);
	console.log(my_fun2.__proto__.a);
}
// 0,0,30,30,0,30,0,30