原型對象
Javascript稻轨,萬事萬物皆對象;
但是呢雕凹,對象也是有區(qū)別滴殴俱,它分為普通對象和函數(shù)對象;
也就是object和function枚抵;
什么是原型對象线欲?
在javascript中,每當定義一個對象的時候汽摹,對象都會包含一些自帶的屬性李丰,
其中,每一個函數(shù)對象都有一個屬性:prototype屬性逼泣,這個屬性趴泌,就是指向函數(shù)的原型對象。
在JS里面拉庶,每一個對象踱讨,都有proto屬性,但是只有函數(shù)對象砍的,才有prototype屬性;
原型對象和普通對象莺治,沒啥區(qū)別廓鞠,只要記住原型對象就是:
構造函數(shù).prototype={}
也就是說,原型對象(person.prototype)就是構造函數(shù)(person)的一個實例
__ proto__
JS在創(chuàng)建對象的時候谣旁,都有一個叫做proto的內置屬性床佳,用于指向創(chuàng)建他的構造函數(shù)的原型對象;
通過這個連接圖榄审,我們可以看到
Person.prototype.constructor = Person砌们; //構造函數(shù)的指針指向他自己
person1._proto_ == Person.prototype; //實例對象的_proto_就是構造函數(shù)的原型對象
person1.constructor == Person; //實例對象的指針搁进,是指向構造函數(shù)浪感;
那么,這個連接鏈饼问,它的重點是:他是存在于 “實例和構造函數(shù)的原型” 之間影兽,而不是存在于 “實例和構造函數(shù)” 之間;
在javascript里面莱革,創(chuàng)建對象有兩種方法:
var obj = {};字面量
var obj = new Object();和上面完全相同
那么obj就是構造函數(shù)的一個實例峻堰,所以
obj.constructor ===Object;
obj._proto_ ===Object.prototype;
也就是說讹开,我們實例化的obj變量(實例化對象),他的指針捐名,指向object這個構造函數(shù)
他的我_proto_指向Object的原型對象旦万;
我們用來創(chuàng)建對象的構造函數(shù)又稱之為構造器;
面向對象繼承
第一種方法
/*第一個方法镶蹋,構造函數(shù)的綁定成艘,也是最簡單的方法*/
/*使用call或者apply方法,將父對象的構造函數(shù)綁定在子對象上梅忌, 那么就在子對象加一句話就可以了*/
function Xs(){
this.type = "學生";
}
function Dxs(name,age){
Xs.apply(this,arguments);
this.name = name;
this.age = age;
}
var mmm = new Dxs("某某某",18);
console.log(mmm.type);
第二種方法(常用)
function Xs(){
this.type = "學生";
}
function Dxs(name,age){
this.name = name;
this.age = age;
}
Dxs.prototype = new Xs();
//將我們的Dsx的prototype對象指向Xs的實例狰腌,它會刪除prototype對象原本的值,并且賦予一個新值(就是替換)
Dxs.prototype.constructor = Dxs();
console.log(Dxs.prototype.constructor==Dxs)牧氮;
//任何一個prototype對象琼腔,都有一個constructor屬性,這個屬性指向他的構造函數(shù)踱葛,前面提到過丹莲,這是一個指針,把prototype指向Dxs尸诽;
//如果沒有Dxs.prototype = new Xs();這一行代碼甥材,Dsx.prototype.constructor指向誰?指向大學生
//但是性含,我們加入Dxs.prototype = new Xs();這一行后洲赵,Dxs的prototype是不是指向Xs,那么這個constructor指針是不是指向Xs
//也就是說商蕴,我們直接使用prototype繼承叠萍,會導致繼承鏈非常混亂绪商,那么我們必須手動糾正回來
//必須必須必須注意苛谷,在編程的過程中,務必遵循格郁,如果替換了prototype對象腹殿,
//那么,下一步必須給這個prototype對象添加上constructor屬性例书,并指回原本自己的構造函數(shù)锣尉;
var mmm = new Dxs("某某某",18);
console.log(mmm.type);
第三種方法
function Xs(){}
Xs.prototype.type = "學生";
function Dxs(name,age){
this.name = name;
this.age = age;
}
Dxs.prototype = Xs.prototype;
Dxs.prototype.constructor = Dxs;
Dxs.prototype.type = "大學生";
var mmm = new Dxs("某某某",18);
console.log(Xs.prototype.type);
/*優(yōu)點:效率比較高,不用創(chuàng)建Xs的實例對象决采,比較省內存*/
/*缺點:Dxs.prototype和Xs的prototype現(xiàn)在指向了同一個對象了*/
/*Dxs.prototype.type = '大學生';
console.log(Xs.prototype.type);*/
//所以悟耘,這個方法是有問題的,問題出在哪兒织狐?
//Dxs.prototype.constructor = Dxs;
//這一行代碼暂幼,把Xs.prototype對象的constructor屬性也給改掉了
第四種方法:利用空對象最為中介(常用)
function Xs(){}
Xs.prototype.type = "學生";
function Dxs(name,age){
this.name = name;
this.age = age;
}
//Dxs.prototype = new Kong();
//Dxs.prototype.constructor = Dxs;
function extend(Child,Parent){
var Kong = function(){};
Kong.prototype = Xs.prototype;
Child.prototype = new Kong();
Child.prototype.constructor = Child;
Child.end = Child.prototype;
//意思筏勒,為子對象設置一個end屬性,這個屬性指向父對象的prototype屬性旺嬉,end(自定義屬性)是“向上”管行、“上一層”的意思
//這個就相當于給子對象開了一條路,可以直接調用父對象方法邪媳,在這里只是為了實現(xiàn)繼承的完整性捐顷,屬于備用性質,
//因為子對象繼承的是空對象雨效,所以要通過這行代碼才能調用得到父對象迅涮;
}
extend(Dxs,Xs);
var mmm = new Dxs("某某某",18);
console.log(mmm.type);
第五種方法:克隆
function extend(Child,Parent){
var p = Parent.prototype;
var c = Child.prototype;
for(var i in p){
c[i]=p[i];
}
c.uber = p;
}
function Xs(){}
Xs.prototype.type = '學生';
function Dxs(name,age){
this.name = name;
this.age = age;
}
extend(Dxs,Xs);
var wzq = new Dxs('吳澤權',18)
alert(wzq.type);
原型鏈
function Person(name,age){
this.name = name;
this.age = age;
}
var p1 = new Person('吳澤權',19);
//p1._proto_是什么東西?
//是不是Person.prototype徽龟;
/*因為實例對象._proto_等于構造函數(shù)的原型對象*/
/*也就是說叮姑,p1._proto_等于Person.prototype*/
/*__proto__每一邊有兩個下劃線*/
console.log(p1.__proto__ === Person.prototype)//true
//Person.__proto__又是什么?
//前面說過据悔,實例對象的__proto__等于構造函數(shù)的prototype
//因為Person是通過function來寫出來的
//也就是說传透,Person是通過new Function來構造出來的實例對象;
//Person.__proto__===Function.prototype;
console.log(Person.__proto__==Function.prototype);
//Person.prototype.__proto__又是什么呢极颓?
//前面說過朱盐,對象分為函數(shù)對象和普通對象,prototype他不是函數(shù)對象菠隆,他只是函數(shù)對象衍生的普通對象
//普通對象就是由Object構造的
//那么Person.prototype.__proto__就是等于Object.prototype
console.log(Person.prototype.__proto__==Object.prototype)
//Object.__proto__又是什么兵琳?
//和第二題一樣,不論是Object還是Function骇径,都是通過函數(shù)構造的
console.log(Object.__proto__==Function.prototype);
//Object.prototype.__proto__又是什么呢闰围?
//記住,Object對象的原型對象既峡,已經(jīng)是處于原型鏈的頂端了,頂端沒有東西了啊碧查,所以是null
console.log(Object.prototype.__proto__==null)
前面說過运敢,所有的函數(shù)對象的__proto__(原型)都指向Function.prototype
在JS里面,有十多個構造器:例如Number忠售、Object传惠、Array、Date;
他們這些構造器的原型都是Function.prototype
而Math稻扬、JSON是以對象的形式存在卦方,不需要new就可以聲明,所以他們的原型(__proto__)就是Object.prototy泰佳;
也就是說:
所有的構造器都來自Function.prototype盼砍,甚至包括根構造器Object以及Function自身尘吗,也就是說,所有的構造器都繼承了Function.prototype的屬性以及方法浇坐,比如說length睬捶、call、apply
也就是說近刘,構造器的__proto__就是Function.prototype擒贸,那么Function.prototype的原型(__proto__)又是誰呢?
在Javascript里面觉渴,函數(shù)是一等公民
也就是說介劫,所有的構造器也都是一個普通的JS對象,同時它也繼承了Object.prototype上面的所有方法:toString,valueOf
Object.protot又是誰呢案淋?
已經(jīng)到頂了座韵,就是null
之前學習Js,可以使用很多內置方法
parseInt()哎迄、toString()回右,都可以使用,為什么可以使用呢漱挚?
因為翔烁,當我們創(chuàng)建一個函數(shù)的時候,
var peoson = new Object()旨涝;
person是Object的實例蹬屹,所以繼承了Object.prototype的所有方法
例如,當我們創(chuàng)建一個數(shù)組的時候白华,是不是數(shù)組就繼承了Array.prototype上面的所有方法慨默,所以我們之所以能用那么多的方法,就是因為繼承
console.log(Date.__proto__==Function.prototype);
console.log(Math.__proto__==Object.prototype);
console.log(Function.prototype.__proto__==Object.prototype);
console.log(Object.prototype)
console.log(Array.prototype)
var num = [1,2,3,]
console.log(num.hasOwnProperty())
/*雖然Array.prototype沒有hasOwnProperty()這個方法弧腥,但是他有原型厦取,他的原型是不是__proto__*/
//Array()是繼承自Object,那么Array.__proto__==Object.prototype
/*所以,當你使用num.hasOwnProperty()的時候,JS會先找到他的構造函數(shù)(Array)的原型對象Array.prototype,看下他有沒有hasOwnProperty()
沒有的話,一路按照原型對象找下去,下一步就是找Arry.prototype的原型對象,那么就是找Array.prototype.__proto__*/
總結:
原型和原型鏈是JS實現(xiàn)繼承的一種模型
原型鏈的形成管搪,真正是靠的__proto__,而不是prototype虾攻;