likes
comments
collection
share

Javascript:Class构造函数

作者站长头像
站长
· 阅读数 21

为什么需要class

在其他语言中class已经是一个早就被实现的功能,在JavaScript中一直到ES6被实现。在class没有实现之前我们是这样写的(如下代码)

function Person(name,sex){
    this.name = ''
    this.sex='
}
Person.prototype.say=function(){
    alert('hello')
}

有了class我们就这么写的,是不是看起来更简洁。甚至对于初学者来说都不需要了解什么是prototype

class Person {
  constructor (name,sex) {
    this.name = name;
    this.sex = sex;
  }
  say() {
    alert('hello')
  }
}

个人认为的是:在没有class这个声明之前,可以使用es5来模拟。但是在一定程度上并不是规范。如果新手或者通过其他语言学习者看到这个prototype什么的就比较懵。有了这个class关键字让js做面向对象的设计更完善了

class是什么

定义:类关键字Class 和 constructor 构造函数

类是用于创建对象的模板。 用 class 关键字来创建一个类,类体在一对大括号 {} 中,我们可以在大括号 {} 中定义类成员的位置,如方法或构造函数。 每个类中包含了一个特殊的方法 constructor(),它是类的构造函数,这种方法用于创建和初始化一个由 class 创建的对象。

class ClassName {
    constructor() { ... } 
}
//定义好类后,我们就可以使用 new 关键字来创建对象:
let site = new ClassName();

创建对象时会自动调用构造函数方法 constructor()。还可以这样创建对象

// 未命名/匿名类
let Runoob = class {
  constructor(name, url) {
    this.name = name;
    this.url = url;
  }
};
console.log(Runoob.name);
// output: "Runoob"
 
// 命名类
let Runoob = class Runoob2 {
  constructor(name, url) {
    this.name = name;
    this.url = url;
  }
};
console.log(Runoob.name);
// 输出: "Runoob2"

// 自执行的类
let Runoob =new class {
  constructor(name, url) {
    this.name = name;
    this.url = url;
  }
}('name','111');
console.log(Runoob)

constructor 构造方法

构造方法是一种特殊的方法:

  • 构造方法名为 constructor()。
  • 构造方法在创建新对象时会自动执行。
  • 构造方法用于初始化对象属性。
  • 如果不定义构造方法,JavaScript 会自动添加一个空的构造方法。

static 静态方法

类(class)通过static关键字定义静态方法。

静态方法调用直接在类上进行,不能在类的实例上调用。但是父类的静态属性和静态方法,会被子类继承

静态方法通常用于创建实用程序函数。

class A {
  static foo = 100;
  static hello() {
    console.log('hello world');
    console.log(A.foo)
  }
}
A.hello()
const a=new A(); // typeError: a.hello is not a function
a.hello()
class B extends A {
}

B.hello()  // hello world

注意,静态属性是通过软拷贝实现继承的。

class A { static foo = 100; }
class B extends A {
  constructor() {
    super();
    B.foo--;
  }
}

const b = new B();
B.foo // 99
A.foo // 100

上面示例中,foo是 A 类的静态属性,B 类继承了 A 类,因此也继承了这个属性。但是,在 B 类内部操作B.foo这个静态属性,影响不到A.foo,原因就是 B 类继承静态属性时,会采用浅拷贝,拷贝父类静态属性的值,因此A.fooB.foo是两个彼此独立的属性。

但是,由于这种拷贝是浅拷贝,如果父类的静态属性的值是一个对象,那么子类的静态属性也会指向这个对象,因为浅拷贝只会拷贝对象的内存地址。

class A {
  static foo = { n: 100 };
}

class B extends A {
  constructor() {
    super();
    B.foo.n--;
  }
}

const b = new B();
B.foo.n // 99
A.foo.n // 99

上面示例中,A.foo的值是一个对象,浅拷贝导致B.fooA.foo指向同一个对象。所以,子类B修改这个对象的属性值,会影响到父类A

私有方法和属性

目前 class 的私有属性特性已经进入了 Stage3 实验阶段,通过 Babel 已经可以使用,并且 Node v12,chrome 中也增加了对私有属性的支持,但这并不妨碍我们用 JS 的现有功能实现一个私有属性特性,以加深对这一概念的理解。

    class Person {
        _count = 100
        // 私有属性外部不可直接设置值,直接访问
        // 变量污染,不能设置同名属性  #userName
        #user_Name; // 实例上的私有属性,不能直接修改值,不能遍历
        static #userName; // 类的静态私有属性,不可继承,无法直接修改,只能通过类内部方法修改
        static names = []

        get value() {
            return this._count
        }

        set value(num) {
            this._count = num
        }

        constructor(name) {
            this.name = name
            this.#user_Name = name
            Person.#userName = '1111'
            Person.names.push(name)
        }

        userName() {
            console.log(Person.#userName)
            console.log(this.#user_Name)
        }

        setUserName(name) {
            Person.#userName = name
            this.#user_Name = name
            Person.names.push(name)
        }

        sayHi() {
            console.log(`我是可以直接设置的名字${this.name}`)
        }
    }

    //
    const myPerson = new Person('用户1')
    // Object.setPrototypeOf(myPerson,null)
    myPerson.sayHi()
    console.log('Person', myPerson)
    const myPerson2 = new Person('用户2')
    // Object.setPrototypeOf(myPerson,null)
    myPerson2.sayHi()
    console.log('Person', myPerson2)
  • 私有属性外部不可直接设置值,直接访问
  • 变量污染,不能设置同名属性 #userName
  • #user_Name; // 实例上的私有属性,不能直接修改值,不能遍历
  • static #userName; // 类的静态私有属性,不可继承,无法直接修改,只能通过类内部方法修改

Javascript:Class构造函数

继承extends和super

  • 关键字用于创建一个类,该类是另一个类的子类。
  • 子类继承了另一个类的所有方法。
  • 继承对于代码可重用性很有用:在创建新类时重用现有类的属性和方法。
  • super() 方法引用父类的构造方法。
  • 通过在构造方法中调用 super() 方法,我们调用了父类的构造方法,这样就可以访问父类的属性和方法。
class childClass extends parentClass

super 关键字用于访问和调用一个对象的父对象上的函数。。

在构造函数中使用时,super关键字将单独出现,并且必须在使用 this 关键字之前使用。super 关键字也可以用来调用父对象上的函数。

第一种情况,super作为函数调用时,代表父类的构造函数。ES6 要求,子类的构造函数必须执行一次super函数。

class A {}

class B extends A {
  constructor() {
    super();
  }
}

第二种情况,super作为对象时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类。

class A {
  p() {
    return 2;
  }
}

class B extends A {
  constructor() {
    super();
    console.log(super.p()); // 2
  }
}

let b = new B();

使用的时候注意事项

  1. 只能new使用,不能作为方法直接调用会报错
  2. this调用的是时指向
  3. 类方法不可枚举

和ES5的区别和相同点

  1. 与 ES5 不同,类不存在变量提升
  2. 类的调用必须要使用 new 命令,否则会报错
  3. 底层还是ES5原型链实现(可称作语法糖)
  4. 实例的__proto__ 指向 类的prototype

Javascript:Class构造函数