《javascript进阶-类(class):构造函数的语法糖》

一、传统构造器函数遇到的问题

  • 属性和原型方法定义隔离,降低了可读性

    比如以下代码;

javascript 复制代码
function User (name,age) {
    this.name = name;
    this.age = age;
}
//定义实例,原型方法
User.prototype.getUserInfo = function () {
    console.log(this.name,this.age)
}
const user = new User()
user.getUserInfo()

上面代码需要定义示例方法,但是需要在后面原型上去定义,这样的写法隔离,影响可读性

  • 原型成员可以被枚举

    使用for...in...原型成员也被枚举出来了

javascript 复制代码
function User (name,age) {
    this.name = name;
    this.age = age;
}
//定义实例,原型方法
User.prototype.getUserInfo = function () {
    console.log(this.name,this.age)
}
const user = new User()
for(const key in user){
    console.log(key)
}
// name
// age
// getUserInfo

大多数情况是不需要遍历出原型上的属性的

  • 默认情况下,构造函数仍然可以被当作普通函数使用

以上的这些问题都是需要在es6中解决的,因此es6做了很大的改进

二、class基本语法糖

2.1 语法

上面案例可以使用class语法糖改进

kotlin 复制代码
class User {
    constructor (name,age) {
        this.name = name;
        this.age = age;
    }                                    //constructor等于上面的构造函数User
    
    getUserInfo(){
         console.log(this.name,this.age) //添加原型方法
     }
}

2.2 类的特点

  • 类声明不会被提升,与let和const一样,存在暂时性死区
  • 类中的所有代码均在严格模式下执行
  • 类的所有方法都是不可枚举的 赋值的方法除外,因为使用=赋值后的属性挂载到了属性本身上而不是属性原型上
kotlin 复制代码
    class User {
    constructor (name,age) {
        this.name = name;
        this.age = age;
    }                                    //constructor等于上面的构造函数User
    
    getUserInfo=()=>{
         console.log(this.name,this.age) //添加原型方法
     }
}
const user = new User()
for(const key in user){
    console.log(key)
}
  • 类所有的方法都无法被当作构造函数来使用

依旧是赋值的方法除外

ini 复制代码
class User {
   constructor (name,age) {
       this.name = name;
       this.age = age;
   }                                    //constructor等于上面的构造函数User
   
   getUserInfo= function(b){
       this.b = b
    }
}
const user = new User()
const a = new user.getUserInfo(123)
console.log(a)
  • 类的构造器必须使用 new 来调用

三、类的其他书写方式

3.1 可计算的属性名

kotlin 复制代码
const name = "getUserInfo"
class User {
    constructor (name,age) {
        this.name = name;
        this.age = age;
    }                                    //constructor等于上面的构造函数User
    
    [name](){
         console.log(this.name,this.age) //添加原型方法
     }
}
//调用
const user = new User()
user[name]()

3.2 getter和setter

在es5的中使用 Object.defineProperty 定义一个属性的读取和设置(改变属性为存储器属性),现在可以使用这两个语法糖处理

csharp 复制代码
const name = "getUserInfo"
class User {
    constructor (name,age) {
        this.name = name;
        this.age = age;
    }                                    //constructor等于上面的构造函数User
    //给age赋值运行这个方法
    set age(){}
    //获取age的时候运行这个方法
    get age(){}
}
//调用
const user = new User()
user[name]()

3.3 静态成员

什么是静态成员:构造函数本身的成员,例如

csharp 复制代码
function User (){

}
User.test = "test11"
//或者

class User {
}
User.test = "test111"

name就是静态成员

一张图表示清楚

graph TD A[构造函数,构造函数本身的成员称为静态成员] --> |prototype|B[构造函数的原型,这上面的属性也称为实例成员] A[构造函数] -->|new|C[通过new对象,此时里面的属性为实例成员] C -->|__proto__|B

由上图可知,静态成员只能通过构造函数访问。

那么静态成员什么时候会使用到呢?

如下有个例子,比如现在生产一辆车,车的名字叫宝马,获取车的名字,则需要先生产车再获取名字

javascript 复制代码
class Car {
    constructor(){
        this.name = "baoma"
    }
}

const car = new Car()

car.name

显示是不合理的,因此静态成员就可以让这些环节变得简单

javascript 复制代码
class Car {
    static name = "baoma"
    constructor(){
        
    }
}
Car.name

不用生产车就知道车的名字了

注意不仅仅是属性可以变成静态成员,方法也可以是静态成员

3.4 属性初始化(es2016提供)

当class创建时,进行初始化赋值不再像下面这样赋值

kotlin 复制代码
class Car {
    constructor(){
        this.name = "baoma"
    }
}

可以直接=赋值

ini 复制代码
class Car {
    name = "baoma"
}

3.5 类表达式

arduino 复制代码
const Test = class {}  //匿名表达式

3.6 装饰器(es2016)、Decorator

使用场景

  • 当某些方法过时
javascript 复制代码
class Test {
        print(){}    // 方法过时了,需要给个提示
}

语法 @装饰器名称 + 装饰器名称函数

javascript 复制代码
class Test {
        @Obsolete
        print(){}    // 方法过时了,需要给个提示
}
function Obsolete(target,methodName,descriptor){
        console.log(target,methodName,descriptor)
        //function Test  , print  ,{value:function print(){}}
        const oldFunc = descriptor.value
        descriptor.value = functiong (...args){
        console.log(`${methodName}方法已经过时了`)
        oldFunc.apply(this,args)
    }
}

目前浏览器暂时还不支持,但是后续typescript和babel可以让编译后让它支持

四、类的继承

怎么判断是否需要继承

如果A和B两个类,可以描述为B是A,则A和B形成继承关系,A就是B的父类,B会自动拥有A中的所有实例成员。

scala 复制代码
class Animal {
    constructor(type,name,age,sex){
        this.type = type;
        this.name = name;
        this.age = age;
        this.sex = sex;
    }
}
class Dog extends Animal {
    constructor(name,age,sex){
        super("dog",name,age,sex)
    }
}

注意es6要求,如果定义了constructor,并且该类是子类,则必须在constructor的第一行手动调用父类构造函数super()

如果子类不写constructor,则会有默认的构造器,该构造器需要的参数和父类一致,并且自动调用父类构造器super()

关键字

  • extends:继承,用于类的定义
  • supper
    • 直接当作函数调用,表示父类构造函数
    • 如果直接当作对象,则表示父类的原型(用于子类需要调用父类原型上的方法,或者获取原型上的值)
相关推荐
浪裡遊27 分钟前
Nivo图表库全面指南:配置与用法详解
前端·javascript·react.js·node.js·php
課代表33 分钟前
JavaScript 二维数组的三种定义与初始化方法
javascript·初始化·二维数组·多维数组·动态数组·循环遍历·数组合并
鸡吃丸子1 小时前
Next.js 入门指南
开发语言·javascript·next.js
罚时大师月色2 小时前
Vue+ts 如何实现父组件和子组件通信
javascript·vue.js·ecmascript
漂流瓶jz2 小时前
快速定位源码问题:SourceMap的生成/使用/文件格式与历史
前端·javascript·前端工程化
samroom2 小时前
iframe实战:跨域通信与安全隔离
前端·安全
fury_1232 小时前
vue3:数组的.includes方法怎么使用
前端·javascript·vue.js
weixin_405023372 小时前
包资源管理器NPM 使用
前端·npm·node.js
宁&沉沦2 小时前
Cursor 科技感的登录页面提示词
前端·javascript·vue.js