class的基本用法

39 阅读4分钟
类的由来
//es5是通过构造函数来实现
function Point(x, y) {
    this.x = x;
    this.y = y
}
Point.prototype.toString = function () {
    return '(' + this.x + ', ' + this.y + ')'
}

var p = new Point(2,3)
console.log('p',p)
//es6 引入了Class的概念 通过class关键字,可以定义类。es6的class可以看做一个语法糖,他的绝大数功能es5都能做到。

//class的写法
class Point2 {
    //构造函数
    constructor(x,y){
        this.x = x;
        this.y = y
    }
    //不用写function 方法定义在原型上 等同于
    //Point.prototype.toString
    toString() {
        return '(' + this.x + ', ' + this.y + ')'
    }
}
console.log(typeof Point2);//function
console.log(Point2.prototype.constructor == Point2)//true

//类内部定义的方法都是不可枚举的  Object.keys方法会返回一个由一个给定对象的自身可枚举属性组成的数组,
console.log(Object.keys(Point2.prototype))//[]
console.log(Object.keys(Point.prototype))//['toString']
constructor() 方法
class Point {
    
}
//类必须通过new调用
class Point {
    //类的默认方法,new时会会自动调用该方法,如果没有显示的定义,会默认添加一个空的constructor
    constructor(){
        //默认返回this
        return Object.create(null)
    }
}
p = new Point()
console.log(p instanceof Point)//false
类的实例
//除非显示定义在this上否则都是定义在原型上
class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  toString() {
    return '(' + this.x + ', ' + this.y + ')';
  }
}

var point = new Point(2, 3);

point.toString() // (2, 3)

point.hasOwnProperty('x') // true
point.hasOwnProperty('y') // true
point.hasOwnProperty('toString') // false
point.__proto__.hasOwnProperty('toString') // true


//类的实例都共享一个原型对象
var p1 = new Point(2,3);
var p2 = new Point(3,2);

p1.__proto__ === p2.__proto__
实例属性新写法
class Point {
  _count = 0 //定义在实例上
  constructor(x, y) {
    this.x = x;
    this.y = y;
    //this._count = 0;
  }

  toString() {
    return '(' + this.x + ', ' + this.y + ')';
  }
}
取值函数(getter)和存值函数(setter)
class MyClass {
    constructor(){

    }
    //对取值进行拦截操作
    get prop(){
        return 'getter'
    }
    /* 对设置值进行拦截操作,value 要设置的新值 */
    set prop(value){
        console.log('setter'+value)
    }
}
var p = new MyClass()
console.log(p.prop)//getter
p.prop = 123//setter123
属性表达式
let methodName = 'getArea';

class Square {
  constructor(length) {
    // ...
  }

  [methodName]() {
    // ...
  }
}
Class 表达式
const MyClass = class Me {
  getClassName() {
    return Me.name;
  }
};
//类内部没用到的话可以省略Me
const MyClass = class { /* ... */ };

//立即执行class
let person = new class {
  constructor(name) {
    this.name = name;
  }

  sayName() {
    console.log(this.name);
  }
}('张三');

person.sayName(); // "张三"
静态方法
//加上static关键字,表示该方法不被实例继承,通过类直接调用的方法称为静态方法
class Foo {
  static classMethod() {
    return 'hello';
  }
}

Foo.classMethod() // 'hello'

var foo = new Foo();
foo.classMethod()
// TypeError: foo.classMethod is not a function

//静态方法中的this指的是类,不是实例
class Foo {
  static bar() {
    this.baz();
  }
  static baz() {
    console.log('hello');
  }
  baz() {
    console.log('world');
  }
}

Foo.bar() // hello

//父类的静态方法可以被子类继承
class Foo {
  static classMethod() {
    return 'hello';
  }
}

class Bar extends Foo {
}

Bar.classMethod() // 'hello'

//静态方法可以从super对象上调用
class Foo {
  static classMethod() {
    return 'hello';
  }
}

class Bar extends Foo {
  static classMethod() {
    return super.classMethod() + ', too';
  }
}

Bar.classMethod() // "hello, too"
静态属性
class Foo {
    static str = '测试' //新写法
}
//老写法
Foo.prop = 1
console.dir(Foo)
私有属性和私有方法
//只能在类内才能访问的属性和方法
class IncreasingCounter {
    //在属性前用#表示
    #count = 0;
    get value() {
      console.log('Getting the current value!');
      return this.#count;
    }
    increment() {
      this.#count++;
    }
    //私有方法
    #sum() {
        return 'sum';
    }
}
const counter = new IncreasingCounter();
console.log(counter.value)//0
counter.#count // 报错
counter.#count = 42 // 报错 

class Counter {
    #xValue = 0;
    
    constructor() {
      console.log(this.#x);
    }
    //设置get和set
    get #x() { return this.#xValue; }
    set #x(value) {
      this.#xValue = value;
    }
}
const counter = new Counter();


//静态的私有属性和方法也只能在类内部调用
class FakeMath {
  static PI = 22 / 7;
  static #totallyRandomNumber = 4;

  static #computeRandomNumber() {
    return FakeMath.#totallyRandomNumber;
  }

  static random() {
    console.log('I heard you like random numbers…')
    return FakeMath.#computeRandomNumber();
  }
}

FakeMath.PI // 3.142857142857143
FakeMath.random()
// I heard you like random numbers…
// 4
FakeMath.#totallyRandomNumber // 报错
FakeMath.#computeRandomNumber() // 报错
static静态块
class C {
    static x;
    static y;
    static z;
    constructor() {
        console.log('constructor')
    }
    /* 给静态属性付初始值 */
    static {
        console.log('static')
        try {
            const obj = doSomethingWith(this.x);
            this.y = obj.y;
            this.z = obj.z;
        }
        catch {
            this.y = 1;
            this.z = 2;
            //this 指代当前类
            this.x; // 1
            // 或者
            C.x; // 1
        }
    }
}
//static
//constructor  静态代码块先执行
var c = new C()
类的注意点
严格模式
    类和模块的内部,默认就是严格模式
不存在提升
    new Foo(); // ReferenceError
    class Foo {}

this指向
类的方法内部如果含有this,它默认指向类的实例。但是,必须非常小心,一旦单独使用该方法,很可能报错。

class Logger {
  printName(name = 'there') {
    this.print(`Hello ${name}`);
  }

  print(text) {
    console.log(text);
  }
}

const logger = new Logger();
const { printName } = logger;
printName(); // TypeError: Cannot read property 'print' of undefined
//this 实际指向的是undefined

解决方法
在构造函数中绑定this
class Logger {
  constructor() {
    this.printName = this.printName.bind(this);
  }
  
  // ...
}
另一种解决方法是使用箭头函数。
new.target 属性
返回new命令作用于的那个构造函数。如果构造函数不是通过new命令或Reflect.construct()调用的,
new.target会返回undefined

function Person(name) {
  if (new.target !== undefined) {
    this.name = name;
  } else {
    throw new Error('必须使用 new 命令生成实例');
  }
}

// 另一种写法
function Person(name) {
  if (new.target === Person) {
    this.name = name;
  } else {
    throw new Error('必须使用 new 命令生成实例');
  }
}

var person = new Person('张三'); // 正确
var notAPerson = Person.call(person, '张三');  // 报错


不能独立使用、必须继承后才能使用的类。
class Shape {
  constructor() {
    if (new.target === Shape) {
      throw new Error('本类不能实例化');
    }
  }
}

class Rectangle extends Shape {
  constructor(length, width) {
    super();
    // ...
  }
}

var x = new Shape();  // 报错
var y = new Rectangle(3, 4);  // 正确