原型是Javascript中的繼承的基礎,JavaScript的繼承主要依靠原型鏈來實現的。html
在JavaScript中,咱們建立一個函數A(就是聲明一個函數), 就會爲該函數建立一個prototype屬性。並且也會在內存中建立一個對象B,A函數的屬性 prototype 指向這個對象B( 即:prototype的屬性的值是這個對象 )。這個對象B就是函數A的原型對象,簡稱函數的原型。這個原型對象B 默認會有一個屬性 constructor, constructor屬性指向函數A ( 意思就是說:constructor屬性的值是函數A )。java
/*
聲明一個函數,則這個函數默認會有一個屬性叫 prototype 。並且瀏覽器會自動按照必定的規則
建立一個對象,這個對象就是這個函數的原型對象,prototype屬性指向這個原型對象。這個原型對象
有一個屬性叫constructor 執行了這個函數
注意:原型對象默認只有屬性:constructor。其餘都是從Object繼承而來,暫且不用考慮。
*/
function Person () {
}
複製代碼
下面的圖描述了聲明一個函數以後發生的事情:瀏覽器
當把一個函數做爲構造函數 (理論上任何函數均可以做爲構造函數) 使用new建立對象的時候,那麼這個對象就會存在一個默認的不可見的屬性,來指向了構造函數的原型對象。 這個不可見的屬性咱們通常用 [[prototype]] 來表示,只是這個屬性沒有辦法直接訪問到。bash
function Person () { }
/*
利用構造函數建立一個對象,則這個對象會自動添加一個不可見的屬性 [[prototype]], 並且這個屬性
指向了構造函數的原型對象。
*/
var p1 = new Person();
複製代碼
觀察下面的示意圖:函數
__proto__ 是對象實例纔有的屬性,指向對象的原型。
prototype 是構造函數纔有的屬性,該屬性指向了一個對象,這個對象正是調用該構造函數而建立的實例的原型
實例的__proto__屬性 和 構造函數的 prototype 都指向該對象原型
複製代碼
這幾句話能解釋一切關於原型方面的問題:優化
當 new 一個函數的時候會建立一個對象,『函數.prototype』 等於 『被建立對象.__proto__』
一切函數都是由 Function 這個函數建立的,因此『Function.prototype === 被建立的函數.__proto__』
一切函數的原型對象都是由 Object 這個函數建立的,因此『Object.prototype === 一切函數.prototype.__proto__』
複製代碼
原型鏈基本思路:利用原型讓一個引用類型繼承另外一個引用類型的屬性和方法。this
每一個構造函數都有一個原型對象,原型對象都包含一個指向構造函數想指針(constructor),而實例對象都包含一個指向原型對象的內部指針(__proto__)。
若是讓原型對象等於另外一個類型的實例,此時的原型對象將包含一個指向另外一個原型的指針(__proto__),另外一個原型也包含着一個指向另外一個構造函數的指針(constructor)。
假如另外一個原型又是另外一個類型的實例……這就構成了實例與原型的鏈條。
複製代碼
原型鏈基本思路(圖解):spa
'__proto__'是對象的屬性、'prototype'是函數的屬性
null是對象原型鏈的終點,其值既有(是一個對象)又無(不引用任何對象),
表明着對象本源的一種混沌、虛無的狀態,正與老子《道德經》中的「道」,有着同等的意義
(心中一萬隻艹尼瑪奔騰而過,仍是寫java爽啊)。
在JS中,undefined是全局對象的一個屬性,它的初始值就是原始數據類型undefined,而且沒法被配置,也沒法被改變。
undefined從字面意思上理解爲「未定義」,即表示一個變量沒有定義其值。
而null是一個JS字面量,表示空值,即沒有對象。
與undefined相比,null被認爲是「指望一個對象,可是不引用任何對象的值」,而undefined是純粹的「沒有值」。
複製代碼
從一張圖看懂原型對象、構造函數、實例對象之間的關係prototype
prototype:構造函數中的屬性,指向該構造函數的原型對象。
constructor:原型對象中的屬性,指向該原型對象的構造函數
_proto_:實例中的屬性,指向new這個實例的構造函數的原型對象
複製代碼
優勢:實現了繼承屬性,但值都不相同指針
缺點: 沒法繼承父級類別中原型上的方法
function Person(name,age,sex,weight){
this.name=name;
this.age=age;
this.sex=sex;
this.weight=weight;
}
Person.prototype.sayHi=function(){
console.log("您好")
}
function Student(name,age,sex,weight,score){
//將當前實例對象傳入Person 借過來使用一次來達到繼承效果
Person.call(this,name,age,sex,weight);
this.score=score;
}
var stu1=new Student("小明",10,"男","10kg","100")
複製代碼
利用prototype,將Student 的prototype 指向 Person 來達到繼承效果,
優勢:繼承了父級原型上的方法
缺點: 實例化多個Student 都必須共用相同的name 和 age
// 此處
Student.prototype.constructor=Student
複製代碼
function Person(name,age){
this.name=name;
this.age=age;
}
Person.prototype.eat=function(){
console.log("Person 吃飯")
}
function Student(num,score){
this.num=num
this.score=score
}
//繼承
Student.prototype=new Person("小紅",10)
Student.prototype.constructor=Student
var stu =new Student(2016002288,80)
stu.eat()//Person 吃飯
複製代碼
組合繼承其實就是結合了上述的兩種方法來實現繼承,擁有兩種方法的優勢
function Person(name,age,sex){
this.name=name;
this.age=age;
this.sex=sex;
}
Person.prototype.sayHi=function(){
console.log("你好")
}
function Student(name,age,sex,score){
//借用構造函數
Person.call(this,name,age,sex)
this.score=score
}
// 改變了原型指向
Student.prototype=new Person();//不傳值
Student.prototype.eat=function(){
console.log("吃東西");
}
var stu=new Student("小黑",20,"男","100分")
console.log(stu.name,stu.age,stu.sex,stu.score);
stu.sayHi()//你好
stu.eat()//吃東西
複製代碼
相似於複製,把一個對象中的屬性和方法直接複製到另外一個對象中
function Person(){
}
Person.prototype.name="小紅"
Person.prototype.age=18
function Student(){
}
var p=Person.prototype;
var s=Student.prototype;
for(key in p){
s[key]=p[key]
}
console.dir(Student)
複製代碼
每次都要for in 好累 , 能夠進行優化封裝一下
function extend(Child,Parent) {
    var p = Parent.prototype;
    var c = Child.prototype;
    for (var i in p) {
      c[i] = p[i];
      }
//這個屬性直接指向父對象的prototype屬性,能夠直接調用父對象的方法,爲了實現繼承的完備性,純屬備用性質
    c.par = p;
  }
複製代碼
優勢 : 效率比較高
缺點 : 由於至關因而個傳址過程 因此修改Student的屬性 Person 的也會被更改
function Person(){};
Person.prototype.name="小紅";
Person.prototype.age=18;
function Student(){};
Student.prototype=Person.prototype;
console.dir(Student);
console.dir(Person);
Student.prototype.age=25;
複製代碼
用這種方式修改 Student 的prototype 不會影響到 Person的prototype
function Person(){};
Person.prototype.name="小紅";
Person.prototype.age=11;
function Student(){};
var F=function(){};
F.prototype=Person.prototype;
Student.prototype=new F();
Student.prototype.constructor=Student;
Student.prototype.age=25;
console.dir(Person)
console.dir(Student)
複製代碼