ES6 之 Class 的基本语法和继承

2019-10-11 admin

Class 类基本用法

Class 类完全可以看作构造函数的另一种写法,类的数据类型其实就是函数,类本身也就是其实例的构造函数。使用的时候也是使用 new 命令。

class Person {
  constructor(name,age) {
    this.name = name;
    this.age = age;
  }

  getName(){
    console.log(`My name is ${this.name}!`)
  }

  static sayHi() {
    console.log('Hi');
  }
}

let p = new Person();

typeof Person          // "function"
p instanceof Person    // true

constructor 方法

constructor 方法就是类的构造方法,this 关键字代表实例对象。其对应的也就是 ES5 的构造函数 Person

constructor 方法是类的默认方法,通过 new 命令生成对象实例时,会自动调用该方法。一个类必须有 constructor 方法,如果没有显式定义,会默认添加一个空的 constructor 方法。

class Person {}

Person.prototype    //  {construtor:f}

constructor 方法默认返回实例对象,也完全可以 return 另外一个对象。

class Foo {
  constructor(){
    return Object.create(null);
  } 
}

new Foo() instanceof Foo      // false

类必须使用 new 调用,也就是 constructor 方法只能通过 new 命令执行,否则会报错。

class Foo {
  constructor(){} 
}

Foo()   // TypeError: Class constructor Foo cannot be invoked without 'new'

Class 的自定义方法

Class 类依旧存在 prototype 属性,且类的所有方法都定义在 prototype 属性上面。

class Person {
  constructor() {}
  aaa(){}
  bbb(){}
}

Object.getOwnPropertyNames(Person.prototype)   // ["constructor", "aaa", "bbb"]

prototype 对象的 constructor 属性,也是直接指向类本身。

Person.prototype.constructor === Person  // true
p.constructor === Person                 // true

类的新方法可通过 Object.assignprototype 一次性添加多个。

class Person {
  constructor() {}
}

Object.assign(Person.prototype, {
  aaa(){},
  bbb(){}
})

注意,定义类的时候,前面不需要加上 function 关键字,也不需要逗号分隔。

与 ES5 构造函数的不同的是,Class 内部所有定义的方法,都是不可枚举的。

class Person {
  constructor() {}
  aaa(){}
}

Object.keys(Person.prototype)                  // []
Object.getOwnPropertyNames(Person.prototype)   // ["constructor", "aaa"]

而 ES5 的构造函数的 prototype 原型定义的方法是可枚举的。

let Person = function(){};
Person.prototype.aaa = function(){};

Object.keys(Person.prototype)                  // ["aaa"]
Object.getOwnPropertyNames(Person.prototype)   // ["constructor", "aaa"]

Class 类的属性名,可以采用表达式。

let methodName = 'getName';

class Person {
  constructor() {}
  [methodName](){}
}

Object.getOwnPropertyNames(Person.prototype)   // ["constructor", "getName"]

取值函数 getter 和存值函数 setter

与 ES5 一样,在 Class 内部可以使用 getset 关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为。

class Person {
  constructor() {
    this.name = 'dora';
  }
  get author() {
    return this.name;
  }
  set author(value) {
    this.name = this.name + value;
    console.log(this.name);
  }
}

let p = new Person();
p.author          //  dora
p.author = 666;   // dora666

且其中 author 属性定义在 Person.prototype 上,但 getset 函数是设置在 author 属性描述对象 Descriptor 上的。

Object.getOwnPropertyNames(Person.prototype)   // ["constructor", "author"]

Object.getOwnPropertyDescriptor(Person.prototype,'author')
// { get: ƒ author()(),
//   set: ƒ author()(value),
//   ...
// }

Class 的 static 静态方法

类相当于实例的原型,所有在类中定义的方法,都会被实例继承。但如果在一个方法前,加上 static 关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。

class Person {
  static sayHi() {
    console.log('Hi');
  }
}

Person.sayHi()      // "Hi"

let p = new Person();
p.sayHi()           // TypeError: p.sayHi is not a function

如果静态方法包含 this 关键字,这个 this 指的是类,而不是实例。静态方法可以与非静态方法重名。

class Person {
  static sayHi() {
    this.hi();
  }

  static hi(){
    console.log('hello')
  }

  hi(){
    console.log('world')
  }
}

Person.sayHi()      // "hello"

实例属性的另一种写法

实例属性除了定义在 constructor() 方法里面的 this 上面,也可以定义在类的最顶层。此时定义的时候,属性前面不需要加上 this。而在类内部其它地方调用的时候,需要加上 this

