3

参考链接:https://segmentfault.com/a/11...
参考链接:https://segmentfault.com/a/11...

什么是类


在面向对象的编程中 类是一个用于创建对象 为状态(成员变量) 和行为实现(成员函数或方法)提供初始值的可扩展程序代码模板

在实际开发中,我们往往需要创建很多相同类型的对象,如用户、商品或其他对象。我们知道,通过new一个function可以创建一个对象,但在现代的JavaScript里,有一个更高级的“类”结构,对于面向对象编程提供了一些很不错的特性。

基本语法

class MyClass{
       constructor(){}
       method1(){}
       method2(){}
       method3(){}
     }

然后通过new MyClass来创建一个拥有以上方法的对象实例 与此同时 通过new操作符 构造方法(constructor)是被自动调用的 ,这意味着在构造方法中我们可以做一些初始化的工作

class User{
      constructor(name){
        this.name = name
      }
      showInfo(){
        alert(this.name)
      }
    }
  var user = new User('WhiteCode') //当new的时候 创建了一个新的对象  构造方法通过给定的参数运行,并为其分配this.name
  user.showInfo()
  //类方法之间是没有逗号的

类是函数的一种

class User{ //创建一个名为User的函数 该函数将成为类声明的结果
      constructor(name){
        this.name = name
      }
      showInfo(){ //在User.prototype中储存所有方法 例如showInfo
      //对于新对象 当我们调用一个方法的时候, 它就是从原型中获取 因此新的User对象就可以访问到类方法了
        alert (this.name)
      }
    }
    alert(typeof User)//function
    alert(User === User.prototype.constructor) //true
    alert(User.prototype.showInfo)//showInfo(){  alert (this.name) }
      // there are exactly two methods in the prototype
    alert(Object.getOwnPropertyNames(User.prototype)) //constructor,showInfo

ES5 与ES6的比较

es5
function People(name,age){
      this.name = name
      this.age = age
    }
    People.prototype.say = function(){
      console.log("hello")
    }
    People.see = function(){
      alert("how are you")
    }
    var a = new People()
    a.say()
    
    
    .通过es6的class声明类
    class People{
    constructor(name,age){
      this.name = name
      this.age = age
    }
    see(){ alert('how are you')}
    say(){
      console.log('hello')
    }
  }
    var a = new People()
    a.say()

①People是一个类,也是一个函数;②constructor是一个对象指向的是People函数,该函数还挂了name和age属性;③将say函数挂载People的原型上。

大多数JavaScript引擎中的类构造函数的字符串表示形式都以“class ...”开头。

类方法是不可枚举的。对于原型中的所有方法,类定义将enumerable标志设置为false。
类总是使用严格模式的。这意味着类构造中的所有代码都自动处于严格模式。

与常规函数不同,如果没有new,则无法调用类构造函数

class User{
    constructor(){}
  }
  alert(typeof User);//function
  User();// Error: Class constructor User cannot be invoked without 'new'

就像函数一样,类可以在另一个表达式中定义,传递,返回,分配等。

let User = class{  //与命名函数表达式类似,类表达式可能有也可能没有名称
      showInfo(){
        alert('hello')
      }
    }
    new User().showInfo()

我们可以'按需'动态创建类

function makeClass(rass){
    // declare a class and return it
    return class{
      showInfo(){
        alert(rass)
      }
    }
  }
  // Create a new class
  let User = makeClass('hello')
  new User().showInfo()//hello

Getters/Setters

// 类可能包括getter/setter,生成器,计算属性等 这里通过使用 get/set来实现user.name
  class User{
    constructor(name){
      this._name =name
    }
    get name(){
      return this._name
    }
    set name(value){
      if(value.length<4){
        alert('名字长度不够')
        return
      }
      this._name = name
    }
  }
  let user = new User('biubiu')
  alert(user.name)
  user = new User()//名字长度不够'

在User的原型对象中, 通过类声明创建get/set

Object.defineProperties(User.prototype,{
    name:{
      get(){
        return this._name
      },
      set(name){
        // ...
      }
    }
  })
class User {
      name = "see"
      sayHi(){
        alert(`hello,${this.name}!`)
      }
    }
    new User().sayHi()
// 该属性未放入User的原型中 相反 它是由new创建的 分别为每一个对象创建,因此该属性永远不会在同一个类的不同的对象之间共享

在Class内部可以使用get和set关键字

对某个属性设置存值函数和取值函数,拦截该属性的存取行为

class MyClass {
      constructor(){
        // ...
      }
      get prop(){
        return 'getter';
      }
      set prop(value){
        console.log('setter:'+ value)
      }
    }
     let inst = new MyClass()
     inst.prop = 124  //setter:124
     var a = inst.prop
     console.log(a) //getter
    //  prop属性有对应的存值函数和取值函数,因此赋值和读取行为都被自定义了

