ES6 Class(类) 总结(九)

ES6 中的 class 是一种面向对象编程的语法糖,提供了一种简洁的方式来定义对象的结构和行为。

JavaScript 语言中,生成实例对象的传统方法是通过构造函数。下面是一个例子。

javascript 复制代码
function Point(x, y) {
  this.x = x;
  this.y = y;
}
Point.prototype.toString = function () {
  return '(' + this.x + ', ' + this.y + ')';
};
var p = new Point(1, 2);

基本上,ES6 的 class 可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的 class 写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。上面的代码用 ES6 的 class 改写,就是下面这样:

javascript 复制代码
class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }
  toString() {
    return '(' + this.x + ', ' + this.y + ')';
  }
}

ES6 的类,完全可以看作构造函数的另一种写法:

javascript 复制代码
class Point {
  // ...
}
typeof Point // "function"
Point === Point.prototype.constructor // true

----------------------------------------------------------------------------
class Point {
  constructor() {
    // ...
  }
  toString() {
    // ...
  }
  toValue() {
    // ...
  }
}
// 等同于
Point.prototype = {
  constructor() {},
  toString() {},
  toValue() {},
};

主要特性:

1. 声明式语法 :使用 class 关键字声明类。
2. 构造函数 :使用 constructor 方法初始化类实例。
3. 实例方法 :定义在类内部的普通方法,使用 this 访问实例属性。
4. 静态方法 :使用 static 关键字定义,不依赖于类的实例。
5. 实例属性 :在构造函数中初始化,或使用字段声明语法(目前是 Stage 3 proposal)。
6. 继承 :使用 extends 关键字实现。
7. super 关键字 :在子类的构造函数中调用父类的构造函数或方法。
8. getter 和 setter :使用 get 和 set定义属性的访问器。
9. 私有属性和方法:使用 # 定义私有属性和方法(目前是 Stage 3 proposal)。

1. 基本类定义和实例化

javascript 复制代码
class Point {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }

    toString() {
        return `Point(${this.x}, ${this.y})`;
    }
}

let point = new Point(10, 20);
console.log(point.toString()); // 输出: Point(10, 20)

2. 静态方法、属性

javascript 复制代码
class MathUtils {
	constructor() {
    	console.log(MyClass.myStaticProp); // 42
    }
    static add(a, b) {
        return a + b;
    }
    static myStaticProp = 42;
}

console.log(MathUtils.add(1, 2)); // 输出: 3

3. 继承和 super

javascript 复制代码
class Rectangle {
    constructor(width, height) {
        this.width = width;
        this.height = height;
    }

    area() {
        return this.width * this.height;
    }
}

class Square extends Rectangle {
    constructor(sideLength) {
        super(sideLength, sideLength);
    }
}

let square = new Square(5);
console.log(square.area()); // 输出: 25

4. getter 和 setter

javascript 复制代码
class Rectangle {
    constructor(width, height) {
        this.width = width;
        this.height = height;
    }

    get area() {
        return this.width * this.height;
    }

    set width(newWidth) {
        if (newWidth > 0) {
            this.width = newWidth;
        } else {
            console.log("Width must be positive.");
        }
    }
}

let rect = new Rectangle(4, 5);
console.log(rect.area); // 输出: 20
rect.width = -10; // 输出: Width must be positive.

class的注意点

(1)严格模式

类和模块的内部,默认就是严格模式,所以不需要使用 use strict 指定运行模式。只要你的代码写在类或模块之中,就只有严格模式可用。考虑到未来所有的代码,其实都是运行在模块之中,所以 ES6 实际上把整个语言升级到了严格模式。

(2)不存在提升

类不存在变量提升(hoist),这一点与 ES5 完全不同。

javascript 复制代码
new Foo(); // ReferenceError
class Foo {}

//不会报错
//因为 Bar 继承 Foo 的时候, Foo 已经有定义了。
//但是,如果存在 class 的提升,上面代码就会报错,
//因为 class 会被提升到代码头部,而 let 命令是不提升的,
//所以导致 Bar 继承 Foo 的时候, Foo 还没有定义。
{
  let Foo = class {};
  class Bar extends Foo {
  }
}

(3)name 属性

由于本质上,ES6 的类只是 ES5 的构造函数的一层包装,所以函数的许多特性都被Class继承,包括 name 属性。

javascript 复制代码
class Point {}
Point.name // "Point"
//name 属性总是返回紧跟在 class 关键字后面的类名。

(4)Generator 方法

如果某个方法之前加上星号( * ),就表示该方法是一个 Generator 函数。

javascript 复制代码
class Foo {
  constructor(...args) {
    this.args = args;
  }
  * [Symbol.iterator]() {
    for (let arg of this.args) {
      yield arg;
    }
  }
}
for (let x of new Foo('hello', 'world')) {
  console.log(x);
}
// hello
// world

//Foo 类的 Symbol.iterator 方法前有一个星号,表示该方法是一个 Generator 函数。 
//Symbol.iterator 方法返回一个 Foo 类的默认遍历器, for...of 循环会自动调用这个遍历器。

(5)this 的指向

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

javascript 复制代码
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,在构造方法中绑定 this:

javascript 复制代码
class Logger {
  constructor() {
    this.printName = this.printName.bind(this);
  }
  // ...
}

避免使用this,使用箭头函数:

javascript 复制代码
class Obj {
  constructor() {
    this.getThis = () => this;
  }
}
const myObj = new Obj();
myObj.getThis() === myObj // true

避免使用this,使用 Proxy

javascript 复制代码
function selfish (target) {
  const cache = new WeakMap();
  const handler = {
    get (target, key) {
      const value = Reflect.get(target, key);
      if (typeof value !== 'function') {
        return value;
      }
      if (!cache.has(value)) {
        cache.set(value, value.bind(target));
      }
      return cache.get(value);
    }
  };
  const proxy = new Proxy(target, handler);
  return proxy;
}
const logger = selfish(new Logger());
相关推荐
酷爱码41 分钟前
css中的 vertical-align与line-height作用详解
前端·css
沐土Arvin1 小时前
深入理解 requestIdleCallback:浏览器空闲时段的性能优化利器
开发语言·前端·javascript·设计模式·html
专注VB编程开发20年1 小时前
VB.NET关于接口实现与简化设计的分析,封装其他类
java·前端·数据库
小妖6661 小时前
css 中 content: “\e6d0“ 怎么变成图标的?
前端·css
L耀早睡2 小时前
mapreduce打包运行
大数据·前端·spark·mapreduce
咖啡の猫2 小时前
JavaScript基础-创建对象的三种方式
开发语言·javascript·ecmascript
HouGISer2 小时前
副业小程序YUERGS,从开发到变现
前端·小程序
outstanding木槿2 小时前
react中安装依赖时的问题 【集合】
前端·javascript·react.js·node.js
小吕学编程3 小时前
Jackson使用详解
java·javascript·数据库·json
霸王蟹3 小时前
React中useState中更新是同步的还是异步的?
前端·javascript·笔记·学习·react.js·前端框架