《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
    • 直接当作函数调用,表示父类构造函数
    • 如果直接当作对象,则表示父类的原型(用于子类需要调用父类原型上的方法,或者获取原型上的值)
相关推荐
入秋2 小时前
Three.js 实战之电子围栏可根据模型自动生成
前端·前端框架·three.js
用户6120414922132 小时前
jsp+servlet做的咖啡品牌管理后台
java·前端·后端
Asort2 小时前
JavaScript设计模式(三)——抽象工厂模式 (Abstract Factory)
前端·javascript·设计模式
nyf_unknown3 小时前
(vue)前端下载本地excel文件
前端·vue.js·excel
fcm193 小时前
(6) tauri之前端框架性能对比
前端·javascript·rust·前端框架·vue·react
今晚务必早点睡3 小时前
前端缓存好还是后端缓存好?缓存方案实例直接用
前端·后端·缓存
双普拉斯3 小时前
微信小程序通用弹窗组件封装与动画实现
javascript·html5
IT_陈寒4 小时前
Vue3性能优化:5个被低估的Composition API技巧让我打包体积减少了40% 🚀
前端·人工智能·后端
x007xyz4 小时前
🚀🚀🚀前端的无限可能-纯Web实现的字幕视频工具 FlyCut Caption
前端·openai·音视频开发