js中的class类

该文章已生成可运行项目,

1、es6中推目的是什么?

为了更好的实现面向对象编程,降低代码的冗余度,es6推出了class类的概念。他实际上是一种语法糖,本质上利用的还是原型和构造函数的概念。

2、类定义

        两种方式:

        A)类声明

class Person {}

        B)类表达式

let Person = class [PersonName] {}


//表达式中这个名称PersonName是可选的,可通过Person.name访问
console.log(Person.name) //PersonName

//创建对象只能用Perosn类名

let p=new Person()

注意:函数受函数作用域的限制,类受块作用域的限制。

{
  class Person{}
  function fun(){}
}

console.log(fun)
console.log(Person)

3、类的构成

js中class类主要分为五类:构造函数方法,实例方法,静态方法,获取函数,设置函数。

class Person {
  //构造函数方法
  constructor(name,age){
    this.name=name;
    this.age=age;
  }
  //实例方法
  getName(){
    console.log(this.name)
  }
  //静态方法
  static sayHello(){
    console.log('你好,陌生人')
  }
  //设置函数
  set _name(val){
    console.log('有人试图改名字',val)
    this.name=val
  }
  //获取函数
  get _name(){
    console.log('有人想获取名字')
    return this.name
  }
}

let p =new Person('zhangsan',18)
p.getName()
Person.sayHello()
p._name='lisi'
console.log(p._name)

4、类构造函数

constructor关键字用于在类定义块内部创建类的构造函数。方法名constructor会告诉解释器在使用new操作符创建类实例的时候,应该调用这个函数。构造函数的定义不是必须的,不定义构造函数相当于将构造函数定义为空函数。

使用new创建类实例的过程:

A)创建一个新对象

B)对象的[[prototype]]属性指向构造函数的原型

C)构造函数中的this指向该对象

D) 执行构造函数,初始化实例对象

E) 构造函数若返回引用类型的值,将返回该值;否则,将返回新对象。

类构造函数和构造函数的区别:

类构造函数只能通过new 操作符调用,构造函数可以直接调用,也能用new调用。

类的本质:

类的本质就是一个函数,只不过只能用new调用。

console.log(typeof Person)
console.log(Person.constructor===Function)


如上面所说,Person的本质上是一个函数(类)对象,他是Function的一个实例,他自身上是没有constructor属性的,但是他的__proto__原型对象指向Function.prototype是有constructor,并且指向Function。

因为Person.constructor就是Function,因此调用该函数会返回一个函数,如下:

A)使用new操作符调用

B)直接调用

因为类是函数,所以可以向立即执行函数那样立即实例化

let p=new class Person {
  //构造函数方法
  constructor(name,age){
    this.name=name;
    this.age=age;
    console.log('调用了构造函数')
  }
  //实例方法
  getName(){
    console.log(this.name)
  }
  //静态方法
  static sayHello(){
    console.log('你好,陌生人')
  }
  //设置函数
  set _name(val){
    console.log('有人试图改名字',val)
    this.name=val
  }
  //获取函数
  get _name(){
    console.log('有人想获取名字')
    return this.name
  }
}('liji',999)

console.log(p)

5、实例,原型和类成员

实例数据成员和方法:

定义在类构造函数中的变量和方法,在用new实例化之后,会进入到不同的对象实例当中,他们是相互独立的。

class Person {
    sex=‘男’  //也会作为实例属性,不推荐这么用
  //构造函数方法
  constructor(name,age){
    this.name=name;
    this.age=age;
    this.sayName=()=>{
      console.log(this.name)
    }
  }
}


let p1=new Person('zhangsan',17)
let p2=new Person('lisi',18)

console.log(p1.sayName===p2.sayName) //输出false,实例方法在不同实例间是相互独立的

原型数据成员和方法:

A)原型方法:

定义在类块中的方法,为原型方法,可在不同的实例之间共享。

