Class 基本语法

55 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第17天,点击查看活动详情

前言

  • class在面向对象编程中,它是为对象提供成员变量的初始值和成员函数/方法的实现。在开发中我们会创建相同类型的对象,当然可以使用new function实现但class有更为好用的功能,下面来看看吧!

class语法

基本语法

class oneClass {
    constructor() { ... }  //构造函数
    method1() { ... }  //方法
    method2() { ... }
}

使用class

class People {
    constructor(name) {
        this.name = name;
    }
    
    callName() {
        alert(this.name)
    }
}


// 使用

let people = new People("Jay")
people.callName();
  • 使用步骤
  1. 创建people类
  2. 使用constructor初始化对象,将参数赋值给this.name
  3. 用new创建新对象赋值给people
  4. 用people对象调用方法callName得到name

关于class

  • 先看一段代码:
class User {
  constructor(name) { this.name = name; }
  sayHi() { alert(this.name); }
}

// 佐证:User 是一个函数
alert(typeof User); // function
let user = new User("Jay")

class User {...} 构造实际上做了如下的事儿:

  1. 创建User函数,函数的代码实际就是constructor方法
  2. 储存类中的方法sayHi

new User对象创建后,调用方法会从原型中获取相应的方法。所以可以user.sayHi() (即 new User('Jay').sayHi())

image.png

下面这些代码很好地解释了它们:

class User {
  constructor(name) { this.name = name; }
  sayHi() { alert(this.name); }
}

// class 是一个函数
alert(typeof User); // function

// ...或者,更确切地说,是 constructor 方法
alert(User === User.prototype.constructor); // true

// 方法在 User.prototype 中,例如:
alert(User.prototype.sayHi); // sayHi 方法的代码

// 在原型中实际上有两个方法
alert(Object.getOwnPropertyNames(User.prototype)); // constructor, sayHi

不仅仅是语法糖

实际上可以使用函数的方法创建出和  class 的情况下声明相同的内容:

// 用纯函数重写 class User

// 1. 创建构造器函数
function User(name) {
  this.name = name;
}
// 函数的原型(prototype)默认具有 "constructor" 属性,所以,我们不需要创建它

// 2. 将方法添加到原型,这里就是类的替代了
User.prototype.sayHi = function() {
  alert(this.name);
};

// 用法:
let user = new User("John");
user.sayHi();

上面的这段代码,看起来和class创建的没什么差别,但是它的差异也很大:

  1. 首先,通过 class 创建的函数具有特殊的内部属性标记 [[IsClassConstructor]]: true。因此,它与手动创建并不完全相同。

    编程语言会在许多地方检查该属性。例如,与普通函数不同,必须使用 new 来调用它:

    class User {
      constructor() {}
    }
    
    alert(typeof User); // function
    User(); // Error: Class constructor User cannot be invoked without 'new'
    

也就是new User.sayHi()

此外,大多数 JavaScript 引擎中的类构造器的字符串表示形式都以 “class…” 开头

```
class User {
  constructor() {}
}

alert(User); // class User { ... }
```

还有其他的不同之处,我们很快就会看到。
  1. 类方法不可枚举。 类定义将 "prototype" 中的所有方法的 enumerable 标志设置为 false

    这很好,因为如果我们对一个对象调用 for..in 方法,我们通常不希望 class 方法出现。

  2. 类总是使用 use strict。 在类构造中的所有代码都将自动进入严格模式。

此外,class 语法还带来了许多其他功能。

类表达式

就像函数一样,类可以在另外一个表达式中被定义,被传递,被返回,被赋值等。

  1. 类似于命名函数表达式(Named Function Expressions),类表达式可能也应该有一个名字。如果类表达式有名字,那么该名字仅在类内部可见:
// “命名类表达式(Named Class Expression)”
// (规范中没有这样的术语,但是它和命名函数表达式类似)
let User = class MyClass {
  sayHi() {
    alert(MyClass); // MyClass 这个名字仅在类内部可见
  }
};

new User().sayHi(); // 正常运行,显示 MyClass 中定义的内容

alert(MyClass); // error,MyClass 在外部不可见
  1. 可以动态地“按需”创建类,就像这样:
function makeClass(phrase) {
  // 声明一个类并返回它
  return class {
    sayHi() {
      alert(phrase);
    }
  };
}

// 创建一个新的类
let User = makeClass("Hello");
new User().sayHi(); // Hello

Getters/setters

就像对象字面量,类可能包括 getters/setters,计算属性(computed properties)等。

这是一个使用 get/set 实现 user.name 的示例:

class User {

  constructor(name) {
    // 调用 setter
    this.name = name;
  }

get name() {
    return this._name;
  }

set name(value) {
    if (value.length < 4) {
      alert("Name is too short.");
      return;
    }
    this._name = value;
  }

}

let user = new User("John");
alert(user.name); // John

user = new User(""); // Name is too short.

从技术上来讲,这样的类声明可以通过在 User.prototype 中创建 getters 和 setters 来实现。

计算属性名称 […]

这里有一个使用中括号 [...] 的计算方法名称示例:

class User {

['say' + 'Hi']() { //就相当于sayHi(){……}
    alert("Hello");
  }

}

new User().sayHi();  //‘hello’

Class 字段

旧的浏览器可能需要 polyfill,类字段(field)是最近才添加到语言中的。

之前,我们的类仅具有方法。

“类字段”是一种允许添加任何属性的语法,它可以减少代码量,而且理解起来更简单。

例如,让我们在 class User 中添加一个 name 属性:

class User {
name = "John";

  sayHi() {
    alert(`Hello, ${this.name}!`);
  }
}

new User().sayHi(); // Hello, John!

所以,我们就只需在表达式中写 " = ",就这样。类字段重要的不同之处在于,它们会在每个独立对象中被设好,而不是设在 User.prototype

class User {
name = "John";
}

let user = new User();
alert(user.name); // John
alert(User.prototype.name); // undefined

使用类字段制作绑定方法

JavaScript 中的函数具有动态的 this,它取决于调用上下文。因此,如果一个对象方法被传递到某处,或在另一个上下文中被调用,则 this 将不再是对其对象的引用。

例如,此代码将显示 undefined

class Button {
  constructor(value) {
    this.value = value;
  }

  click() {
    alert(this.value);
  }
}

let button = new Button("hello");

setTimeout(button.click, 1000); // undefined

这个问题被称为“丢失 this”,原因是不是同一个上下文。

有两种可以修复它的方式:

  1. 传递一个包装函数,例如 setTimeout(() => button.click(), 1000)
  2. 将方法绑定到对象,例如在 constructor 中。

我们这里用另一种方法:

class Button {
  constructor(value) {
    this.value = value;
  }
click = () => {
    alert(this.value);
  }
}

let button = new Button("hello");

setTimeout(button.click, 1000); // hello

类字段 click = () => {...} 是基于每一个对象被创建的,在这里对于每一个 Button 对象都有一个独立的方法,在内部都有一个指向此对象的 this。我们可以把 button.click 传递到任何地方,而且 this 的值总是正确的。

在浏览器环境中,它对于进行事件监听尤为有用。