s 深入理解原型和原型鏈?
構造函數
- 構造函數的好處我們可以把我們的屬性和方法都添加到一個函數上面去
function User(name, age, gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
var panda = new User('panda', 18, 'male');
console.log('panda:', panda);
//使用 new 的過程我們成為 ==實例化==
理解原型和原型鏈
怎么理解 prototype 和 __proto__ 啦?
- 當我們使用普通的構造函數來搭建我們的方法的時候:
function User(name,age,gender){
this.name = name ;
this.age = age ;
this.gender = gender ;
this.eat = function(){
console.log('miao')
} ;
this.greeting = function(){
console.log('yo! my name is '+this.name)
}
}
// 我們每次使用的時候都需要重新的實例化對象
// 這樣做都會重新的向內存申請新的空間,太浪費
var panda = new User();
- 為了防止空間浪費的情況我們使用prototype的方法去設計我們的函數,這樣做
function A(){}
A.prototype.name = 'lala';
var a = new A();
var b = new A();
//首先打開你的瀏覽器
//我們可以先看看 __proto__ 是什么東西
console.log(a.__proto__);
console.log(a.constructor);
// 然后比較一下 a,b 的__proto__是否指向相同
console.log(a.__proto__ === b.__proto__);//true
- 可以看到新實例化的對象的proto指向的是 一個 Object, a的構造器(constructor) 指向的就是我們創(chuàng)建的 A()
這里就需要解釋一下其中的 Object 是什么東東?還有 constructor 又是什么東西?
- 記住這么一句話 在 JavaScript 所有的都是對象
這里有一個 case
new的時候js都干了什么
在 JavaScript 中,使用 new 關鍵字后,意味著做了如下四件事情:
1:創(chuàng)建一個新的對象,這個對象的類型是 object;
2:設置這個新的對象的內部、可訪問性和[[prototype]]屬性為構造函數(指prototype.construtor所指向的構造函數)中設置的;
3:執(zhí)行構造函數,當this關鍵字被提及的時候,使用新創(chuàng)建的對象的屬性; 返回新創(chuàng)建的對象(除非構造方法中返回的是‘無原型’)。
4:在創(chuàng)建新對象成功之后,如果調用一個新對象沒有的屬性的時候,JavaScript 會延原型鏈向止逐層查找對應的內容。這類似于傳統(tǒng)的‘類繼承’。
//打開你的瀏覽器的console 試試吧
//在JavaScript中我們都知道怎么創(chuàng)建 對象 Object
//其實 aa 創(chuàng)建的方法 只是 bb 創(chuàng)建 {} 的一個簡寫,其實我們是實例化了 一個對象。
var aa = {};
var bb = new Object();
console.log(a.constructor === b.constructor)//true
console.log(a.__proto__ === b.__proto__)//true
//我們可以看到他們的constructor 和 __proto__ 都是指向的相同的
//具體的內容,可以看到原型 __proto__ 指向的是一個 Object 然后繼承了一大堆方法
//測試數組
var a = [];
var a = new Array();
console.log('a',a)
上面涉及繼承,這個東西我們馬上談到
- 使用 create 創(chuàng)建純潔的 啥也沒有繼承的 Object
//純潔的 object
var pure = Object.create({});
console.log('pure:',pure.__proto__)
//來對比一下變量 a
var a = {};
console.log('a:',a.__proto__)
//用這樣的方法創(chuàng)建的變量 pure 可以看到它什么都沒有繼承,查找它的 __proto__ 也什么都沒有,零污染
多級繼承鏈怎么實現
理解了 prototype 終于要說怎么實現原型上面的繼承了。
-
看代碼,我們先設計三個東東讓他們達成繼承關系
- 一開始我們是三個對象函數
- 常理來說 是這樣的繼承關系
- Person --> Manmal --> Animal
function Animal() {
}
function Manmal() {
}
function Person() {
}
- 我們首先賦予 Animal 一些能力(屬性和方法),然后來實例化一些 Animal
Animal() Code
function Animal(color,weight) {
this.color = color;
this.weight = weight;
}
Animal.prototype.eat = function(){
console.log('mia mia mia...');
}
Animal.prototype.sleep = function(){
console.log('hu lu lu...');
}
//繼承能力
var panda = new Animal('red',180);
var monkey = new Animal('yellow',20);
console.log(panda);
console.log(monkey);
console.log('a.eat === b.eat:',a.eat === b.eat);//true
//思考 我們怎樣讓我們的Burce來繼承原始的 Animal 的能力(屬性和方法)啦?
var Burce.L = new Person();
- 接著上面代碼的問題,我們要讓 Burce.L 繼承 Animal 的能力, 因為 Burce 是一個 Person(),所以我們這樣做一下準備工作 ,讓我們的 Mannal 來 繼承 Animal ,思路齊了。開始工作
- 代碼如下
//現在 Manmal 空空如也什么都還沒有
function Manmal() {
}
//要怎么繼承啦?可能我們開始接觸 prototype 最直接的方法是 將 Mannal的 prototype 直接的指向 Animal 的 prototype
Manmal.prototype = Animal.prototype;
//這樣的方法可行,但是但我們修改 Manmal的繼承屬性的時候會污染我們的 Animal
//所以我們使用如下的方法,開辟一個無污染的空間
Manmal.prototype = Object.create(Animal.prototype);
var a = new Manmal();
//現在來看看 a 的情況
console.log(a.__proto__);
console.log(a.constructor);
//我們可以看到 __proto__ 指向了 Animal
//但是有個問題就是 這里的 constructor 應該指向我們的 new 的構造器 Manmal ,所以我們這里還得添加一行代碼
Manmal.prototype.constructor = Manmal;
var b = new Manmal();
console.log(b.__proto__);
console.log(b.constructor);
console.log(b.eat);
//現在達到我們的想法了,將我們的 Manmal 繼承了 Animal 的 ==方法==
那么屬性要怎么繼承啦?這里就要使用call方法了
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/call
function Manmal(color,weight){
//綁定Animal的 顯性屬性的繼承方式
Animal.call(this,color,weight);
}
- 整理一下關于 Manmal()的代碼
Manmal() Code
function Manmal(color,weight){
//綁定Animal的 顯性屬性的繼承方式
Animal.call(this,color,weight);
}
Manmal.prototype = Object.create(Animal.prototype);
Manmal.prototype.constructor = Manmal;
//將繼承的constructor 再重新指回 Manmal constructor應該為父輩,這里指向了爺爺
//我們隨意給Manmal添加一個方法
Manmal.prototype.suckle = function(){
console.log('biu biu biu...');
}
//test
var a = new Manmal('red',100);
console.log(a);
- 同理整理一下關于 Person()的代碼
Person() Code
//修改Person
function Person(color,weight) {
//綁定Animal的 本地屬性
Manmal.call(this,color,weight);
}
Person.prototype = Object.create(Manmal.prototype);
Person.prototype.constructor = Manmal;
Person.prototype.lie = function(){
console.log('你不帥!');
}
var Bruce = new Person('yellow',120);
console.log('Bruce:',Bruce);
- 最后 整合我們所有的代碼
function Animal(color,weight){
this.color = color;
this.weight = weight;
}
Animal.prototype.eat = function(){
console.log('mia mia mia...');
}
Animal.prototype.sleep = function(){
console.log('hu lu lu...');
}
function Manmal(color,weight){
//綁定Animal的 顯性屬性的繼承方式
Animal.call(this,color,weight);
}
function Person(color,weight) {
//綁定Animal的 本地屬性
Manmal.call(this,color,weight);
}
//使用繼承 manmal --> animal
Manmal.prototype = Object.create(Animal.prototype);
Manmal.prototype.constructor = Manmal;
//將繼承的constructor 再重新指回 Manmal constructor應該為父輩,這里指向了爺爺
Manmal.prototype.suckle = function(){
console.log('biu biu biu...');
}
//修改Person
Person.prototype = Object.create(Manmal.prototype);
Person.prototype.constructor = Manmal;
Person.prototype.lie = function(){
console.log('你不帥!');
}
var Bruce = new Person('yellow',120);
console.log('Bruce:',Bruce);
總結
幾個原型鏈之間的關系:
image