class Person {
  //构造函数方法
  constructor(name,age){
    this.name=name;
    this.age=age;
    this.sayName=()=>{
      console.log(this.name)
    }
  }
  //原型方法
  getName(){
    console.log(this.name)
  }
}


let p1=new Person('zhangsan',17)
let p2=new Person('lisi',18)

console.log(p1.getName===p2.getName) //输出true,原型方法共享

B)原型数据成员:

class Person2 {
  sex:'nan'
}

如上,类中不支持直接添加原型数据成员,需要手动添加:

Person2.prototype.sex='男'

类方法和类数据成员

A)类方法:

class Person {
  
  //静态方法
  static sayHello(){
    console.log('你好,陌生人')
  }
  
  static sayGoodBye(){
    console.log('再见,陌生人')
  }
  

}

B)类数据成员:

        两种方式:

//第一种
class Person {
    static sex='男'
}

//第二种
Person.sex='男'

小结:

类成员和原型成员都是实现共享,只是从不同层面上的共享。类成员是类层面的共享,原型成员是实例层面的共享。

6、迭代器和生成器方法

可以在类本身,或原型上定义生成器函数,使实例化出来的对象成为一个可迭代对象。(虽然方法能定义在类本身或原型上,但是只有在原型上实现[Symbol.iterator],实例化出来的对象才是可迭代对象)

//迭代器方法
class Person {
  constructor(){
    this.nickNames=['jack','jake','J-dog']
  }
  
  [Symbol.iterator](){
    return this.nickNames.entries()
  }
}
let p=new Person();
for (let item of p){
  console.log(item)
}

//生成器方法
class Person {
  constructor(){
    this.nickNames=['jack','jake','J-dog']
  }
  
  *[Symbol.iterator](){
    yield *this.nickNames.entries()
  }
}
let p=new Person();
for (let item of p){
  console.log(item)
}

7、类继承

class类通过extends关键字实现继承,他可以继承自任何拥有[[Construct]]和原型的对象(不就是函数对象吗),这意味着,他既可以继承自类,亦可以继承自构造函数

//继承自类
class Person {}
class Driver extends Person{} 
let d=new Driver();
console.log(d instanceof Person)

//继承自构造函数
function Person() {}
class Driver extends Person{}
let d=new Driver();
console.log(d instanceof Person)

注意:extends可以用于类表达式中,extends关键字后面也可以接表达式(求值结果为一个函数或一个类)

let Bar = class extends Foo {} //extends用于类表达式中

//extends后面接表达式
let getZoom=()=>{
    return class Zoom {
      test(){
        console.log('test')
      }
    }
}

class Foo extends getZoom(){}
let foo=new Foo()
foo.test()  //test

super关键字的用法:

首先要记住,super关键字必须用在派生类中,且不能单独使用。

A)用在派生类构造函数中:

class Parent {
  constructor(name) {
    this.name = name;
  }
}

class Child extends Parent {
  constructor(name, age) {
    super(name); // 调用父类的构造函数,并传递参数
    this.age = age;
  }
}

const child = new Child('Alice', 10);
console.log(child.name); // 输出: Alice
console.log(child.age);  // 输出: 10

注意点:

如果子类没有显示定义构造函数,在实例化的时候,会通过super调用父类构造函数。

在子类的构造函数中,super必须用在this之前。

在子类构造函数中,要么调用super,要么必须返回一个对象。

B)用在派生类原型方法中

class Parent {
  greet() {
    return 'Hello from Parent';
  }
}

class Child extends Parent {
  greet() {
    return super.greet() + ' and Hello from Child';
  }
}

const child = new Child();
console.log(child.greet()); 
// 输出: Hello from Parent and Hello from Child

C)用在派生类的静态方法中

class Parent {
  static staticMethod() {
    return 'Static method from Parent';
  }
}

class Child extends Parent {
  static staticMethod() {
    return super.staticMethod() + ' and Static method from Child';
  }
}

console.log(Child.staticMethod());
// 输出: Static method from Parent and Static method from Child

D)在getter和setter中使用

class Parent {
  get value() {
    return 'Parent value';
  }
}

