原型對象 原理解析:
我們創建的每一個函數,解析器都會向函數中添加一個屬性 prototype
這個屬性對應着一個對象,就是原型對象。
如果函數作爲普通函數,調用 prototype 沒有任何作用
當函數以構造函數的形式調用時,它所創建的對象都會有一個隱含的屬性 __proto__,
我們可以通過 __proto__ 來訪問原型對象
原型對象就相當於一個公共區域,同一個構造函數的實例都能訪問到這個原型對象
我們可以將對象共有的內容,統一設置到原型對象中
創建構造函數時,可以將對象共有的屬性和方法,統一添加到構造函數的原型對象中,
這樣不用分別爲每一個對象添加,也不會影響到全局作用域,就可以使每個對象都具有這些屬性和方法了
當我們訪問對象的一個屬性或者方法時,他會先在對象自身中尋找,如果有則直接使用,
如果沒有則會去原型對象中尋找,如果有則直接使用。
如果原型對象中也沒有,則會在原型對象的原型對象中尋找,一層一層往上找,直到找到Object的原型對象,
如果沒有,就沒有這個屬性或方法
圖解:
下面寫一些具體例子:
拿到原型對象:
// 拿到原型對象:
function method1() {
// 構造函數
function Person() {
}
// 原型對象
let yuanxing = Person.prototype;
// 實例化對象
let person1 = new Person();
// 通過對象找到原型對象 person1.__proto__
console.log(yuanxing == person1.__proto__);
// 任何對象都可以找到原型對象 person2.__proto__
let person2 = new Person();
console.log(yuanxing == person2.__proto__);
}
調用對象屬性和方法,先在自身找,自身沒有在原型對象中找
//調用對象屬性和方法,先在自身找,自身沒有在原型對象中找
function method2() {
// 構造函數
function Person() {
}
// 向原型對象中添加屬性和方法
Person.prototype.name = 'zhh';
Person.prototype.method = function (args) {
console.log('原型對象的方法>>>' + args);
}
// 調用對象屬性和方法,先在自身找,自身沒有在原型對象中找
let person1 = new Person();
let person2 = new Person();
console.log(person1.name = 'peson1');
person1.method('person1調用');
console.log(person2.name = 'perso2');
person2.method('person2調用');
}
原型對象也是對象,所以它也有原型(就是原型對象的原型對象)
當我們使用一個對象的屬性或方法時,會先在自身找,
自身有,則直接使用
自身沒有,則去原型對象中尋找,如果原型對象中有,則使用
如果原型對象中也沒有,則去 原型對象的原型對象 中去尋找。
以此類推向上尋找,直到找到Object的原型對象,Object 的原型對象,沒有原型對象,
如果依然沒找到則返回 unedfined
function method3() {
// 構造函數
function Person() {
}
let person = new Person();
// person 對象中沒有 toLocaleString() ; 調用的是原型對象中的方法
person.toLocaleString();
// 下面查找 toLocaleString() 在哪一級的原型對象裏面
// hasOwnProperty() 對象自身是否含有某個屬性或方法
// person 中是否有 toLocaleString 方法
console.log(person.hasOwnProperty('toLocaleString'));
// person的原型對象中是否有 toLocaleString 方法
console.log(person.__proto__.hasOwnProperty('toLocaleString'));
// person的原型對象的原型對象中是否有 toLocaleString 方法
// 打印結果是true
// 說明person.toLocaleString()調用的是person.__proto__.__proto__中的toLocaleString方法
console.log(person.__proto__.__proto__.hasOwnProperty('toLocaleString'));
}
OK,講完了
下面是一個綜合的例子:
<script>
// 構造函數
function Person() {
}
// 拿到原型對象,原型對象是一個公共區域
// 構造函數的實例,都能訪問到裏面的屬性和方法
let yuanxing = Person.prototype;
// 原型裏面添加屬性和方法
yuanxing.name = 'zhh';
yuanxing.say = function () {
console.log('原型對象中的方法');
}
// 實例化對象
let person1 = new Person();
// 調屬性,先在自身找,自身沒有在原型對象裏找
console.log(person1.name);
// 調方法,先在自身找,自身沒有在原型對象裏找
person1.say();
// 調屬性或方法,自身沒有,原型對象中也沒有,一層一層往上找
console.log(person1.toLocaleString());
</script>
下面對原型對象進行深層次的解析
1、原型對象在堆棧存儲
看下面一段代碼:
<script>
// 創建函數時內部語句
// this.prototype = {};
function Dog() {
this.test1 = function() {
console.log('test1方法');
}
}
// 拿到原型對象,並給原型對象添加方法
Dog.prototype.test2 = function() {
console.log('test2方法');
}
// 實例化對象時內部語句
// this.__proto__=Dog.prototype
var dog = new Dog();
dog.test1();
dog.test2();
console.log(dog.toString());
dog.test3();
</script>
上面代碼,堆棧是示意圖,(很重要,這個圖比上面Person示意圖要詳細)
2、構造函數和原型對象的相互指向
構造函數對象的 prototype 屬性,指向原型對象
原型對象中的 constructor 指向構造函數對象
看下面一段代碼:
<script>
// 構造函數和原型對象的相互指向
// 構造函數
function Type() {
}
// 拿到原型對象,
// 構造函數對象的 prototype 屬性,指向原型對象
// 原型對象就是一個 Object 的實例
let yuanxing = Type.prototype;
yuanxing.name = function() {};
// 原型對象中的 constructor 指向構造函數對象
console.log(yuanxing.constructor == Type);
</script>
圖解:
3、幾個重要概念和結論
I、兩個概念:
1.顯式原型對象 函數對象.prototype
2.隱式原型對象 實例對象.__proto__
II、Function 和Object 的三個重要結論
1、函數的原型對象都是Object的實例(但Object除外)
2、所有的函數對象都是 Function 的實例 (Function 是他自己的實例)
3、Object 的原型對象是原型鏈的盡頭
上面3個結論的驗證:
1、函數的原型對象都是Object的實例(但Object除外)
/*
* 1、函數的原型對象都是Object的實例(但Object除外)
*/
// Person的原型對象,是Object 實例
console.log(Person.prototype instanceof Object);
// Object的原型對象不是Object 實例
console.log(Object.prototype instanceof Object);
// Function的原型對象,是 Object 實例
console.log(Function.prototype instanceof Object);
2所有的函數對象都是 Function 的實例 (Function 是他自己的實例)
/*
* 2所有的函數對象都是 Function 的實例 (Function 是他自己的實例)
*/
{
// 例如:Dog函數對象,內部就是 new Function()得到的
// 所以 Dog 就是 Function 的實例
function Dog() {
}
// 實例化對象
let dog = new Dog();
// 對象中添加方法
dog.method = function() {
console.log('對象中添加方法');
}
dog.method();
}
{
// 上面的代碼可以寫成這樣
// Dog是構造函數對象
let Dog = new Function();
// 實例化對象
let dog = new Dog();
// 對象中添加方法
dog.method = function() {
console.log('對象中添加的方法2');
}
dog.method();
}
// Function是自己的實例
// Function.prototype 的Function是函數對象
// Function.__proto__ 的Function是實例對象
console.log(Function.prototype == Function.__proto__);
3.Object 的原型對象是原型鏈的盡頭
/*
* 3.Object 的原型對象是原型鏈的盡頭
*/
console.log(Object.prototype.__proto__);
根據上面的三條結論驗證 instanceof 判斷時原型鏈的指向
例子1:
// 案例1
function Foo() {
}
let foo = new Foo();
console.log(foo instanceof Foo);
console.log(foo instanceof Object);
console.log(foo instanceof Foo); 結果是true,示意圖如下:
console.log(foo instanceof Object);結果true,示意圖如下:
例子2
// 案例2
console.log(Object instanceof Function);
console.log(Object instanceof Object);
console.log(Function instanceof Function);
console.log(Function instanceof Object);
function Fooo(){}
console.log(Object instanceof Fooo);
console.log(Object instanceof Function); 結果true,示意圖如下:
console.log(Object instanceof Object);結果是true,示意圖如下:
console.log(Function instanceof Function);結果是true,示意圖如下:
console.log(Function instanceof Object);結果是true,示意圖如下:
function Fooo(){}
console.log(Object instanceof Fooo);結果是 false 結果如下:
OK,原型對象全部講完了
參考視頻
http://www.gulixueyuan.com/my/course/58
http://www.gulixueyuan.com/course/194
js繼承機制的設計思想
http://www.ruanyifeng.com/blog/2011/06/designing_ideas_of_inheritance_mechanism_in_javascript.html