1、简介

类的由来

javascript语言中,生成实例对象的传统方法是通过构造函数

  1. es5的写法
  2. function Point(x,y){
  3. this.x=x;
  4. this.y=y;
  5. }
  6. Point.prototype.toString=function(){
  7. console.log('toString');
  8. }
  9. var p=new Ponit(1,2);
  10. es6class写法
  11. class Point{
  12. constructor(x,y){
  13. this.x=x;
  14. this.y=y;
  15. }
  16. toString(){
  17. console.log('toString');
  18. }
  19. }
  20. 类的所有方法都定义在类的prototype属性上面
  21. class Point {
  22. constructor(){
  23. // ...
  24. }
  25. }
  26. Object.assign(Point.prototype, {
  27. toString(){},
  28. toValue(){}
  29. });
  30. 类的内部所有的方法,都是不可枚举的,这一点和es5不一致(可枚举可以用for in
  31. 或者Object.keys()遍历出来)
  32. class Point {
  33. constructor(x, y) {
  34. // ...
  35. }
  36. toString() {
  37. // ...
  38. }
  39. }
  40. Object.keys(Point.prototype)
  41. // []
  42. Object.getOwnPropertyNames(Point.prototype) //["constructor","toString"]
  43. Point === Point.prototype.constructor
  44. constructor()方法默认返回实例对象(即this),完全可以指定返回另一个对象
  45. Es5一样,类的所有实例共享一个原型对象
  46. var p1=new Ponit();
  47. var p2=new Ponit();
  48. p1.__proto__===p2.__proto__
  49. p1p2都是Point的实例,它们的原型都是Point.prototype
  50. Object.getPrototypeOf 方法来获取实例对象的原型
  51. point.__proto__===Point.prototype

constructor方法

constructor()方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法
constructor()方法默认返回实例对象(即this),完全可以返回另外一个对象

  1. class Foo{
  2. constructor(){
  3. return Object.create(null)
  4. }
  5. }
  6. new Foo() instanceof Foo //false

类的实例

与ES5一样,实例的属性除非显式定义在其本身(即定义在this对象上),否则是定义在原型上(即定义在class上),类的所有实例共享一个原型对象

  1. class Point{
  2. }
  3. var point=Ponit(2,3) //报错

取值函数(getter)和存值函数(setter)

与es5一样,在‘类’的内部可以使用get和set关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为
存值和取值函数是设置在属性的Descriptor对象上的

  1. var descriptor=Object.getOwnPropertyDescriptor(CustomHTMLElement.prototype, "html")
  2. 'get' in descriptor;
  3. 'set' in descriptor;

class表达式

  1. let person=new class {
  2. constructor(name) {
  3. this.name=name;
  4. }
  5. sayName(){}
  6. }('章三')

注意点⚠️

(1)、严格模式

(2)、不存在变量提升

(3)、name属性

  1. class Point {}
  2. Point.name // "Point"

(4)、this的指向

  1. class Logger {
  2. printName(name='there') {
  3. this.print(`Hello`)
  4. }
  5. print(text){
  6. console.log(text)
  7. }
  8. }
  9. const logger=new Logger();
  10. const {printName}=logger;
  11. printName();

上面方法printName方法中的this,默认指向Logger类的实例话,但是这个方法单独取出来使用,this会指向该方法
运行时所在的环境,由于class是严格模式,所以this实际指向的是undefined。
一个比较简单的解决方法是在构造方法中绑定this.

  1. class Logger {
  2. constructor(){
  3. this.printName=this.printName.bind(this)
  4. }
  5. }

另一种解决方案是使用箭头函数

  1. class Obj {
  2. constructor(){
  3. this.getThis=()=>this;
  4. }
  5. }
  6. const myObj = new Obj();
  7. myObj.getThis() === myObj // true

静态方法

类相当于实例的原型,所有在类中定义的方法,都会被实例继承,如果在一个方法前加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为’静态方法’;
⚠️如果静态方法包含this关键字,这个this指向的是类,而不是实例
静态方法可以被继承
静态方法也可以从super对象上调用的

  1. class Foo {
  2. static classMethod(){
  3. return 'hello'
  4. }
  5. }
  6. class Bar extends Foo {
  7. static classMethod(){
  8. return super.classMethod()+',too';
  9. }
  10. }
  11. Bar.classMethod() //'hello,too'

静态属性

静态属性指的是Class本身的属性,即Class.propName,而不是定义在实例对象(this)上的属性
因为Es6明确规定,Class内部只有静态方法,没有静态属性。现在有一个提案,在实例属性的前面,加上static关键字。

实例属性

实例属性除了定义在constructor方法里面的this上面,也可以定义在类的最顶层。

  1. class Point {
  2. constructor(){
  3. this._count=0;
  4. }
  5. }
  6. class Point {
  7. _count=0;
  8. }

new.target属性

new.target属性可以用来确定构造函数是怎么调用的

  1. 方法一
  2. function Person(name) {
  3. if(new.target!==undefined) {
  4. this.name=name;
  5. }else{
  6. throw new Error('必须使用new命令生成实例');
  7. }
  8. }
  9. 方法二
  10. function Person(name) {
  11. if(new.target===Person) {
  12. this.name=name;
  13. }else{
  14. throw new Error('必须使用new命令生成实例');
  15. }
  16. }

1、class内部调用new.target,返回当前Class
2、子类继承父类时,new.target会返回子类 (利用这一点可以写出不能独立使用,必须继承后才能使用的类)

super关键字

super关键字,即可以当作函数使用,也可以当作对象使用

  1. 第一种:super作为函数调用时,代表父类的构造函数,返回的是子类的实例,即super内部的this指的是B的实例,ES6要求,子类的构造函数必须执行一 次super函数
  2. 第二种:super作为对象时,在普通方法中,指向父类的原型对象;

    在静态方法中,指向父类;在ES6规定,在子类普通方法中通过super调用父类的方法时,方法内部的this指向 当前的子类实例 ```javascript class A{ constructor(){ this.x=1; } print(){ console.log(this.x) } }

class B extend A{ constructor(){ super(); this.x=2; } m(){ super.print(); } } let b=new B(); b.m() //2

  1. 由于this指向子类实例,所以如果通过super对某个属性赋值,这时super就是this,赋值的属性会变成子类实例的属性。
  2. ```javascript
  3. class A {
  4. constructor() {
  5. this.x = 1;
  6. }
  7. }
  8. class B extends A {
  9. constructor() {
  10. super();
  11. this.x = 2;
  12. super.x = 3;
  13. console.log(super.x); // undefined,这里太绝了吧,表示没看懂💔
  14. console.log(this.x); // 3
  15. }
  16. }
  17. let b = new B();

上面代码中,super.x赋值为3,这时等同于对this.x赋值为3。而当读取super.x的时候,读的是A.prototype.x,所以返回undefined。
注意⚠️使用super的时候,必须显式指定是作为函数、还是作为对象使用,否则会报错。