共计 5218 个字符,预计需要花费 14 分钟才能阅读完成。
参考链接: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 面向对象编程的一个较大的好处之一 是我们不必理解其内部实现 依然可以很好的去进行编程开发