很多語言中都有繼承的概念往声,繼承這種東西為什么會出現?
簡而言之戳吝,繼承之所以出現浩销,就是為了減少重復無用功。
好比爸爸有10萬塊听哭,并且還有一項技能:用各種理財手段把10萬變成100萬慢洋,兒子們都想手里有錢并精通理財,那么只需要從爸爸那兒繼承錢和理財的方法欢唾,不用再自己苦哈哈得掙錢學理財了且警。
第一種:原型鏈繼承
原型鏈繼承非常的簡單粗暴,就是讓子類的prototype指向父類的一個實例礁遣,如下:
function Parent(name){
this.name = name;
this.friends = ['a','b','c','d'];
}
function Child() {
}
// 子類的原型是父類的一個實例
Child.prototype = new Parent('father');
let child1 = new Child();
let child2 = new Child();
console.log(child1.friends, child2.friends); // [ 'a', 'b', 'c', 'd' ] [ 'a', 'b', 'c', 'd' ]
可以看到斑芜,孩子輕輕松松就認識了父親的朋友~
如果孩子把friends改掉會怎樣?
// child1.friends = ['a', 'b'];
console.log(child1.friends, child2.friends); // [ 'a', 'b' ] [ 'a', 'b', 'c', 'd' ]
為什么 child1.friends = ['a', 'b']; 對 child2 沒有影響祟霍? 因為這種操作相當于給子類自己加了個自己的屬性而已杏头。
child1.friends.push('e');
console.log(child1.friends, child2.friends); // [ 'a', 'b' ] [ 'a', 'b', 'c', 'd' ]
[ 'a', 'b', 'c', 'd', 'e' ] [ 'a', 'b', 'c', 'd', 'e' ]
這樣寫結果就不一樣了盈包,因為這相當于修改了父親屬性,所有實例都會共享醇王。
這就是原型鏈繼承的一個缺點:一個變了其余都會變呢燥。
原型鏈的另一個缺點就是子類無法向父類構造函數傳遞參數,如果我想用new Children('name')就行不通了寓娩。
基于以上缺點叛氨,出現了借助構造函數的繼承方式。
第二種:借助構造函數的繼承
這種繼承方式其實是在子類的構造函數里調用父類的構造函數棘伴,如下:
function Parent(name){
this.name = name;
this.friends = ['a','b','c'];
this.sayName = function(){
console.log('I am '+this.name);
}
}
function Child(name){
Parent.call(this, name);
}
const child1 = new Child('kid1'); //向父親構造函數傳參
const child2 = new Child('kid2');
console.log(child1, child2);
child1.sayName(); // 用到了父親的方法
child1.friends.push('d');
console.log(child2.friends);// ['a','b','c'] 沒被受影響
借助構造函數寞埠,完美解決的以上兩個問題,但是仔細想想焊夸,sayName方法沒必要為每個實例都帶一個仁连,完全可以放在父類的prototype中。
第三種:組合繼承
其實這種方法就是將上面兩種結合起來阱穗,取長補短饭冬,相得益彰。把需要共享的方法揪阶,屬性用原型鏈繼承昌抠,不需要共享的就用構造函數。
function Parent(name){
this.name = name;
this.friends = ['a','b','c'];
}
Parent.prototype.sayName = function(){
console.log('I am '+this.name);
}
function Child(name){
Parent.call(this, name);
}
Child.prototype = new Parent();
有時我只是僅僅想讓子類把父類的屬性復制粘貼到自己身上鲁僚,也要寫各種構造函數扰魂,各種原型鏈指向么?不需要蕴茴!
第四種:原型繼承
你只需要一行代碼:
const child = Object.create({name: 'parent', friends: ['a', 'b', 'c']});
那么Object.create方法做了什么?
function create(parent){
let o = function(){};
o.prototype = parent;
return new o();
}
就是這么的簡單粗暴姐直!創(chuàng)建->原型指向->new一個返回去倦淀,一套步驟封裝好,用的時候就只要一行代碼就OK了声畏。
引申:
- Object.create() 和 new有什么不同撞叽?
new的過程實際上是:
- new一個空的Object
- 讓this指向剛new出來的object
- 完成構造函數里的賦值操作
- 返回這個Object
以上是我比較粗糙的理解,在網上搜索了一番之后插龄,發(fā)現我的理解還是有些偏差愿棋;
首先,新建的空對象的[[prototype]]指向構造函數的prototype對象均牢;
其次糠雨,綁定this實際上是通過constructor.call(obj)來實現的;
學術一點兒的步驟: - 創(chuàng)建一個新的對象徘跪,這個對象的類型是object甘邀;
- 設置這個新的對象的內部琅攘、可訪問性和[[prototype]]屬性為構造函數(指prototype.construtor所指向的構造函數)中設置的;
- 執(zhí)行構造函數松邪,當this關鍵字被提及的時候坞琴,使用新創(chuàng)建的對象的屬性;
- 返回新創(chuàng)建的對象(除非構造方法中返回的是‘無原型’)逗抑。
可以得知剧辐,new 并不涉及“繼承”,只是單純的按模版(構造函數)創(chuàng)建對象邮府。
- instanceOf 和 isPrototype有什么不同荧关?
都是只要是出現在原型鏈中的構造函數就會判true
console.log(child1 instanceof Child); // true
console.log(child1 instanceof Parent); // true
console.log(child1 instanceof Object); // true
console.log(Parent.prototype.isPrototypeOf(child1)); // true
console.log(Child.prototype.isPrototypeOf(child1)); // true
console.log(Object.prototype.isPrototypeOf(child1)); // true
A.isPrototypeOf(B) 判斷的是A對象是否存在于B對象的原型鏈之中
A instanceof B 判斷的是B.prototype是否存在與A的原型鏈之中
- ES6中Class繼承如何實現?
ES5 的繼承挟纱,實質是先創(chuàng)造子類的實例對象this羞酗,然后再將父類的方法添加到this上面(Parent.apply(this))。
ES6 的繼承機制完全不同紊服,實質是先創(chuàng)造父類的實例對象this(所以必須先調用super方法)檀轨,然后再用子類的構造函數修改this。
class Component{
constructor(componentName){
// console.log(this); // Input {}
this.componentName = componentName;
}
getName(){
console.log('componentName:', this.componentName);
}
}
class Input extends Component{
constructor(name, model){
// Component.prototype.constructor.call(this, name);
super(name);
// 如果不調super報錯:
// ReferenceError:
// Must call super constructor in derived class before accessing 'this' or returning from derived constructor
this.model = model;
}
static myMethod(msg) {
super.myMethod(msg);
}
getModel(){
console.log('model:', this.model);
// super作為對象時欺嗤,在普通方法中参萄,指向父類的原型對象;在靜態(tài)方法中煎饼,指向父類讹挎。
console.log(super.getName); // [Function: getName]
}
}
const input = new Input('input', 'inputModel');
input.getName(); // componentName: input
input.getModel(); // model: inputModel
參考內容
[JavaScript中的new關鍵詞]https://www.w3cplus.com/javascript/javascript-new-keyword.html
[instanceOf 和 isPrototype]http://www.cnblogs.com/ArthurXml/p/6555509.html
[ES6繼承]http://es6.ruanyifeng.com/#docs/class-extends