存值函数和取值函数是设置在属性descriptor对象上的

class CustomHTMLElment {//存值函数和取值函数是设置在属性descriptor对象上的
     constructor(element){
       this.element = element
     }
     get html(){
       return this.element.innerHTML
     }
     set html(value){
       this.element.innerHTML = value
     }
   }
  //  Object.getOwnPropertyDescriptor 如果指定的属性存在于对象上 则返回其属性描述符对象,否则返回underfined
   var descriptor = Object.getOwnPropertyDescriptor(CustomHTMLElment.prototype,'html')
   console.log(descriptor) //{enumerable: false, configurable: true, get: ƒ, set: ƒ}
  //  存值函数和取值函数是定义在html属性的描述对象上面
  console.log('get' in descriptor)//true
  console.log('get' in descriptor)//true

Es6中Class私有和受保护的属性及方法

在JavaScript中,有两种类型的对象字段(属性和方法):

公共的:随处都可访问,它们包含外部接口,我们在开发中一直常用的也就是公共的属性和方法了
私有的:仅在类的内部可访问,主要用于内部接口

class MBWCar{
     oilAmount = 0
     constructor(power){
       this.power = power
       alert(`Created a mbw-car,power: ${power}`)
     }
   }
   let mbwCar = new MBWCar(100)
   mbwCar.oilAmount = 200
  //  从上面的代码可以看出oilAmount与power这两个属性是公共的, 我们可以在外部轻易的设置以及获取他们
  // 将oilAmount属性更改为protected以对其进行更多的控制

受保护的属性通常以下划线_为前缀
这不是在语言层面强制去执行,但咱们程序员之间有一个众所周知的惯例,即不应该从外部访问这些属性和方法
例如,我们不希望任何人将其设置为零以下。

class MBWCar{
    _oilAmount = 0
    constructor(power){
      this._power = power
    }
    set oilAmount(value){
      if (value<0) throw new Error('Negative oil')
      this._oilAmount = value
    }
    get oilAmount(){
        return  this._oilAmount
    }
  }
  let mbwCar = new MBWCar(100)
  mbwCar.oilAmount = -10 //Error Negative oil
  console.log(mbwCar)  //现在访问受到控制,因此将油量设置为零以下将失败。

我们可以把power属性设置为只读属性 不可修改其值

class MBWCar{
      constructor(power){
        this._power = power
      }
      get power(){
        return this._power
      }
    }
    let mbwCar = new MBWCar(100)
    alert(`Power is: ${mbwCar.power}W`)
    mbwCar.power = 25//Error (no set)

getter/setter方法

class Car{
      _oilMount = 0
      setOilMount(value){
        if(value<0)throw Error('Negative oil')
        this._oilMount = value
      }
      getOilMount(){
        return this._oilMount
      }
    }
    let car = new Car()
    car.setOilMount(100)
    alert(car.getOilMount())
//  受保护的属性是可以继承的
// 如果我们继承类Car扩展Car 那么没有什么能够阻止我们从新的方法中访问this._oilMount 或this._power属性  所以受保护的属性是可以继承的

私有字段

这是js新增的一个针对类属性的语法,js引擎目前部分支持 需要进行polyfilling
私有的私有字段与#开头仅在类的内部可进行访问

class Car{
    #oilLimit = 100
    #checkOil(value){
      if(value>0) throw Error('Negative oil')
      if(value > this.#waterLimit) throw new Error('Too much oil')
    }
  }
  let car = new Car()
  car.#checkOil() //error
  car.#waterLimit = 1000 //Error

  class BWMCar extends Car{
    methods(){
      alert(this.#oilLimit) // Error: can only access from Car
    }
  }

私有字段不能通过this[name]去访问

class User{
    sayHi(){
      let filedName = 'Darkcod'
      alert(`Hello,${this.filedName}`)
    }
  }
  new User().sayHi()

1 面向对象编程最重要的原则之一 -从外部接口划分内部接口 这就涉及到属性和方法的可访问范围度
2 在面向对象的编程中, 可通过private protected public 来对类的属性和方法进行限制,例如你从你父亲那里继承了一些属性,但你父亲那里继承了一些属性 但你的父亲其他属性不想被你继承到等
3 在javascript中 受保护的属性和方法以_开头 私有的属性和方法名以#开头
4 面向对象编程的一个较大的好处之一 是我们不必理解其内部实现 依然可以很好的去进行编程开发


HappyCodingTop
526 声望847 粉丝

Talk is cheap, show the code!!