Class的基本语法
简介
类的数据类型是函数,类本身指向构造函数
- 普通的ES语法
function Point(x, y) {
this.x = x
this.y = y
}
Point.prototype.toString = function () {
return `(${this.x}, ${this.y})`
}
- Class改写
class Point {
// 构造方法
constructor(x, y) {
this.x = x
this.y = y
}
// toString方法
toString() {
return `(${this.x}, ${this.y})`
}
}
- 类的所有方法都定义在类的prototype属性上
class Point {
constructor() {}
toString() {}
toValue() {}
}
// 等同于
Point.prototype = {
constructor() {},
toString() {},
toValue() {},
}
严格模式
类和模块的内部默认使用严格模式
constructor方法
constructor方法是类的默认方法,提供new命令生成对象实例时自动调用该方法
类的实例对象
class Point {
constructor(x, y) {
this.x = x
this.y = y
}
toString() {
return this.x + this.y
}
}
var point = new Point(1,3);
// toString是原型对象的属性
point.hasOwnProperty('toString') // false
point._proto_.hasOwnProperty('toString') // true
// 类的所有实例共享一个原型对象
var p1 = new Point(2,3)
var p2 = new Point(3,2)
p1._proto_ === p2._proto_ // true
可以通过实例的_proto_属性为类添加方法
p._proto_.printName = function(){ }
Class 表达式
Class也可以使用表达式的形式定义
const MyClass = class Me {
getClassName() {
return Me.name
}
}
const my = new MyClass()
// Me只在Class的内部代码可用
my.getClassName() // Me
不存在变量提升
类不存在变量提升(hoist)
// 如果有提升变量,class会被提升到头部,let命令不提示,就会报错
{
let Foo = class {}
class Bar extends Foo {}
}
私有方法
_bar
方法前面的下划线表示这是一个只限于内部使用的私有方法,但类的外部仍可以调用
/**
* 通过变通方法来模拟实现
*/
class Widget {
// 公有方法
foo(baz) {
this._bar(baz)
}
// 私有方法
_bar(baz) {
return (this.snaf = baz)
}
}
- 将私有方法移出模块,因为模块内部的所有方法都是对外可见的
class Widget {
foo(baz) {
bar.call(this, baz)
}
}
function bar(baz) {
return (this.snaf = baz)
}
- 利用Symbol值的唯一性将私有方法的名字命名为一个Symbol值
const bar = Symbol('bar')
const snaf = Symbol('snaf')
export default class myClass {
// 公有方法
foo(baz) {
this[bar](baz)
}
// 私有方法
[bar](baz) {
return (this[snaf] = baz)
}
}
私有属性
- 在属性名之前,使用
#
来表示
class Point {
#x
constructor(x = 0) {
this.#x = +x
}
get x() {
return this.#x
}
set x(value) {
this.#x = +value
}
}
- 编写私有方法
class Foo {
#a
#b
#sum() {
return this.#a + this.#b
}
printSum() {
console.log(this.#sum())
}
constructor(a, b) {
this.#a = a
this.#b = b
}
}
this的指向
- 类的方法内部如果含有this,它将默认指向类的实例
class Logger {
printName(name = 'there') {
this.print(`Hello ${name}`)
}
print(text) {
console.log(text);
}
}
const logger = new Logger();
logger.printName() // Hello there
const { printName } = logger;
// 提取出来单独使用,this会指向该方法运行的环境
printName() // TypeError: Cannot read property 'print' of undefined
- 解决方法1: 在构造方法中绑定this
class Logger {
constructor() {
this.printName = this.printName.bind(this)
}
}
- 解决方法2: 用箭头函数
class Logger {
constructor() {
this.printName = (name = 'there') => {
this.print(`Hello ${name}`)
}
}
}
- 解决方法3: 使用proxy
function selfish(target) {
const cache = new WeakMap()
const handler = {
get(target, key) {
const value = Reflect.get(target, key)
if (typeof value !== 'function') {
return value
}
if (!cache.has(value)) {
cache.set(value, value.bind(target))
}
return cache.get(value)
},
}
const proxy = new Proxy(target, handler)
return proxy
}
const logger = selfish(new Logger())
name属性
name属性总是返回紧跟class关键字后面的类名
class Point {}
Point.name // Point
Class的取值函数(getter)和存值函数(setter)
/**
* getter 和 setter
*/
class MyClass {
constructor() {}
get prop() {
return 'getter'
}
set prop(value) {
console.log(`setter: ${value}`)
}
}
let inst = new MyClass()
inst.prop = 123 // setter: 123
console.log(inst.prop) // getter
Class的Generator方法
方法之前加上*
,表示该方法是个Generator函数
class Foo {
constructor(...args) {
this.args = args
}
// 返回一个Foo类的默认遍历器,for...of循环会自动调用
*[Symbol.iterator]() {
for (const arg of this.args) {
yield arg
}
}
}
for (const x of new Foo('hello', 'world')) {
console.log(x);
}
Class的静态方法
- 类相当于实例的原型,所有在类中定义的方法都会被实例继承。在一个方法前面加上
static
就表示该方法不会被实例继承,直接通过类调用。
class Foo {
static classMethod() {
return 'hello'
}
}
Foo.classMethod() // hello
- 父类的静态方法可以被子类继承
class Foo {
static classMethod() {
return 'hello'
}
}
Foo.classMethod() // 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本身的属性,即Class.propname,不是定义在实例对象(this)的属性
Class内部只有静态方法,没有静态属性
class Foo {}
Foo.prop = 1
Foo.prop // 1
Class的实例属性
class ReactCounter extends ReactCounter.Component {
state
constructor(props) {
super(props)
this.state = {
count: 0,
}
}
}
Class的静态属性
Class的静态属性只要在上面的实例属性前加static关键字
class MyClass {
static myStaticProp = 42
constructor() {
console.log(MyClass.myStaticProp) // 42
}
}
new.target属性
- 返回new命令所作用的构造函数,如果构造函数不是通过new命令调用的,则new.target会返回undefined
function Person(name) {
if (new.target !== undefined || new.target === Person) {
this.name = name
} else {
throw new Error('Please create instance by new method')
}
}
var person = new Person() // 正确
var per = Person.call(person, '张三') // 报错
- Class内部调用new.target,返回当前Class(
子类继承父类会返回子类
)
class Rectangle {
constructor(length, Width) {
console.log(new.target === Rectangle)
this.length = length
this.width = this.width
}
}
var obj = new Rectangle(1, 2)
- 利用以上特点可以写出不能独立而必须继承后才能使用的类
// 这里Shape类不能实例化,只能用于继承
class Shape {
constructor() {
if (new.target === Shape) {
throw new Error('本类不能实例化')
}
}
}
class Rectangle extends Shape {
constructor(length, width) {
super()
}
}
var x = new Shape()
var y = new Rectangle(3, 4)