JS面向對象編程——ES6 中class的繼承用法詳解
本文實例講述了 ES6 中class的繼承用法。分享給大家供大家參考,具體如下:
JS是一種基于對象的語言,要實現(xiàn)面向對象,寫法跟傳統(tǒng)的面向對象有很大的差異。ES6引入了Class語法糖,使得JS的繼承更像面向對象語言的寫法。
此篇博客,分為:基本介紹、Vue使用案例
基本介紹
Class可以通過extends關鍵字實現(xiàn)繼承,這比ES5的通過修改原型鏈實現(xiàn)繼承,要清晰和方便很多;
class Father {
}
class Son extends Father {
}
代碼定義了一個Son 類,該類通過extends關鍵字,繼承了Father類的所有屬性和方法,但是由于沒有部署任何代碼,所以這兩個類完全一樣,等于復制了一個Father類。
class Son extends Father {
constructor (name,age,city) {
super(name,age);//調用父類的constructor(name,age);
this.city = city;
}
toString () {
return this.city+ " " +super.toString();//調用父類的toString()
}
}
constructor方法和toString方法之中,都出現(xiàn)了super關鍵字,他在這里表示父類的構造函數(shù),用來新建父類的this對象;
子類必須在constructor方法中調用super方法,否則新建實例時會報錯,這是因為子類沒有自己的this對象,而是繼承父類的this對象,然后對其進行加工,如果不調用super方法,子類就得不到this對象;
class Father { }
class Son extends Father {
constructor(){ }
}
let s = new Son();
//referenceError : this is not defined
Son繼承了父類Fatherm,但是他的構造函數(shù)沒有調用super方法,這導致新建實例時報錯;
ES5的繼承,實質是先創(chuàng)造子類的實例對象this,然后再將父類的方法添加到this上(Parent.apply(this)),ES6的繼承機制完全不同,實質是先創(chuàng)造父類的實例對象this(所以必須先調用super方法),然后再用子類的構造函數(shù)修改this;
如果子類沒有定義constructor方法,這個方法會默認添加,也就是說,不管有沒有顯式定義,任何一個子類都有constructor方法。
class Son extends Father {
}
//等同于
class Son extends Parent {
constructor(...args) {
super(...args);
}
}
另一個需要注意的是:在子類的構造函數(shù)中,只有調用super之后,才能使用this關鍵字,否則會報錯。這是因為子類實例的構建,是基于對父類實例加工,只有super方法才能返回父類實例;
class Father {
constructor (x,y) {
this.x= x;
this.y = y;
}
}
class Son extends Father {
constructor (x, y, color) {
this.color =color ;//ReferenceError : this is not defined
super(x,y);
this.color = color;//正確
}
}
let s = new Son(25,8,"green");
s instanceof Son //true
s instanceof Father //true
子類的constructor方法沒有調用super之前,就使用this關鍵字,結果報錯,而放在super方法之后就是正確的;
Object.getPrototypeOf()方法用來從子類上獲取父類
Object.getPrototypeOf( Son ) ===Father //true //因此可以用這個方法判斷,一個類是否繼承了另一類
super 關鍵字
super這個關鍵字,既可以當作函數(shù)使用,也可以當作對象使用,
(1)第一情況是:super當作函數(shù)調用時,代表父類的構造函數(shù),ES6要求,子類的構造函數(shù)必須執(zhí)行一個super函數(shù);
class Father { }
class Son extends Father {
constructor () {
super();
}
}
//子類Son的構造函數(shù)之中的super(),代表調用父類的構造函數(shù)。這是必須的,否則 JavaScript 引擎會報錯。
super雖然代表了父類Father的構造函數(shù),但是返回的是子類Son的實例,即super內部的this指向的是Son,因此super()在這里相當于Father.constructor.call(this);
而且作為函數(shù)時,super()只能用在子類的構造函數(shù)中,在其他地方會報錯;
class A {
constructor (){
console.log(new.target.name);
}
}
class B extends A {
constructor () {
super();
}
}
new A()//A
new B()//B
new.target指向當前正在執(zhí)行的函數(shù),在super()執(zhí)行時,他指向的是子類B的構造函數(shù),而不是父類A的構造函數(shù),super()內部的this指向的是B;
(2)第二種情況,super作為對象時,在普通方法中,指向父類的原型對象,在靜態(tài)方法中,指向父類;
class Father{
getName ( ) {
return "MGT360124";
}
}
class Son extends Father {
constructor () {
super();
console.log(super.getName() ) //“MGT360124”
}
}
let s = new Son();
子類Son中的super.p()就是將super當作一個對象使用,這時,super在普通方法中,指向Father.prototype,所以super.getName()就相當于Father.prototype.getName();//"MGT360124",由于super指向父類的原型對象,所以定義在父類實例上的方法或者屬性,是無法通過super調用的;
class Father {
constructor () {
this.p =2
}
}
class Son extends Father {
get m ( ) {
return super.p;
}
getValue ( ) {
return super.a;
}
}
let s = new Son();
s.m
//undefined
p是父類Father實例的屬性,super.p就引用不到它
如果屬性定義在父類的原型對象上,super就可以取到。
class A {}
A.prototype.x = 2;
class B extends A {
constructor() {
super();
console.log(super.x) // 2
}
}
let b = new B();
屬性x是定義在A.prototype上面的,所以super.x可以取到它的值。
ES6 規(guī)定,通過super調用父類的方法時,super會綁定子類的this。
class Father {
constructor () {
this.x =1;//這個this指向的是Father對象的實例
}
print () {
console.log(this.x);
}
}
class Son extends Father {
constructor () {
super();
this.x = 2;//這個this指向的是Son對象的實例
}
m() {
super.print();
}
}
let s = new Son();
s.m();
//2
super.print()雖然調用的是Father.prototype.print(),但是Father.prototype.print()會綁定子類Son的this,導致輸出的是2,而不是1,也就是說,實際上執(zhí)行的是 super.print.call(this)。
如果super作為對象,用在靜態(tài)方法中,這時super將指向父類,而不是父類的原型對象;
class Parent {
static myMethod (msg) {
console.log("static",msg);
}
myMethod (msg) {
console.log("instance" ,msg);
}
}
class Child extends Parent {
static myMethod(msg) {
super.myMethod(msg);
}
myMethod (msg) {
super.myMethod(msg);
}
}
Child.myMethod(1);
//static 1
var child = new Child();
child.myMethod(2);
//instance 2
super在靜態(tài)方法之中指向父類,在普通方法之中指向父類的原型對象。
注意,使用super的時候,必須顯式指定是作為函數(shù)、還是作為對象使用,否則會報錯。
類的prototype屬性和proto屬性
大多數(shù)瀏覽器的ES5實現(xiàn)之中,每一個對象都有proto屬性,指向對應的構造函數(shù)的prototype屬性,class作為構造函數(shù)的語法糖,同時有prototype屬性和proto屬性,因此同時存在兩條繼承鏈;
(1)子類的proto屬性,表示構造函數(shù)的繼承,總是指向父類;
(2)子類prototype屬性的proto屬性,表示方法的繼承,總是指向父類的prototype屬性;
class A{
}
class B{
}
//B的實例繼承A的實例
Object.setPrototypeOf(B.prototype, A.prototype);
//B 的實例繼承A的靜態(tài)屬性
Object.setPrototypeOf(B,A);
const b = new B();
《對象的擴展》一章中Object.setPrototypeOf()方法的實現(xiàn):
Object.setPrototypeOf = function (obj, proto) {
obj.__proto__ = proto;
return obj ;
}
因此
Object.setPrototypeOf( B.prototype , A.prototype ); //等同于 B.prototype.__proto__ = A.prototype ; Object.setPrototypeOf(B, A); //等同于 B.__proto__ = A;
這兩條繼承鏈,可以理解為:作為一個對象,子類B的原型(proto屬性)是父類(A);作為一個構造函數(shù),子類B的原型對象(prototype屬性)是父類的原型對象(prototype)的實例;
extends的繼承目標
extends關鍵字后面可以跟很多類型的值;
class B extends A{
}
只要A有一個prototype屬性的函數(shù),就能被B繼承,由于函數(shù)都有prototype屬性(除了Function.prototype函數(shù)),因此A可以使任意函數(shù),下面三種情況:
(1)子類繼承Object類
class A extends Object {
}
A.__proto__ === Object //true;
A.prototype.__proto__ === Object.prototype //true
這種情況就是 : A就是構造函數(shù)Object的復制,A的實例就是Object的實例
(2)不存在任何繼承
class A {
}
A.__proto__ === Function.prototype //true
A.prototype.__proto__ = Object.prototype //true
這種情況是:A作為一個基類(不存在任何繼承),就是一個普通的函數(shù),所以直接繼承Function.prototype。但是A調用后返回一個空對象(即Object實例),所以A.prototype.proto指向構造函數(shù)(Object)的prototype屬性;
實例的proto屬性
子類實例的proto屬性的proto屬性,指向父類實例的proto屬性。也就是說,子類的原型的原型,是父類的原型。
原生構造函數(shù)的繼承
原生構造函數(shù)是指語言內置的構造函數(shù),通常用來生成數(shù)據(jù)結構。
Boolean() Number() String() Array() Date() Function() RegExp() Error() Object()
extends關鍵字不僅可以用來繼承類,還可以用來繼承原生的構造函數(shù)。因此可以在原生數(shù)據(jù)結構的基礎上,定義自己的數(shù)據(jù)結構。
vue使用
testClass.js
//定義類
class Person{
// 構造
constructor(x,y){
this.x = x;
this.y = y;
}
//定義在類中的方法不需要添加function
toString(){
return (this.x + "的年齡是" +this.y+"歲");
}
}
export {
Person
};
test.vue
<template>
<div>
<p id="testJs"></p>
</div>
</template>
<script>
import {Person} from './testClass.js';
export default {
data() {
return {
}
},
mounted(){
let text=document.getElementById("testJs");
//使用new的方式得到一個實例對象
let person = new Person('張三',12);
text.innerHTML=person.toString();//張三的年齡是12歲
console.log(typeof Person);//function
}
}
</script>
感興趣的朋友可以使用在線HTML/CSS/JavaScript代碼運行工具:http://tools.jb51.net/code/HtmlJsRun測試上述代碼運行效果。
更多關于JavaScript相關內容感興趣的讀者可查看本站專題:《javascript面向對象入門教程》、《JavaScript錯誤與調試技巧總結》、《JavaScript數(shù)據(jù)結構與算法技巧總結》、《JavaScript遍歷算法與技巧總結》及《JavaScript數(shù)學運算用法總結》
希望本文所述對大家JavaScript程序設計有所幫助。
相關文章
JavaScript實現(xiàn)環(huán)繞鼠標旋轉效果
這篇文章主要為大家詳細介紹了JavaScript實現(xiàn)環(huán)繞鼠標旋轉效果,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-01-01
JavaScript實現(xiàn)將UPC轉換成ISBN的方法
這篇文章主要介紹了JavaScript實現(xiàn)將UPC轉換成ISBN的方法,涉及javascript字符串操作的相關技巧,需要的朋友可以參考下2015-05-05
javascript使用數(shù)組的push方法完成快速排序
排序的方法有很多,本節(jié)為大家介紹的是使用數(shù)組的push方法完成快速排序,當然你也可以舉一反三2014-09-09

