繼承
JS中繼承的概念:
- 通過【某種方式】讓一個對象可以訪問到另一個對象中的屬性和方法,我們把這種方式稱之為繼承
並不是所謂的xxx extends yyy
為什么要使用繼承?
- 有些對象會有方法(動作、行為),而這些方法都是函數,如果把這些方法和函數都放在構造函數中聲明就會導致內存的浪費
function Person(){
this.say=function(){
console.log("你好")
}
}
var p1=new Person();
var p2=new Person();
console.log(p1.say === p2.say); //false
繼承的第一種方式:原型鏈繼承1
Person.prototype.say=function(){
console.log("你好")
}
- 缺點:添加1、2個方法無所謂,但是如果方法很多會導致過多的代碼冗余
繼承的第二種方式:原型鏈繼承2
Person.prototype = {
//切記不能忘記
constructor:Person,
say:function(){
console.log("你好");
},
run:function(){
console.log("正在進行百米沖刺");
}
}
- 注意點:
- a、一般情況下,應該先改變原型對象,再創建對象
- b、一般情況下,對於新原型,會添加一個constructor屬性,從而不破壞原有的原型對象的結構
繼承的第三種方式:拷貝繼承(混入繼承:mixin)
- 場景:有時候想使用某個對象中的屬性,但是又不能直接修改它,於是就可以創建一個該對象的拷貝
- 實際運用:
- jquery:$.extend:編寫jquery插件的必經之路
- 基於jquery封裝一個表格控件
- jquery:$.extend:編寫jquery插件的必經之路
var o1={ age:2 };
var o2 = o1;
o2.age=18;
//1、修改了o2對象的age屬性
//2、由於o2對象跟o1對象是同一個對象
//3、所以此時o1對象的age屬性也被修改了
var o3={gender:"男",grade:"初三",group:"第五組",name:"張三"};
var o4={gender:"男",grade:"初三",group:"第五組",name:"李四"};
//上述代碼中,如果使用拷貝繼承對代碼進行優化會非常和諧
//實現拷貝繼承:
//1、已經擁有了o3對象
//2、創建一個o3對象的拷貝(克隆):for...in循環
//3、修改克隆對象,把該對象的name屬性改為"李四"
- 實現1:
var source={name:"李白",age:15}
var target={};
target.name=source.name
target.age=source.age;
-
淺拷貝和深拷貝
- 淺拷貝只是拷貝一層屬性,沒有內部對象
- 深拷貝其實是利用了遞歸的原理,將對象的若干層屬性拷貝出來
var students=[ {name:"",age:""}, {name:"",age:""} ]
-
上面的方式很明顯無法重用,實際代碼編寫過程中,很多時候都會使用拷貝繼承的方式,所以為了重用,可以編寫一個函數把他們封裝起來:
function extend(target,source){
for(key in source){
target[key]=source[key];
}
return target;
}
extend(target,source)
-
由於拷貝繼承在實際開發中使用場景非常多,所以很多庫都對此有了實現
- jquery:$.extend
-
es6中有了 <對象擴展運算符> 仿佛就是專門為了拷貝繼承而生:
- 優點:簡單的令人發指
var source={name:"李白",age:15}
//讓target是一個新對象,同時擁有了name、age屬性
var target={ ...source }
var target2={ ...source,age:18 }
繼承的第四種方式:原型式繼承:(道格拉斯在蝴蝶書中提出來的)
- 場景:
- a、創建一個純潔的對象:對象什么屬性都沒有
- b、創建一個繼承自某個父對象的子對象
var parent={ age:18,gender:"男"}; var student=Object.create(parent); //student.__proto__===parent
- 使用方式:
- 空對象:Object.create(null)
var o1={ say:function(){} } var o2=Object.create(o1);
繼承的第五種方式:借用構造函數實現繼承
- 場景:適用於2種構造函數之間邏輯有相似的情況
- 原理:函數的call、apply調用方式
function Animal(name,age,gender){
this.name=name;
this.age=age;
this.gender=gender;
}
function Person(name,age,gender,say){
this.name=name;
this.age=age;
this.gender=gender;
this.say=function(){
}
}
-
局限性:Animal(父類構造函數)的代碼必須完全適用於Person(子類構造函數)
-
以上代碼用借用構造函數實現
function Animal(name,age){
this.name=name;
this.age=age;
}
function Person(name,age,address){
Animal.call(this,name);
//this.name=name;
//this.age=age;
this.address=address;
}
- 寄生繼承、寄生組合繼承...