構(gòu)造函數(shù)的缺點
- js 通過構(gòu)造函數(shù)生成新對象
function OutFun (name,gender){ this.name=name this.gender=gender } var fun = new OutFun('小明','女') console.log(fun.name)
- OutFun函數(shù)是一個構(gòu)造函數(shù),OutFun函數(shù)內(nèi)部定義了name屬性和gender屬性。實例對象上都會有這兩個屬性,
- 屬性定義到實例上,但有一個缺點。就是同一個構(gòu)造函數(shù)的多個實例之間是無法共享屬性。造成資源的浪費
function OutFun (name,gender){ this.name=name this.gender=gender this.innerFun = function (){ console.log('this') } } var fun = new OutFun('小明','女') var fun1 = new OutFun('小明','女') console.log(fun.innerFun===fun1.innerFun)//false
- 可以看出fun和fun1雖然是同一個構(gòu)造函數(shù)但其實是兩個實例。他們實例上都有innerFun 。我們每實例化的時候就創(chuàng)建一個新的innerFun。那怎么共享innerFun?那么就用到下面的原型對象prototype了
原型對象prototype
- js繼承,就是原型對象中的所有屬性和方法,都能被實例對象共享。js中每一個函數(shù)都有prototype,prototype其實就是一個object。
console.log(typeof OutFun.prototype)//object-
對于普通函數(shù),prototype沒有太大作用。但對于構(gòu)造函數(shù),生成實例的時候該屬性會自動成為實例對象的原型。
function OutFun(){ this.age='33' } OutFun.prototype.name='小明' OutFun.prototype.age='44' var fun=new OutFun() console.log(fun.name)//小明 console.log(fun.gender)//undefined console.log(fun.age)//33 我們在訪問name屬性的時候。實例對象上沒有該屬性或方法,它就回去原型對象去找該屬性或方法.若實例對象上有則不去原型對象上找。若原型對象上也沒有該屬性和方法。那么就去原型的原型找,還找不到就去原型的原型的原型找,直到找到Object.prototype還沒有,就返回undefined
注意,要找某個屬性會一級級向上,對性能也是有影響的。這就意味著,如果這個屬性根本不存在,那么我們就得再整條原型鏈里查找一邊。對性能的開銷也越大。原型對象:定義實例對象共享的屬性和方法。
-
原型鏈
-
原型鏈:js中所有對象都有自己的原型對象(prototype)。任何對象都可以充當(dāng)其他對象的原型。原型本身也是對象,所以它也有自己的原型。那么就形成了---原型鏈(對象,對象的原型,對象原型的原型……)
對象的原型最終到Object.prototype(null)。所有對象都可以繼承原型(prototype)上的屬性Object.getPrototypeOf(Object.prototype)//nullObject.getPrototypeOf方法返回參數(shù)對象的原型

