class 的介绍和使用

class介绍

ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。

ES6 的class可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。

calss基本语法

 我们先来回顾一下ES5类的写法

function Person(name, age) {
    this.name = name;
    this.age = age;
}
Person.prototype.eat = function () {
    console.log('正在吃饭');
}
Person.prototype.introduce = function () {
    console.log(`我的名字叫${this.name},我今年${this.age}`);
}
const person = new Person('张三', 18)
person.eat();  // 正在吃饭
person.introduce();   // 我的名字叫张三,我今年18

我们创建了一个人物的类,给这个类添加了属性和方法。

下面我们使用ES6中的Class来改写这个方法

class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
    eat() {
        console.log('正在吃饭');
    }
    introduce() {
        console.log(`我的名字叫${this.name},我今年${this.age}`);
    }
}
const person = new Person('张三', 18)

person.eat();  // 正在吃饭
person.introduce();   // 我的名字叫张三,我今年18

定义eat()introduce()方法的时候,前面不需要加上function这个关键字,直接把函数定义放进去了就可以了。

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

使用的时候,也是直接对类使用new命令,跟构造函数的用法完全一致。

类的数据类型就是函数,类本身就指向构造函数

console.log(typeof Person);   // function
console.log(Person === Person.prototype.constructor);  // true

构造函数的prototype属性,在 ES6 的“类”上面继续存在。事实上,类的所有方法都定义在类的prototype属性上面。

 我们在Person类上定义eat()introduce()方法都在prototype属性上面。

因此,在类的实例上面调用方法,其实就是调用原型上的方法。

console.log(person.constructor === Person.prototype.constructor);  // true

由于类的方法都定义在prototype对象上面,所以类的新方法可以添加在prototype对象上面。Object.assign()方法可以很方便地一次向类添加多个方法。

// 给类添加新方法
Object.assign(Person.prototype, {
  fn1() {},
  fn2() {}
});

 

 类的内部所有定义的方法,都是不可枚举的,这一点与 ES5 的行为不一致。

// Object.keys() 方法会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和正常循环遍历该对象时返回的顺序一致 
console.log(Object.keys(Person.prototype));  // ['fn1', 'fn2']

Object.keys() 方法会返回一个由一个给定对象的自身可枚举属性组成的数组,在数组中我么可以看到,数组里面只有两个值['fn1', 'fn2'],我们在构造函数内部定义的eat()introduce()方法并没有出现在数组中。

constructor 方法

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

class A {}
console.log(new A());

 上面代码中,定义了一个空的类A,JavaScript 引擎会自动为它添加一个空的constructor()方法。

类必须使用new调用,否则会报错。

class A {}
console.log(A());   // calss.html:79 Uncaught TypeError: Class constructor A cannot be invoked without 'new' at 

function  B() {
}
console.log(B());   // undefined 不报错

这是它跟普通构造函数的一个主要区别,后者不用new也可以执行。

与 ES5 一样,类的所有实例共享一个原型对象。

class A {}
const a = new A()
const a1 = new A()
console.log(a.__proto__ === a1.__proto__);  // true

它们的原型都是Point.prototype,所以__proto__属性是相等的。

这也意味着,可以通过实例的__proto__属性为“类”添加方法。

a.__proto__.hi = function () {
    console.log("hi");
}
a.hi()   // hi
a1.hi()   // hi

上面代码在p1的原型上添加了一个hi()方法。

由于a的原型就是p2的原型,因此a1也可以调用这个方法。

这意味着,使用实例的__proto__属性改写原型,必须相当谨慎,不推荐使用,因为这会改变“类”的原始定义,影响到所有实例。

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

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

class MyClass {
  constructor() {
    // ...
  }
  get prop() {
    return 'getter';
  }
  set prop(value) {
    console.log('setter: '+value);
  }
}

let inst = new MyClass();

inst.prop = 123;
// setter: 123

inst.prop
// 'getter'

上面代码中,prop属性有对应的存值函数和取值函数,因此赋值和读取行为都被自定义了。

静态方法

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

class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
    eat() {
        console.log('正在吃饭');
    }
    introduce() {
        console.log(`我的名字叫${this.name},我今年${this.age}`);
    }
    // 添加静态方法,只有类本身可以调用
    static hello () {
        console.log('hi');
    }
}
// 添加静态方法,只有类本身可以调用 与上static添加效果相同
Person.hi = function () {
    console.log('hello');
}
const person = new Person('张三', 18)
Person.hi()   // hello
Person.hello()   // hi
// person.hi()   // Uncaught TypeError: person.hi is not a function at
person.hello() // Uncaught TypeError: person.hello is not a function at

方法前有static关键字,表明该方法是一个静态方法,可以直接在Person类上调用,而不是在Person类的实例上调用。如果在实例上调用静态方法,会抛出一个错误,表示不存在该方法。

注意,如果静态方法包含this关键字,这个this指的是类,而不是实例。

静态方法可以与非静态方法重名。

class Persom {
    constructor () {
        this.name = name
    }
    hi (){
        console.log("你好");
    }
    static hi (){
        console.log('hello');
    }
}
const person = new Persom('张三')
person.hi() // 你好
Persom.hi()  // hello

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

