(七) class类

1. ES5中的

在es5中, 我们约定俗成的将首字母大写的函数称为构造函数, 我们可以通过构造函数来进行实例化, 在其他高级语言中, 我们或多或少都看过这样的说明: 是一类事物的抽象, 而对象的具体表现, 但是在es5中, 并没有类的概念, 而是将构造函数 认为, 并通过原型绑定的方式来实现共有属性和方法的继承

示例

function Person(name) {
  this.name = name
}

Person.prototype.sayName = function () {
  console.log(this.name)
}

var p1 = new Person('猫13')
var p2 = new Person('猫14')

console.log(p1.sayName)   // 猫13
console.log(p2.sayName)   // 猫14

为了改变这种缺陷: 你说你是面向对象语言, 却连真正的类都没有实现, 这有点说不过去了吧, 所以, 在es6中, 引入了Class(类)的概念

2. Class(类)

我们先来看一下Class的基本使用语法:

示例 1

// class关键字
class Person {
  // 构造方法 constructor
  constructor(name) {
    this.name = name
  }
  // 类方法
  sayName() {
    this.name = this.name || '猫14'
    console.log(this.name)
  }
}

let p = new Person('猫13')
p.sayName()		// 猫13

其中constructor 方法是类的默认方法, 不管在声明类的时候有没有显式创建, 而当我们通过new关键字创建实例对象时, 就会自动调用constructor方法

class Person {
  constructor() {
    console.log(111111)
  }
}

new Person()    // 111

我们来看一个有意思的地方

// 代码为 示例1
console.log(typeof Person)  // function
Person.prototype.sayName()	// 猫14

我们使用class定义的类竟然是一个function类型, 而且Person的原型上竟然也有sayName方法 !

对比最开始我们再es5中使用构造函数创建 "类" 的方式, 我们发现: 其实es6中的class只是对es5的一种封装, 说白了, class只是一个语法糖而已 , 虽然如此, class的封装至少让我们不必再像以前那样繁琐, 并且还有了自身独特的新特性

2.1 class中属性和方法的本质

class Fn {
  // name 相当于 es5的 this.name, 是成员属性
  name = '猫13'
	// say方法是定义在Fn的prototype上, 与es5一样
	say() {
  	return 100
	}
}

let f = new Fn()
console.log(f.name)  // 猫13	
console.log(Fn.prototype.name)	// undefined
console.log(Fn.prototype.say()) // 100

2.2 class的新特性

  • 类声明不会被提升, 与let声明类似
  • 类内部有一个默认的方法constructor, 通过关键字new创建实例时会自动调用该方法, 返回实例对象, 即this
  • 类内部定义的所有方法,都是不可枚举的
  • 创建实例必须使用new关键字, 否则会报错
  • 类中的所有代码都处于严格模式下

2.3 class表达式

与函数一样, class也可以使用表达式的形式来定义

let Person = class {
  constructor(name) {
    this.name = name
    console.log(this.name)
  }
}

new Person('猫13')

2.4 this问题


class的新特性之一: 内部默认使用严格模式

class Person {
  callFn() {
    this.fn()
  }

  fn() {
    console.log('测试');
  }
}

let p = new Person()
// p.callFn()  // 测试
let f = p.callFn
f()   // Cannot read property 'fn' of undefined

严格模式下, 直接调用函数(在全局或者其他函数内), this将保持进入执行环境的值,所以,如果没有指定环境,则默认undefined

function test() {
  console.log(this) // undefined    
  (function(){
     console.log(this) // undefined
  })
}
test() 

这也是上面调用f为什么会报错的原因, 因为this的值是undefined, 你再去调用函数, 就相当于

function fn() {}

undefined.fn()	

@解决办法

bind

class Person {

  // 通过bind提前绑定好this指向
  constructor() {
    this.callFn = this.callFn.bind(this)
  }

  callFn() {
    this.fn()
  }

  fn() {
    console.log('测试');
  }
}

let p = new Person()
let f = p.callFn
    f()   // 测试

箭头函数

class Person {
	 
  // 箭头函数
  callFn = () => {
    this.fn()
  }

  fn() {
    console.log('测试');
  }
}

let p = new Person()
let f = p.callFn
f()   // 测试

通过proxy

2.5 静态属性和方法

使用static关键字声明, 定义的静态属性和方法, 是类属性和方法, 只能通过类名.属性名/方法名的方式调用

class Person {
  static name
  static sayName() { }
}
Person.name
Person.sayName()

2.6 私有属性和方法 (es7)

只能在类的内部使用, 不对外暴露

class Math {
  #count = 0
  #sum() {
  return ++this.#count
}

callSum() {
  return this.#sum()
}
}

let math = new Math()
//  
// console.log(math.#sum())   #sum' must be declared in an enclosing class
console.log(math.callSum())  // 1
posted @ 2021-08-02 22:39  只猫  阅读(149)  评论(0编辑  收藏  举报