class Child extends Parent {
  get value() {
    return super.value + ' and Child value';
  }
}

const child = new Child();
console.log(child.value);
// 输出: Parent value and Child value

抽象基类:

有时候我们可能需要这样一个类,他只能被继承,但是自身不能被实例化。这时候我们就用到了抽象基类,我们可以要求子类必须要实现某个方法。如下:

class Parent {
  constructor(){
    if(new.target=='Parent'){
      throw new Error('Parent不能被实例化')
    }
    if(!this.foo){
      throw new Error('派生类必须实现foo方法')
    }
    console.log('success!')
  }
}

class Child1 extends Parent {
  foo(){}
}

class Child2 extends Parent {}

let child1=new Child1()
let child2=new Child2()

以上代码中,在父类的构造函数中使用new.target判断通过new操作符调用的函数或类是不是父类本身,以此保证父类不能被实例化。

子类实例化时,如果子类中没有显示定义构造函数,则默认用super调用父类的构造函数,父类构造函数中的this指向创建出来的子类实例。因此this.foo判断了子类的实例或原型上有没有foo方法,没有就会抛出异常。

继承内置类型:

class MyArray extends Array {
  constructor(...args) {
    super(...args); // 调用父类构造函数,传递参数
  }
 
  // 添加一个自定义方法
  first() {
    return this[0];
  }
 
  // 重写一个方法(可选)
  // 注意:重写内置方法时要小心,以免破坏内置行为
  // 这里只是演示,通常不建议重写如 push、pop 等关键方法
  // push(...items) {
  //   console.log('Adding items:', items);
  //   return super.push(...items);
  // }
}
 
// 使用自定义数组类
const myArr = new MyArray(1, 2, 3);
console.log(myArr.first()); // 输出: 1
console.log(myArr instanceof MyArray); // 输出: true
console.log(myArr instanceof Array); // 输出: true

类混入:

js提供的Object.assign()方法实现了对象混入的功能,类混入的话需要你自己编写相应的表达式。

js中,一个class无法同时继承自多个类,但是通过现有的东西,我们是可以实现的

class A {
  a(){
    console.log('a')
  }
}

class B {
  b(){
    console.log('b')
  }
}

class C {
  c(){
    console.log('c')
  }
}

如上,有A,B,C三个类,我们要定义一个目标类,同时继承他们三个怎么做呢?我们可以让A先继承B,B在继承C,最后让目标类继承A。

let extC=()=>{
  return class extends C {
    b(){
      console.log('b')
    }
  }
}
let extB=(supClass)=>{
  return class  extends supClass {
    a(){
      console.log('a')
    }
  }
}

let Target= extB(extC()) 
let target = new Target()
target.a()  //a
target.b()  //b
target.c()  //c

以上这种方式很麻烦的,读着代码也挺绕的。首先你要定义一个类,在后面定义函数,继承这个类,然后加上自己的东西,返回一个新类。上面代码中,extC继承了C类,返回了B类。extB继承了B类,返回了A类。最终Target相当于A类。

现在很多的js框架实现继承,已抛弃混入模式,转向组合模式(把方法提取到独立的类和辅助对象中,然后把他们组合起来,但不使用继承),如下:

// 定义一个混入对象
const myMixin = {
  greet() {
    console.log(`Hello, my name is ${this.name}`);
  },
  setName(name) {
    this.name = name;
  }
};

// 定义一个混入对象
const myMixin2 = {
  sayAge() {
    console.log(`我今年${this.age}岁了`)
  }
};
 
// 定义一个类
class Person {
  constructor(age) {
    this.name = '';
    this.age=age
  }
}
 
// 使用 Object.assign 将混入对象的方法添加到 Person.prototype
Object.assign(Person.prototype, myMixin, myMixin2);
 
// 使用类
const person = new Person(18);
person.setName('Alice');
person.greet(); // 输出: Hello, my name is Alice
person.sayAge();

本文章已经生成可运行项目
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值