ES6—Class的基本语法

简介

类的数据类型是函数,类本身指向构造函数

  1. 普通的ES语法
function Point(x, y) {
  this.x = x
  this.y = y
}

Point.prototype.toString = function () {
  return `(${this.x}, ${this.y})`
}
  1. Class改写
class Point {
	// 构造方法
  constructor(x, y) {
    this.x = x
    this.y = y
  }
	// toString方法
  toString() {
    return `(${this.x}, ${this.y})`
  }
}

  1. 类的所有方法都定义在类的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 {}
}

私有方法

  1. _bar方法前面的下划线表示这是一个只限于内部使用的私有方法,但类的外部仍可以调用
/**
 * 通过变通方法来模拟实现
 */
class Widget {
  // 公有方法
  foo(baz) {
    this._bar(baz)
  }

  // 私有方法
  _bar(baz) {
    return (this.snaf = baz)
  }
}
  1. 将私有方法移出模块,因为模块内部的所有方法都是对外可见的
class Widget {
  foo(baz) {
    bar.call(this, baz)
  }
}

function bar(baz) {
  return (this.snaf = baz)
}

  1. 利用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)
  }
}

私有属性

  1. 在属性名之前,使用#来表示
class Point {
  #x
  constructor(x = 0) {
    this.#x = +x
  }

  get x() {
    return this.#x
  }
  set x(value) {
    this.#x = +value
  }
}

  1. 编写私有方法
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的指向

  1. 类的方法内部如果含有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. 解决方法1: 在构造方法中绑定this
class Logger {
  constructor() {
    this.printName = this.printName.bind(this)
  }
}

  1. 解决方法2: 用箭头函数
class Logger {
  constructor() {
    this.printName = (name = 'there') => {
      this.print(`Hello ${name}`)
    }
  }
}
  1. 解决方法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的静态方法

  1. 类相当于实例的原型,所有在类中定义的方法都会被实例继承。在一个方法前面加上static就表示该方法不会被实例继承,直接通过类调用。
class Foo {
  static classMethod() {
    return 'hello'
  }
}

Foo.classMethod() // hello

  1. 父类的静态方法可以被子类继承
class Foo {
  static classMethod() {
    return 'hello'
  }
}

Foo.classMethod() // hello

class Bar extends Foo {}
Bar.classMethod()  //  hello

  1. 静态方法也可以从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属性

  1. 返回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, '张三')  // 报错
  1. 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)
  1. 利用以上特点可以写出不能独立而必须继承后才能使用的类
// 这里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)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值