class Point {}
typeof Point // "function"
Point === Point.prototype.constructor // true
类的内部所有定义的方法,都是不可枚举的
class Point {
constructor() {}
toString() {}
}
Object.keys(Point.prototype)
// []
Object.getOwnPropertyNames(Point.prototype)
// ["constructor","toString"]
const Point = function (x, y) {}
Point.prototype.toString = function () {}
Object.keys(Point.prototype)
// ["toString"]
Object.getOwnPropertyNames(Point.prototype)
// ["constructor","toString"]
不存在变量提升
new Foo() // ReferenceError
class Foo {}
没有显式定义,一个空的constructor方法会被默认添加
class Point {}
// 等同于
class Point {
constructor() {}
}
通过new命令生成对象实例时,自动调用该方法,返回实例对象(即this),当然也可以返回另一个对象
class Foo {
constructor() {
return Object.create(null)
}
}
new Foo() instanceof Foo // false
class MyClass {
get prop() {
return 'getter'
}
set prop(value) {
console.log(`setter: ${value}`)
}
constructor() {}
}
const inst = new MyClass()
inst.prop = 123
// setter: 123
inst.prop
// 'getter'
Object.getOwnPropertyNames(Object.getPrototypeOf(inst))
// ["constructor","prop"]
const methodName = 'getArea'
const propName = 'propName'
class Square {
constructor(length) {
this[propName] = length
}
[methodName]() {}
}
与函数一样,类也可以使用表达式的形式定义。
const MyClass = class Me {
getClassName() {
return Me.name
}
}
// Me只在 Class 的内部可用,在 Class 外部,这个类只能用MyClass引用
// 也可以简写
const MyClass = class { /* ... */ }
立即执行的 Class
const person = new class {
constructor(name) {
this.name = name
}
sayName() {
console.log(this.name)
}
}('张三')
person.sayName() // 张三
类相当于实例的原型,所有在类中定义的方法,都会被实例继承。 如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。
class Foo {
static classMethod() {
return 'hello'
}
}
Foo.classMethod() // 'hello'
const foo = new Foo()
foo.classMethod()
// TypeError: foo.classMethod is not a function
如果静态方法包含this关键字,这个this指的是类,而不是实例
class Foo {
static bar() {
this.baz() // 相当于Foo.baz()
}
static baz() {
console.log('hello')
}
baz() {
console.log('world')
}
}
Foo.bar() // hello
父类的静态方法,可以被子类继承
class Foo {
static classMethod() {
return 'hello'
}
}
class Bar extends Foo {}
Bar.classMethod() // 'hello'
静态方法也是可以从super对象上调用的
class Foo {
static classMethod() {
return 'hello'
}
}
class Bar extends Foo {
static classMethod() {
return `${super.classMethod()}, too`
}
}
Bar.classMethod() // "hello, too"
静态属性指的是 Class 本身的属性,即Class.propName,而不是定义在实例对象(this)上的属性
// 老写法
class Foo {
// ...
}
Foo.prop = 1
// 新写法
class Foo {
static prop = 1
}
私有方法和私有属性,是只能在类的内部访问的方法和属性,外部不能访问
方法一:
class Widget {
foo(...args) {
bar.call(this, ...args)
}
}
function bar() {}
方法二:
const bar = Symbol('bar')
class MyClass {
[bar]() {}
};
但是高手还是拿的到
const inst = new MyClass()
console.log(inst[Reflect.ownKeys(MyClass.prototype)[1]])
// ƒ [bar]() {}
ES2022正式为class添加了私有属性
class Foo {
#a
#b
constructor(a, b) {
this.#a = a
this.#b = b
}
#sum() {
return #a + #b
}
printSum() {
console.log(this.#sum())
}
}
从 Chrome 111 开始,开发者工具里面可以读写私有属性,不会报错,原因是 Chrome 团队认为这样方便调试
不管在类的内部或外部,读取一个不存在的私有属性,会报错(公开属性如果不存在,会返回undefined)
class Counter {
#xValue = 0
get #x() {
return this.#xValue
}
set #x(value) {
this.#xValue = value
}
constructor() {
console.log(this.#x)
}
}
class Foo {
#privateValue = 42
static getPrivateValue(foo) {
return foo.#privateValue
}
}
Foo.getPrivateValue(new Foo()) // 42
class FakeMath {
static PI = 22 / 7
static #totallyRandomNumber = 4
static random() {
console.log('I heard you like random numbers…')
return FakeMath.#computeRandomNumber()
}
static #computeRandomNumber() {
return FakeMath.#totallyRandomNumber
}
}
FakeMath.PI // 3.142857142857143
FakeMath.random()
// I heard you like random numbers…
// 4
FakeMath.#totallyRandomNumber // 报错
FakeMath.#computeRandomNumber() // 报错
判断某个对象是否为类的实例
class C {
#brand
static isC(obj) {
try {
obj.#brand
return true
} catch {
return false // 访问不存在的私有属性会报错
}
}
}
ES2022 改进了in运算符,使它也可以用来判断私有属性
class C {
#brand
static isC(obj) {
if (#brand in obj) {
// 私有属性 #brand 存在
return true
} else {
// 私有属性 #foo 不存在
return false
}
}
}
允许在类的内部设置一个代码块,在类生成时运行且只运行一次,主要作用是对静态属性进行初始化
比如我想初始化静态属性
class C {
static x = 234
static y
static z
constructor() {
// 这里写,坏处是每次实例化都会执行
}
}
// 类外部写,坏处是将类的内部逻辑写到了外部
try {
const obj = doSomethingWith(C.x)
C.y = obj.y
C.z = obj.z
} catch {
C.y = 'y'
C.z = 'z'
}
采用静态块
class C {
static x = 234
static y
static z
// 只运行一次
// 只能访问之前声明的静态属性
// 不能有return语句
// 可以使用类名或this
static {
try {
const obj = doSomethingWith(this.x)
this.y = obj.y
this.z = obj.z
} catch {
this.y = 'y'
this.z = 'z'
}
}
}
class Rectangle {
static staticWidth = 20
// 实例属性 可写可不写
height = 0
width = 0
// 原型链上的Getter
get area() {
return this.calcArea()
}
// constructor
constructor(height, width) {
// 实例属性(必须定义在类的方法里)
this.height = height
this.width = width
}
// 静态方法(不需要实例化该类,不能通过实例调用静态方法)
static calcGirth(height, width) {
return height * 2 + width * 2
}
static getDoubleStaticWidth() {
return this.staticWidth * 2 // this表示类构造函数
}
// 原型链上的方法
calcArea() {
return this.height * this.width
}
getDoublePrototypeWidth() {
return this.prototypeWidth * 2 // this表示类实例,此时prototypeWidth从原型链继承过来
}
}
// 原型的数据属性(必须定义在类定义的外面)
Rectangle.prototype.prototypeWidth = 25
const square = new Rectangle(10, 10)
console.log(square.area) // 100
console.log(square.calcArea()) // 100
console.log(square.calcGirth(10, 10)) // square.calcGirth is not a function
console.log(Rectangle.calcGirth(10, 10)) // 40
console.log(Rectangle.getDoubleStaticWidth()) // 40
console.log(square.getDoublePrototypeWidth()) // 50