class Person {
  name = 'dora';
  getName() {
    return this.name;
  }
}

let p = new Person();
p.name           // "dora"
Object.keys(p)   // ["name"]

这种写法的好处是,所有实例对象自身的属性都定义在类的头部,看上去比较整齐,写法简洁,一眼就能看出这个类有哪些实例属性。

Class 的继承

Class 子类可以通过 extends 关键字实现继承。

class Person {
  constructor() {}
  sayHi() {
    return 'Hi';
  }
}

class Teacher extends Person {
  constructor() {
    super();
  }
}

let t = new Teacher();
t.sayHi();   // "Hi"

子类的 constructor

子类必须在 constructor 方法中调用 super() 方法,否则新建实例时会报错。

如果子类没有定义 constructor 方法,这个方法会被默认添加,且子类默认添加的 constructor 方法都会默认执行 super() 方法。

class Teacher extends Person {
}

let t = new Teacher();
t.sayHi();   // "Hi"

等同于

class Teacher extends Person {
  constructor(...args) {
    super(...args);
  }
}

super 关键字

super 这个关键字,既可以当作函数使用,也可以当作对象使用。用法完全不同。

super() 方法

super 作为函数调用时,代表父类的构造函数。子类的构造函数必须执行一次 super() 方法。

因为 ES6 的继承机制与 ES5 构造函数不同,ES6 的子类实例对象 this 必须先通过父类的构造函数创建,得到与父类同样的实例属性和方法后再添加子类自己的实例属性和方法。因此如果不调用 super() 方法,子类就得不到 this 对象。

super 虽然代表了父类的构造函数,但返回的是子类的实例,即通过super 执行父类构造函数时,this 指的都是子类的实例。也就是 super() 相当于 Person.call(this)

class A {
  constructor() {
    console.log(this.constructor.name)
  }
}

class B extends A {
  constructor() {
    super();
  }
}

new A()       // A
new B()       // B

作为函数时,super() 只能在子类的构造函数之中,用在其他地方就会报错。

super 对象

在普通方法中指向父类的 prototype 原型

super 作为对象时,在普通方法中,指向父类的 prototype 原型,因此不在原型 prototype 上的属性和方法不可以通过 super 调用。

class A {
  constructor() {
    this.a = 3;
  }
  p() {return 2;}
}
A.prototype.m = 6;

class B extends A {
  constructor() {
    super();
    console.log(super.a);    // undefined
    console.log(super.p());  // 2
    console.log(super.m);    // 6
  }
}

new B();

在子类普通方法中通过 super 调用父类方法时,方法内部的 this 指向当前的子类实例。

class A {
  constructor() {
    this.x = 'a';
  }
  aX() {
    console.log(this.x);
  }
}

class B extends A {
  constructor() {
    super();
    this.x = 'b';
  }
  bX() {
    super.aX();
  }
}

(new B()).bX()    // 'b'

在静态方法中,指向父类

class A {
 static m(msg) {
   console.log('static', msg);
 }
 m(msg) {
   console.log('instance', msg);
 }
}

class B extends A {
  static m(msg) {
    super.m(msg);
  }
  m(msg) {
    super.m(msg);
  }
}

B.m(1);          // "static" 1
(new B()).m(2)   // "instance" 2

在子类静态方法中通过 super 调用父类方法时,方法内部的 this 指向当前的子类,而不是子类的实例。

任意对象的 super

由于对象总是继承其它对象的,所以可以在任意一个对象中,使用 super 关键字,指向的是该对象的构造函数的 prototype 原型。

let obj = {
  m() {
    return super.constructor.name;
  }
};
obj.m();    // Object

注意,使用 super 的时候,必须显式的指定是作为函数还是作为对象使用,否则会报错。

class B extends A {
  m() {
    console.log(super);
  }
}
// SyntaxError: 'super' keyword unexpected here

静态方法的继承

父类的静态方法,可以被子类继承。

class Person {
  static sayHi() {
    return 'hello';
  }
}

class Teacher extends Person {
}

Teacher.sayHi()      // "hello"

在子类的 static 内部,可以从 super 对象上调用父类的静态方法。

class Teacher extends Person {
  static sayHi() {
    super.sayHi();
  }
}

Teacher.sayHi()      // "hello"

new.target 属性

