一、類(lèi)與實(shí)例
1. 類(lèi)的聲明
- 傳統(tǒng)function類(lèi)的聲明
function Animal() {
this.name = 'name';
}
- ES6中的class聲明
class Animal2 {
//構(gòu)造函數(shù)
constructor (){
this.name = name;
}
}
1.1 ES6 class與ES5 function的區(qū)別:
Class的特點(diǎn):
Class在語(yǔ)法上更加貼合面向?qū)ο蟮膶?xiě)法
Class實(shí)現(xiàn)繼承更加易讀,更加容易理解
更易于寫(xiě)Java等后端語(yǔ)言的使用
本質(zhì)還是是語(yǔ)法糖,使用prototype
- 重復(fù)定義
- function會(huì)覆蓋之前定義的方法
- class會(huì)報(bào)錯(cuò)
- 構(gòu)造器 constructor
- 在function定義的構(gòu)造函數(shù)中,其prototype.constructor屬性指向構(gòu)造器自身
- 在class定義的類(lèi)中,constructor也相當(dāng)于定義在prototype屬性上
- 原型或者類(lèi)中方法的枚舉
-
class中定義的方法不可用Object.keys(Point.prototype)枚舉到 -
function構(gòu)造器原型方法可被Object.keys(Point.prototype)枚舉到,除了constructor - 所有原型方法屬性都可用
Object.getOwnPropertyNames(Point.prototype)訪(fǎng)問(wèn)到 - 不管是
class還是function,constructor屬性默認(rèn)不可枚舉
2. 生成實(shí)例
如何通過(guò)類(lèi)實(shí)例類(lèi)的對(duì)象
console.log(new Animal(), new Animal2());
注:如果構(gòu)造函數(shù)沒(méi)有聲明的話(huà),new函數(shù)名后面的括號(hào)可以省略
二、類(lèi)與繼承
2.1 如何實(shí)現(xiàn)繼承
2.2 繼承的幾種方式
- 借助構(gòu)造函數(shù)實(shí)現(xiàn)繼承
原理:在子類(lèi)的函數(shù)體內(nèi)執(zhí)行父類(lèi),用call和apply都可以改變函數(shù)運(yùn)行的上下文,導(dǎo)致父類(lèi)執(zhí)行的實(shí)例都會(huì)掛載到子類(lèi)上面。
function Parent1 () {
this.name = 'parent1';
}
Parent1.prototype.say = function () {};/*new Child1().say() 報(bào)錯(cuò)*/
function Child1 () {
Parent1.call(this);/*apply也可以改變函數(shù)運(yùn)行的上下文*/
this.type = 'child1';
}
控制臺(tái)輸出:
console.log(new Child1);
缺點(diǎn):構(gòu)造函數(shù)除了函數(shù)體里面的內(nèi)容,還可能有原型鏈上的,但是這種方式中構(gòu)造函數(shù)原型鏈上的東西并沒(méi)有被繼承。
擴(kuò)展學(xué)習(xí):javascript中apply、call和bind方法的區(qū)別
- 借助原型鏈實(shí)現(xiàn)繼承(彌補(bǔ)構(gòu)造函數(shù)繼承不足)
function Parent2() {
this.name = 'parent2';
this.play = [1, 2, 3];
}
function Child2() {
this.type = 'child2';
}
Child2.prototype = new Parent2();
//缺點(diǎn)
console.log(new Child2());
var o1 = new Child2();
var o2 = new Child2();
console.log(o1.play, o2.play);//都為1,2,3
o1.play.push(4);//將o1重新復(fù)制
console.log(o1.play, o2.play);//都為1,2,3,4
實(shí)現(xiàn)原理:低級(jí)構(gòu)造函數(shù)的原型是高級(jí)構(gòu)造函數(shù)的實(shí)例,new Child2().__proto__就是Parent2父類(lèi)的一個(gè)實(shí)例對(duì)象。即new Child2().__proto__ === Parent2.prototype為true。new Child2().__proto__.name的值為parent2。
缺點(diǎn):因?yàn)樵玩溨械脑褪?strong>共用的,所以?xún)蓚€(gè)對(duì)象不隔離。改變一個(gè)影響另一個(gè)。o1.__proto__ = o2.__proto__。
- 組合繼承方式(構(gòu)造函數(shù)+原型鏈)
function Parent3() {
this.name = 'Parent3';
this.play = [1, 2, 3];
}
function Child3() {
Parent3.call(this);
this.type = 'Child3';
}
Child3.prototype = new Parent3();
//檢驗(yàn)
console.log(new Child3());
var o1 = new Child3();
var o2 = new Child3();
o1.play.push(4);
console.log(o1.play, o2.play);//這時(shí)候結(jié)果就不一樣了 分別是 [1,2,3,4] [1,2,3]
缺點(diǎn):實(shí)例化子類(lèi)的時(shí)候父類(lèi)構(gòu)造函數(shù)執(zhí)行兩次。是沒(méi)有必要的。
- 組合繼承優(yōu)化一
function Parent4() {
this.name = 'Parent4';
this.play = [1, 2, 3];
}
function Child4() {
Parent4.call(this);
this.type = 'Child4';
}
Child4.prototype = Parent4.prototype;
console.log(new Child4());
var o1 = new Child4();
var o2 = new Child4();
o1.play.push(4);
console.log(o1.play, o2.play);
console.log(o1 instanceof Child4, s5 instanceof Parent4);//true, true
console.log(o1.constructor);
怎么區(qū)分,是否是直接實(shí)例化的?
直接拿的父類(lèi)實(shí)例
- 組合繼承優(yōu)化二
function Parent5() {
this.name = 'Parent5';
this.play = [1, 2, 3];
}
function Child5() {
Parent5.call(this);
this.type = 'Child5';
}
Child5.prototype = Object.create(Parent5.prototype);
創(chuàng)建中間對(duì)象,完美~
2.3 Class和普通構(gòu)造函數(shù)實(shí)現(xiàn)繼承對(duì)比
Class實(shí)現(xiàn)(ES6)
class Animal {
constructor(name) {
this.name = name
}
eat() {
console.log(`${this.name} eat`)
}
}
class Dog extends Animal {
constructor(name) {
super(name)
this.name = name
}
say() {
console.log(`${this.name} say`)
}
}
const dog = new Dog('husky')
dog.say()
dog.eat()
普通構(gòu)造函數(shù)實(shí)現(xiàn)
function Animal() {
this.eat = function () {
alert('Animal eat')
}
}
function Dog() {
this.bark = function () {
alert('Dog bark')
}
}
Dog.prototype = new Animal()
var husky = new Dog()
husky.bark()
husky.eat()