繼承的實現(xiàn)方法
- 屬性拷貝
- 淺拷貝
- 深拷貝
- 原型繼承
- 原型式繼承
- 原型鏈繼承
- 組合繼承(深拷貝+優(yōu)化后的原型式繼承)
1. 屬性拷貝
- 直接賦值和屬性拷貝
- 直接賦值
var obj1 = {name:'aa',...}; var obj2 = obj1;
- 屬性拷貝
var obj1 = {name:'aa',...}; var obj2 = {}; for(var i in obj1){ obj2[i] = obj1[i]; }
- 兩種方式的區(qū)別及不足:
- 直接賦值:
- 所有數(shù)據(jù)共享
- 重新設(shè)置某個對象的引用類型值時,其余對象受影響
- 屬性拷貝:
- 引用類型是共享的
-
重新設(shè)置某個對象的引用類型值時,其余的對象不會受影響今布。
屬性拷貝.png
2. 原型式繼承
- 利用動態(tài)特性
- 直接替換原型對象
- 設(shè)置子構(gòu)造函數(shù)的原型對象 = 父構(gòu)造函數(shù)的原型對象
注意點(不足):
- 原型對象父子共享
- 子構(gòu)造函數(shù)創(chuàng)建的對象的構(gòu)造器屬性不正確(和父構(gòu)造函數(shù)原型對象的構(gòu)造屬性相同啄踊。)
-
無法獲取父構(gòu)造函數(shù)實例對象上的屬性和方法(只能獲得父構(gòu)造函數(shù)原型對象上的屬性和方法。)
原型式繼承.png
3. 原型鏈繼承
子構(gòu)造函數(shù).prototype = new 父構(gòu)造函數(shù)();
原型鏈繼承.png
延伸
- 每個對象都是由構(gòu)造函數(shù)創(chuàng)建出來的
- 每個構(gòu)造函數(shù)都有一個與之相關(guān)連的原型對象(prototype)
- 構(gòu)造函數(shù)的原型對象本身也是對象颠毙,因此構(gòu)造函數(shù)的原型對象也有自己的構(gòu)造函數(shù)
- 構(gòu)造函數(shù)的原型對象的構(gòu)造函數(shù)也有相關(guān)聯(lián)的原型對象民傻,而這個原型對象也是一個對象言秸,因此也有構(gòu)造函數(shù)
- 構(gòu)造函數(shù)的原型對象的構(gòu)造函數(shù)的原型對象也有構(gòu)造函數(shù)..也有原型對象..也是對象...構(gòu)造函數(shù)....
以上 會形成一種鏈?zhǔn)降脑L問結(jié)構(gòu),這種結(jié)構(gòu)稱為原型鏈
原型鏈的終點是Object.prototype 扳缕,Object.prototype的原型對象是null(Object.prototype.proto == null)所有對象原型鏈的終點都是Object.prototype
3.1 原型鏈中屬性的搜索規(guī)則:
- 對象在訪問(讀取|寫)屬性的時候,先遍歷當(dāng)前的對象查找自身有沒有指定的屬性
- 如果有該屬性慌闭,那么就直接使用
- 如果沒有該屬性,那么就遍歷當(dāng)前對象的原型對象躯舔,在原型對象身上查詢指定的屬性
- 找到那么就直接使用
- 沒有找到驴剔,那么就繼續(xù)向上查詢
- 重復(fù)這個過程,直到Object.prototype,如果找到那么就使用粥庄,如果沒有找到那么就返回undefined或者是報錯
注意點:
- 要注意設(shè)置原型鏈繼承的位置:先完成原型鏈繼承丧失,再給原型對象添加成員
子構(gòu)造函數(shù).prototype = new 父構(gòu)造函數(shù)();
- 完成原型鏈繼承之后最好修正構(gòu)造器屬性
子構(gòu)造函數(shù).prototype.contructor = 父構(gòu)造函數(shù)名;
- 完成原型鏈繼承之后,不能使用字面量的方式來替換原型對象(繼承會失效)
不足:
- 父構(gòu)造函數(shù)實例成員(屬性+方法)會成為子購造函數(shù)創(chuàng)建對象的原型成員惜互,有共享的問題
- 無法對父構(gòu)造函數(shù)傳遞
4. 組合繼承(優(yōu)化前)
- 借用構(gòu)造函數(shù) + 原型式繼承
//父構(gòu)造函數(shù)
function Person(name,age){
this.name = name;
this.age = age;
}
Person.prototype.logName=function(){
console.log(this.name);
}
//子構(gòu)造函數(shù)
function Children(name,age){
//構(gòu)造函數(shù)內(nèi)部會默認(rèn)創(chuàng)建空對象并賦值給this
Person.call(this,name,age); //相當(dāng)于this.name = name;this.age = age;(借用構(gòu)造函數(shù))
}
//原型式繼承
Children.prototype = Person.prototype;
//創(chuàng)建實例對象
var child = new Children("小明",18);
不足:原型對象共享問題布讹。
5. 深拷貝
- 淺拷貝:for...in循環(huán)直接拷貝(引用類型屬性共享問題)
- 深拷貝:
- 使用for...in遍歷被拷貝的對象。
- 如果是值類型训堆,直接賦值
- 如果是引用類型描验,新創(chuàng)建一個對象,再次遍歷...重復(fù)此過程
function deepCopy(obj1,obj2){
for(var i in obj1){
//只拷貝對象自身的屬性和方法坑鱼,不拷貝其原型對象中的屬性和方法
if(obj1.hasOwnProperty(i)){
//當(dāng)為引用類型時膘流,繼續(xù)遍歷-拷貝
if(typeof obj1[i] == 'object'){
//區(qū)分?jǐn)?shù)組類型和object類型
obj2[i] = Array.isArray() ? [] : {};
//遞歸
deepCopy(obj1[i],obj2[i]);
} else {
obj2[i] = obj1[i];
}
}
}
}
6. 通過深拷貝實現(xiàn)繼承(組合繼承優(yōu)化后)
//父構(gòu)造函數(shù)
function Person(name,age){
this.name = name;
this.age = age;
}
Person.prototype.logName=function(){
console.log(this.name);
}
//子構(gòu)造函數(shù)
function Children(name,age){
//構(gòu)造函數(shù)內(nèi)部會默認(rèn)創(chuàng)建空對象并賦值給this
Person.call(this,name,age); //相當(dāng)于this.name = name;this.age = age;(借用構(gòu)造函數(shù))
}
//深拷貝
deepCopy(Person.prototype,Children.prototype);
//創(chuàng)建實例對象
var child = new Children("小明",18);
Object方法
- Object.create()方法
- 作用:創(chuàng)建對象并設(shè)置為原型對象
- 用法:
var o = Object.create(obj);
//相當(dāng)于:創(chuàng)建一個空對象o,并且設(shè)置這個對象的原型對象為obj
- 注意兼容性處理鲁沥。
var o;
if(typeof Object.create == "function") {
o = Object.create(obj);
}else {
Object.create = function () {
function F() {};
F.prototype = obj;
o = new F();
}
}
- Object.assign()方法
- 作用:拷貝屬性呼股,一次性拷貝多個對象的屬性。
- 用法:
Object.assign(目標(biāo)對象画恰,要拷貝屬性的對象1彭谁,要拷貝屬性的對象2,要拷貝屬性的對象3)
- 注意點:
- 新特性有兼容性問題
- 默認(rèn)原型(.proto)成員不能拷貝
3.call和apply函數(shù)
- 來源:所有的對象方法都擁有這兩個函數(shù)(方法),這兩個方法寫在Function.prototype上面
- 作用:借用其他對象的方法
- 用法:
對象1.方法.call(借用者對象,參數(shù)1允扇,參數(shù)2缠局,參數(shù)3...) //方法內(nèi)部的this會綁定給call的第一個參數(shù)
對象1.方法.apply(借用者對象,[參數(shù)1则奥,參數(shù)2,參數(shù)3...])
- 兩個函數(shù)的區(qū)別(傳遞參數(shù)):
- 傳遞參數(shù)不同
- 期望的形參長度不同(函數(shù).length 形參的個數(shù))
- call:length 1 期望傳遞一個參數(shù)
- apply:length 2 期望傳遞兩個參數(shù)