Class 内部调用 new.target,返回当前 Class。且子类继承父类时,new.target 会返回子类。因此利用这个特点,可以写出不能独立使用必须继承后才能使用的类。

class Shape {
  constructor() {
    if(new.target === Shape) {
      throw new Error('本类不能实例化');
    }
  }
}

class Circle extends Shape {
  constructor(radius) {
    super();
    console.log('ok');
  }
}

let s = new Shape();      // 报错
let cir = new Circle(6);  // 'ok'

[转载]原文链接:https://segmentfault.com/a/1190000020652616

本站文章除注明转载外,均为本站原创或编译。欢迎任何形式的转载,但请务必注明出处。

转载请注明:文章转载自 JavaScript中文网 [https://www.javascriptcn.com]

本文地址:https://www.javascriptcn.com/read-77156.html

文章标题:ES6 之 Class 的基本语法和继承

相关文章
一些前端学习中好的书籍,整理
一、Javascript方面的书籍: 1 JavaScript权威指南(第6版):号称javascript圣经,前端必备;前端程序员学习核心JavaScript语言和由Web浏览器定义的JavaScript API的指南和综合参考手册; 2...
2015-11-12
js性能优化 如何更快速加载你的JavaScript页面
确保代码尽量简洁 不要什么都依赖JavaScript。不要编写重复性的脚本。要把JavaScript当作糖果工具,只是起到美化作用。别给你的网站添加大量的JavaScript代码。只有必要的时候用一下。只有确实能改善用户体验的时候用一下。 ...
2015-11-12
10个强大的纯CSS3动画案例分享
我们的网页外观主要由CSS控制,编写CSS代码可以任意改变我们的网页布局以及网页内容的样式。CSS3的出现,更是可以让网页增添了不少动画元素,让我们的网页变得更加生动有趣,并且更易于交互。本文分享了10个非常炫酷的CSS3动画案例,希望大家...
2015-11-16
v-charts | 饿了么团队开源的基于 Vue 和 ECharts 的图表工具
在使用echarts生成图表时,经常需要做繁琐的数据类型转化、修改复杂的配置项,v-charts的出现正是为了解决这个 痛点。基于Vue2.0和echarts封装的v-charts图表组件,只需要统一提供一种对前后端都友好的数据格式 设置简...
2018-05-24
JavaScript实现PC手机端和嵌入式滑动拼图验证码三种效果
PC和手机端网站滑动拼图验证码效果源码,同时包涵了弹出式Demo,使用ajax形式提交二次验证码所需的验证结果值,嵌入式Demo,使用表单形式提交二次验证所需的验证结果值,移动端手动实现弹出式Demo三种效果 首先要确认前端使用页面,比如...
2017-03-17
Vue获取DOM元素样式和样式更改示例
在 vue 中用 document 获取 dom 节点进行节点样式更改的时候有可能会出现 ‘style’ is not definde的错误,这时候可以在 mounted 里用 $refs 来获取样式,并进行更改: <template...
2017-03-13
从2014年的发展来展望JS的未来将会如何
<font face="寰�杞�闆呴粦, Arial, sans-serif ">2014骞达紝杞�浠惰�屼笟鍙戝睍杩呴€燂紝鍚勭�嶈��瑷€灞傚嚭涓嶇┓锛屼互婊¤冻鐢ㄦ埛涓嶆柇鍙樺寲鐨勯渶姹傘€傝繖浜涜��...
2015-11-12
12个你未必知道的CSS小知识
虽然CSS并不是一种很复杂的技术,但就算你是一个使用CSS多年的高手,仍然会有很多CSS用法/属性/属性值你从来没使用过,甚至从来没听说过。 1.CSS的color属性并非只能用于文本显示 对于CSS的color属性,相信所有Web开发人员...
2015-11-12
ajax为什么令人惊异?ajax的优缺点
使用Ajax的最大优点,就是能在不更新整个页面的前提下维护数据。这使得Web应用程序更为迅捷地回应用户动作,并避免了在网络上发送那些没有改变的信息。 Ajax不需要任何浏览器插件,但需要用户允许JavaScript在浏览器上执行。就像DHT...
2015-11-12
HTML5的5个不错的开发工具推荐
HTML5规范终于在今年正式定稿,对于从事多年HTML5开发的人员来说绝对是一个重大新闻。数字天堂董事长,DCloud CEO王安也发表了文章,从开发者和用户两个角度分析了HTML对两个人群的优势。其实,关于HTML5的开发工具,我们以往的...
2015-11-12
回到顶部