javascript 类 class

类的本质是函数

class Father {}
console.dir(Father)
​
function Parent() {}
console.dir(Parent)
​
// Father 和 Parent 不管从结构还是原型,几乎一模一样,所以类的本质是函数(尤其指构造函数),类只是一种语法糖。
​
console.log(typeof Father) // function

 

成员变量(属性)

class Father {
    constructor(a){
        this.a = a
        this.b = 'b'
    }
}
console.dir(Father) // 没有 a 和 b 属性
​
function Parent(a){
    this.a = a
    this.b = 'b'
}
console.dir(Parent) // 没有 a 和 b 属性
​
console.log(new Father('爸爸')) // 有 a 和 b 属性
console.log(new Parent('父亲')) // 有 a 和 b 属性
​
// 类的成员变量(属性)通过 constructor 方法初始化,跟构造函数的属性几乎一样。
// 类的成员变量是属于对象的,不属于类。

 

成员方法

class Father {
  show(){
    console.log('show...')
  }
}
console.dir(Father) // show 方法在 Father.prototype下
​
function Parent(){}
Parent.prototype.show = function(){
  console.log('show...')
}
console.dir(Parent) // show 方法在 Parent.prototype下
​
// 类的成员方法跟构造函数原型上的方法几乎一样
// 不同点:
// 1、类的成员方法只有一个 __proto__ 原型,而构造函数的方法还是跟普通函数一样,拥有两个原型(prototype 和 __proto__)。
// 2、类的成员方法是不可遍历的,方法的 enumerable 特征为 false,而构造函数的方法正好相反。

 

每个对象拥有独立的成员变量(属性),原理同构造函数。

每个对象拥有公共的成员方法,原理同构造函数的原型方法。

 

静态属性

class Father {
  static a = 'a'
}
console.dir(Father) // 有 a 属性
​
function Parent(a){}
Parent.a = 'a'
console.dir(Parent) // 有 a 属性
​
console.log(new Father()) // 没有 a 属性
console.log(new Parent()) // 没有 a 属性
​
// 静态属性属于类本身,不属于对象。

 

静态方法

class Father {
  static show(){
    console.log('show...')
  }
}
console.dir(Father) // 有 show 方法
​
function Parent(){}
Parent.show = function(){
  console.log('show...')
}
console.dir(Parent) // 有 show 方法
​
console.dir(new Father()) // 没有 show 方法
console.dir(new Parent()) // 没有 show 方法
​
// 静态方法属于类本身,不属于对象。

 

静态方法的应用

class User {
  constructor(name, age, sex) {
    this.name = name
    this.age = age
    this.sex = sex
  }
​
  static create(...args) { // 用展开运算符,接收任意多个参数
    return new this(...args) // 返回 new User(...args),此处可用 this 代指 User
  }
}
​
const user = User.create('张三', 11, '男') // 静态方法创建对象
console.log(user)

 

访问器

不使用访问器

class User {
  setEmail(email){
    if (email) {
      console.log(`email is ${email}`)
    } else {
      throw new Error('email is error')
    }
  }
}
const user = new User()
user.setEmail('aa@qq.com') // 普通的成员方法,一个在 __proto__ 下的普通函数。

使用访问器

class User {
    set email(email){
        if (email) {
            console.log(`email is ${email}`)
        } else {
            throw new Error('email is error')
        }
    }
}
console.dir(User) // 在 prototype 上有一个 email 方法,本质就是普通的成员方法
const user = new User()
console.log(user) // user 对象的原型 __proto__ 上有一个 email 方法,user 可以调用这个方法
user.email = 'aa@qq.com' // 以设置属性的方式使用 email 方法
console.log(user) // user 对象上并没有增加 email 属性,说明虽然是以属性设置的方式,但本质上还是方法调用

使用访问器需要避免属性和方法重名

class User {
    constructor(email) {
        this.email = email
    }
​
    set email(email) {
        if (email) {
            this.email = email
        } else {
            throw new Error('email is error')
        }
    }
}
​
const user = new User()
user.email = 'aa@qq.com'
// 当属性和方法重名时,一方面在调用 user.email 时,js 引擎无法分辨这个 email 是属性还是方法,另一方面,在调用 email 方法时,如果设置成功,那么就相当于执行了 this.email = email,那么这里又会触发访问器,进入无限循环。
​
// 一般采用 data 属性来封装普通属性,类似 vue 和小程序的 data 属性。
class User {
    constructor() {
        this.data = {
            email: ''
        }
    }
​
    set email(email) {
        if (email) {
            this.data.email = email
        } else {
            throw new Error('email is error')
        }
    }
}
​
const user = new User()
user.email = 'aa@qq.com'
console.log(user)

可以用 get 来获取

class User {
    constructor() {
        this.data = {
            email: ''
        }
    }
​
    set email(email) {
        if (email) {
            this.data.email = email
        } else {
            throw new Error('email is error')
        }
    }
​
    get email() {
        return this.data.email
    }
}
​
const user = new User()
user.email = 'aa@qq.com'
console.log(user.email) // aa@qq.com

 

属性保护

命名规则保护属性

class Website {
    _site = 'https://www.baidu.com'
}
// 使用约定俗成的命名规则,在变量前面加下划线,说明这是一个私有属性。
// 没有实质性的保护效果,不推荐

使用 Symbol 定义 protected 属性

  const HOST = Symbol('site') // 利用 Symbol 定义变量的不重复特性,定义一个不重复的变量
​
  class Website {
    constructor(a) {
      this.data = {
        a: a,
        [HOST]: 'https://www.baidu.com' // 初始化,用中括号括起来的意思是,解析 HOST 变量所存储的字符串,以这个字符串作为属性名
      }
    }
    // 由于 Symbol 定义的变量不能直接拿来访问,因此对象必须通过访问器来访问 protected 属性
    set host(host) {
      this.data[HOST] = host
    }
​
    get host() {
      return this.data[HOST]
    }
  }
​
  console.dir(Website)
  const website = new Website('aaa')
  website.host = 'https://www.qq.com'
  console.log(website.host) // https://www.qq.com
// 当需要多个 protected 属性时
const protecteds = Symbol('protecteds')
​
class Website {
    constructor(a) {
        this.data = {
            a: a,
        }
        this[protecteds] = {
            host: 'https://www.baidu.com'
        }
    }
​
    set host(host) {
        this[protecteds].host = host
    }
​
    get host() {
        return this[protecteds].host
    }
}
​
const website = new Website('aaa')
website.host = 'https://www.qq.com'
console.log(website)

使用 WeakMap 定义 protected 属性

// 需要用到模块化,不好演示

private 私有属性

class Website {
  #password = '123456'
​
  set password(password) {
    this.#password = password
  }
​
  get password() {
    return this.#password
  }
}
​
const website = new Website()
console.log(website)
website.password = '000000'
console.log(website.password) // 000000
console.log(website.#password) // Uncaught SyntaxError: Private field '#password' must be declared in an enclosing class // 私有属性错误

 

类的继承

class Father {
}
​
class Son extends Father {
}
​
console.dir(Son)
// 打印发现,类的继承其实就是原型链继承,Father 挂载在 Son 的 prototype 上。

 

super

class Father {
  show() {
    console.log('show father')
  }
}
​
class Son extends Father {
  constructor() {
    super() // 必须先调用父类的构造函数,否则会报派生类错误
    super.show() // 调用父类的 show 方法
    this.show() // 调用自己的 show 方法
  }
​
  show() {
    console.log('show son')
  }
}
​
new Son()
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值