// 父类的静态方法,可以被子类继承。
class Father {
    constructor (){}
    static hi (){
        console.log("你好");
        return "你好"
    }
}
class Son extends Father {
    constructor (){}
    static world (){
        // 静态方法也是可以从super对象上调用的。 等同于Father.hi()
        console.log(super.hi() + "世界");
    }
}
// 上面代码中,父类Father有一个静态方法,子类Bar可以调用这个方法。
Son.hi()   // 你好
Son.world()  // 你好世界

上面代码中,父类Father有一个静态方法,子类Son可以调用这个方法。

静态方法也是可以从super对象上调用的。

实例属性的新写法

实例属性除了定义在constructor()方法里面的this上面,也可以定义在类的最顶层。其他都不变。

class Person {
    // 定义在类的最顶层
    name = "张三"
    constructor() { }
    static hi() {
        console.log("你好");
        return "你好"
    }
}
const person = new Person()
console.log(person.name);

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

另外,写起来也比较简洁。

静态属性

静态属性指的是 Class 本身的属性,即Class.propName,而不是定义在实例对象(this)上的属性。

// 静态属性 
class Person {
    // 为Persom类定义了一个静态属性name
    static name = "张三"
    constructor () {}
}
// 为Persom类定义了一个静态属性age。
Person.age = "18"
console.log(Person.age);  // 18
console.log(Person.name);   // "张三"

两种写法一种是通过类.xxx = xxx 例如: Person.age = "18" 

另外一种是在类中定义属性的时候加上static 例如: static name = "张三"

私有属性

私有方法和私有属性,是只能在类的内部访问的方法和属性,外部不能访问。

目前,为class加了私有属性。方法是在属性名之前,使用#表示。

// 私有属性
class Counter {
    #count = 0;
    get value() {
        return this.#count;
    }
    increment() {
        this.#count++;
    }
}
const counter = new Counter();
console.log(counter.value);  // 0
console.log(counter.increment());  
// counter.#count // 报错
// counter.#count = 42 // 报错

上面代码中,#count就是私有属性,只能在类的内部使用(this.#count)。如果在类的外部使用,就会报错。

这种写法不仅可以写私有属性,还可以用来写私有方法。

另外,私有属性也可以设置 getter 和 setter 方法。

私有属性和私有方法前面,也可以加上static关键字,表示这是一个静态的私有属性或私有方法。

in 运算符

in运算符判断当前类A的实例,是否有私有属性#item,如果有返回true,否则返回false

class A {
    isItem () {
        if (#item in this) {
            return true
        } else {
            return false
        }
    }
}
const a = new A()
console.log(a.isItem());

in也可以跟this一起配合使用。

这里我使用了一下会报语法错误

静态块

静态属性的一个问题是,它的初始化要么写在类的外部,要么写在constructor()方法里面

ES2022 引入了静态块(static block),允许在类的内部设置一个代码块,在类生成时运行一次,主要作用是对静态属性进行初始化。

class C {
  static x = ...;
  static y;
  static z;

  static {
    try {
      const obj = doSomethingWith(this.x);
      this.y = obj.y;
      this.z = obj.z;
    }
    catch {
      this.y = ...;
      this.z = ...;
    }
  }
}

它的好处是将静态属性yz的初始化逻辑,写入了类的内部,而且只运行一次。

每个类只能有一个静态块,在静态属性声明后运行。静态块的内部不能有return语句。

静态块内部可以使用类名或this,指代当前类。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
ES6引入了class关键字,使得JavaScript可以更方便地使用面向对象编程。下面是ES6 class类的使用介绍: 1. 定义类: 使用class关键字定义一个类,类名通常首字母大写。类可以包含构造函数、方法和属性。 ```javascript class MyClass { constructor() { // 构造函数 } method() { // 方法 } property = value; // 属性 } ``` 2. 创建对象: 使用`new`关键字创建类的实例对象。 ```javascript const myObject = new MyClass(); ``` 3. 构造函数: 构造函数是一个特殊的方法,用于初始化对象的属性。在创建对象时自动调用。 ```javascript class MyClass { constructor(name) { this.name = name; } } const myObject = new MyClass("Alice"); console.log(myObject.name); // 输出 "Alice" ``` 4. 方法: 类中的方法定义在类的原型上,可以通过实例对象调用。 ```javascript class MyClass { method() { console.log("Hello"); } } const myObject = new MyClass(); myObject.method(); // 输出 "Hello" ``` 5. 继承: 使用`extends`关键字实现类的继承。子类可以继承父类的属性和方法,并可以添加自己的属性和方法。 ```javascript class ChildClass extends ParentClass { constructor() { super(); // 调用父类的构造函数 } childMethod() { // 子类的方法 } } ``` 6. 静态方法: 使用`static`关键字定义静态方法,静态方法属于类本身而不是实例对象。 ```javascript class MyClass { static staticMethod() { console.log("Static method"); } } MyClass.staticMethod(); // 输出 "Static method" ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李公子丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值