image.png
eg:讓構(gòu)造函數(shù)中原型對象指向一個數(shù)組,這就意味著我們這個實例對象就可以使用數(shù)組的方法
function OutFun(){}
OutFun.prototype=new Array()
var fun = new OutFun()
fun.push(1,2,3)
console.log(fun.length)//3
console.log(fun instanceof Array)//true--------------------instanceof 判斷一個對象是否為某個構(gòu)造函數(shù)的實例
instanceof運算符
- 判斷一個對象是否為某個構(gòu)造函數(shù)的實例
- X instanceof S -----------------------X對象,S構(gòu)造函數(shù)。instanceof 檢查右邊S構(gòu)造函數(shù)的原型對象(prototype),是否在左邊對象的原型鏈上。
于S.prototype.isPrototypeOf(X)一樣。
function OutFun(){}
var fun=new OutFun()
console.log(fun instanceof OutFun)//true----表示該fun實例對象是OutFun構(gòu)造函數(shù)的實例
- instanceof檢查整條原型鏈,同一個實例對象,可能會對多個構(gòu)造函數(shù)都返回true。
var arr=new Array()
console.log(arr instanceof Array)//true
console.log(arr instanceof Object)//true
- 任意對象(null除外)都是Object的實例,所以instanceof運算符可以判斷是否為非null
var obj = { name: 'xiaoming' };
var arr = new Array()
console.log(obj instanceof Object)// true
console.log(null instanceof Object)// false
console.log(arr instanceof Object)// true
- instanceof運算符只能用于對象,不適用基本數(shù)據(jù)類型的值
var num = 2342;
console.log(num instanceof Number) // false
var str = '2342';
console.log(str instanceof Number) // false
var isTrue = true
console.log(isTrue instanceof Number) // false
var und = undefined
console.log(und instanceof Number) // false
- 利用instanceof運算符,解決調(diào)用構(gòu)造函數(shù)時,忘了加new命令的問題。
function Fubar (foo, bar) {
if (this instanceof Fubar) {//this是否是構(gòu)造函數(shù)的實例,如果不是則沒有new
this._foo = foo;
this._bar = bar;
} else {
return new Fubar(foo, bar);
}
}
constructor 屬性
- prototype對象有一個constructor屬性,默認(rèn)指向prototype對象所在的構(gòu)造函數(shù)。
function OutFun(){}
//默認(rèn)指向prototype所在的構(gòu)造函數(shù)--可知道我們要想知道這個實例對象是那個構(gòu)造函數(shù)產(chǎn)生的
console.log(OutFun.prototype.constructor===OutFun)//true
//改變OutFun的原型對象,那么constructor的指向也跟著改變了---constructor屬性表示原型對象與構(gòu)造函數(shù)之間的關(guān)聯(lián)關(guān)系,如果修改了原型對象,會同時修改constructor屬性,防止引用的時候出錯。
OutFun.prototype=new Array()
console.log(OutFun.prototype.constructor===OutFun)//false
//手動改變constructor
OutFun.prototype.contructor=OutFun
console.log(OutFun.prototype.constructor===OutFun)//false
- 既然constructor是prototype的屬性,那么表示constructor可以被所有實例對象繼承
- 我們可以從constructor調(diào)用構(gòu)造函數(shù)。相當(dāng)于調(diào)用自身的構(gòu)造函數(shù)。
function OutFun(){}
var fun=new OutFun()
var funCon = new fun.constructor()
console.log(funCon instanceof OutFun)//true
- 通過name屬性,從實例得到構(gòu)造函數(shù)的名稱。
function OutFun(){}
var fun=new OutFun()
console.log(fun.constructor.name)//OutFun
構(gòu)造函數(shù)的繼承
- 子類是整體繼承父類
- 第一步是在子類的構(gòu)造函數(shù)中,調(diào)用父類的構(gòu)造函數(shù)。
- 第二步,是讓子類的原型指向父類的原型,這樣子類就可以繼承父類原型。
//首先我們先定義父類
function Parent(){
this.num = 0
}
Parent.prototype.sum = function(num){
this.num +=num
}
//子構(gòu)造函數(shù)
function Son(){
Parent.call(this)//調(diào)用父類構(gòu)造函數(shù)
}
Son.prototype = Object.create(Parent.prototype)//子類繼承父類的原型
Son.prototype.constructor = Son
var son = new Son()
son.sum(3)
son.sum(5)
console.log(son.num)
- 子繼承父類的單個方法
//定義父類
function Parent(){
this.num = 0
}
Parent.prototype.sum = function(num){
console.log(num,'num')
this.num += num
}
//子構(gòu)造函數(shù)
function Son(){
Parent.call(this)//調(diào)用父類構(gòu)造函數(shù)
}
Son.prototype.sum=function(){
Parent.prototype.sum.call(this,3)
}
var son = new Son()
son.sum()
son.sum()
console.log(son)//6
console.log(son instanceof Son)//false
console.log(son instanceof Parent)//false