理解原型及原型鏈(筆記)
私有變數和函式
Javascript中是沒有塊級作用域的,但是有函式作用域。在函式內部定義的變數和函式是私有的,不能被外界訪問到。
function Foo(){
//私有變數
var name='A';
//私有函式
var fn=function(){
return 'fn';
}
console.log('inside: name:' + name); //inside: name:A
console.log('inside: fn:' + fn()); //inside: fn:fn
}
Foo();
console.log ('outside: name:' + name); //Uncaught TypeError: name is not defined
console.log('outside: fn:' + fn()); //Uncaught TypeError: fn is not a function(…)
靜態變數和函式
當定義一個函式後通過點號 “.”為其新增的屬性和函式,通過物件本身仍然可以訪問得到,但是其例項卻訪問不到,這樣的變數和函式分別被稱為靜態變數和靜態函式。
function Person(){
//靜態變數
Person.eye= 2;
//靜態函式
Person.say=function(){
console.log('Hello');
}
};
Person();
console.log(Person.eye);//2
Person.say();//Hello
var Tom= new Person();
console.log(Tom.eye);//undefined
Tom.say();//Uncaught TypeError: Tom.say is not a function(…)
例項變數和函式
在面向物件程式設計中除了一些庫函式我們還是希望在物件定義的時候同時定義一些屬性和方法,例項化後可以訪問,JavaScript也能做到這樣
function Person(){
//例項變數
this.eye=2;
//例項方法
this.say=function(){
console.log('Hello');
}
}
var Tom = new Person();
console.log(Tom.eye);//2
Tom.say();//Hello
prototype的由來
下例中,我們發現例項中改變了屬性,並不影響另外一個例項。兩個例項中的屬性方法名相同,但不是一個引用,而是對Person物件定義的屬性和方法的複製。
但是對於方法而言,完全一樣的功能但複製了無數份,顯然不科學。於是,prototype應運而生。
function Person(){
this.name=[];
this.say=function(){
console.log('Hello');
}
}
var Tom = new Person();
Tom.name.push('Tom');
console.log(Tom.name);//["Tom"]
console.log(typeof Tom.name);//object
var Jerry= new Person();
console.log(Jerry.name);//[]
Jerry.name.push('Jerry');
console.log(Jerry.name);//["Jerry"]
prototype的基本概念
建立函式時,會自動建立一個名為prototype(原型物件)的屬性,它的用途是包含所有例項共享的屬性和方法。prototype呼叫建構函式建立物件例項的原型物件。
prototype中除了有建構函式,還有一個屬性 __proto__
,它指向建立它的函式物件的prototype
var Person=function(){};
Person.prototype.name='super man';
Person.prototype.say=function(){
console.log('Hello');
}
var Tom = new Person();
console.log(Tom.name);//super man
Tom.name= 'Tom';
console.log(Tom.name);//Tom
var Jerry= new Person();
console.log(Jerry.name);//super man
Jerry.say(); //Hello
在呼叫Jerry.say()
時,Jerry沒有這個方法,於是會沿著Jerry的__proto__
(指向的是Person的prototype)向上查詢,找到了say()
方法,於是執行。
原型及原型鏈
var Person=function(name){
this.name=name;
};
Person.prototype.say=function(){
console.log('Hello, I am ' + this.name);
}
var person1 = new Person('Byron');
person1.say(); //Hello, I am Byron
var person2= new Person('Frank');
person2.say(); //Hello, I am Frank
我們把這個有__proto__
串起來的直到Object.prototype.__proto__
為null的鏈叫做原型鏈。
原型繼承
function Father(){
this.familyname='Lee';
}
Father.prototype.getFamilyName = function(){
console.log('My family name is ' + this.familyname);
}
function Son(){
this.age=1;
}
Son.prototype= new Father();
var Tom = new Son();
console.log(Tom.familyname); //Lee
實現繼承的核心就是Son.prototype= new Father();
,為什麼這樣就實現了繼承呢?我們通過框圖來理解。
上文中提到,例項物件(此處為new Father())的__proto__
指向的是建立它的函式物件的prototype(此處為Father.prototype
),於是能訪問到Father.prototype
中的getFamilyName
方法。Son.prototype
等價於 new Father()
,則Son.prototype.__proto__
等價於 new Father()
的__proto__
。於是Son和Fahter之間產生了繼承的關係。
當例項化一個Son時(上程式碼中為Tom),Tom的__proto__
指向Son.prototype
,Son.prototype
中的__proto__
指向Father.prototype
,Father.prototype
中含有getFamilyName
方法。
強調一下:函式物件有prototype
屬性,普通物件沒有prototype
,但有__proto__
屬性