当前位置:   article > 正文

JavaScript Class类详解_js class

js class

        ECMAScript 6 提供了更接近传统语言的写法,新引入的class关键字具有正式定义类的能力。类(class)是ECMAScript中新的基础性语法糖结构,虽然ECMAScript 6类表面上看起来可以支持正式的面向对象编程,但实际上它背后使用的仍然是原型和构造函数的概念,让对象原型的写法更加清晰、更像面向对象编程的语法。

欢迎大家到我的掘金上访问,目前基本在掘金社区发展,前端农民晨曦 的个人主页 - 文章 - 掘金


一、类的定义

定义类也有两种主要方式:类声明和类表达式。这两种方式都使用class关键字加大括号:

  1. // 类声明
  2. class Person {}
  3. // 类表达式
  4. const TestPerson = class {}

注意:函数声明类声明之间的一个重要区别在于,函数声明会提升,类声明不会。需要先声明类,然后再访问它,否则就会出现ReferenceError,如:

  1. const test = new Person(); // ReferenceError: Person is not defined
  2. class Person {}

 另一个跟函数声明不同的地方是,函数受函数作用域限制,而类受块作用域限制:

  1. {
  2. function FunctionDeclaration () {}
  3. class ClassDeclaration {}
  4. // 使用var 声明
  5. var VarClass = class {}
  6. // 使用let/const 声明
  7. let LetClass = class {}
  8. }
  9. console.log(FunctionDeclaration) // FunctionDeclaration () {}
  10. console.log(ClassDeclaration) // ReferenceError: ClassDeclaration is not defined
  11. console.log(VarClass) // class {}
  12. console.log(LetClass) // ReferenceError: letClass is not defined

class 类完全可以看成构造函数的另一种写法,这种写法可以让对象的原型属性和函数更加清晰。

  1. class Person {}
  2. console.log(typeof Person) // function
  3. console.log(Person === Person.prototype.constructor) // true

上面代码表明,类的数据类型就是函数,类本身就指向构造函数。

二、类构造函数

constructor 方法是一个特殊的方法,这种方法用于创建和初始化一个由class创建的对象。通过 new 关键字生成对象实例时,自动会调用该方法。一个类只能拥有一个名为"constructor"构造函数,不能出现多个,如果定义了多个"constructor"构造函数,则将抛出 一个SyntaxError错误。如果没有定义"constructor"构造函数,class 会默认添加一个空的"constructor"构造函数。

  1. class Person {}
  2. // 等于
  3. class Person {
  4. constructor () {}
  5. }

使用new操作符实例化Person的操作等于使用new调用其构造函数。唯一可感知的不同之处就是,JavaScript解释器知道使用new和类意味着应该使用constructor函数进行实例化。

类必须使用new调用,否则会报错。这是它跟普通构造函数的一个主要区别,后者不用new也可以执行。

  1. class Person {}
  2. Person() // TypeError: Class constructor Test1 cannot be invoked without 'new'

使用new调用类的构造函数会执行如下操作。

  1. 在内存中创建一个新对象;
  2. 这个新对象内部的[[Prototype]]指针被赋值为构造函数的prototype属性;
  3. 构造函数内部的this被赋值为这个新对象(即this指向新对象);
  4. 执行构造函数内部的代码(给新对象添加属性);
  5. 如果构造函数返回非空对象,则返回该对象;否则,返回刚创建的新对象;

一起来看看下面例子:

  1. class Person {}
  2. class Test1 {
  3. constructor () {
  4. console.log('Test1 初始化')
  5. }
  6. }
  7. class Test2 {
  8. constructor () {
  9. this.test = '通过初始化构造函数设置值'
  10. }
  11. }
  12. // 构造函数返回指定对象
  13. const dataObj = { n: '自定义实例对象' }
  14. class Test3 {
  15. constructor () {
  16. this.test = '通过初始化构造函数设置值'
  17. return dataObj
  18. }
  19. }
  20. const a = new Person();
  21. const b = new Test1(); // Test1 初始化
  22. const c = new Test2();
  23. console.log(c.test) // 通过初始化构造函数设置值
  24. const d = new Test3();
  25. d instanceof Test3; // false
  26. console.log(d) // { n: '自定义实例对象' }

类实例化时传入的参数会用作构造函数的参数。如果不需要参数,则类名后面的括号也是可选的:

  1. class Person {
  2. constructor (...args) {
  3. console.log(args.length)
  4. }
  5. }
  6. class Test1 {
  7. constructor (test) {
  8. console.log(arguments.length)
  9. this.test = test || '默认值'
  10. }
  11. }
  12. // 不传值 可以省略()
  13. const a = new Person // 0
  14. const b = new Person('1', '2') // 2
  15. const c = new Test1() // 0
  16. console.log(c.test) // 默认值
  17. const d = new Test1('传入值') // 1
  18. console.log(d.test) // 传入值
  19. const d = new Test1('1', '2', '3') // 3
  20. console.log(d.test) // 1

与立即调用函数表达式相似,类也可以立即实例化:

  1. const a = new class Person {
  2. constructor (text) {
  3. this.text = text
  4. console.log(text)
  5. }
  6. }('立即实例化类');
  7. // 立即实例化类
  8. console.log(a); // Person

三、类的实例 、原型及类成员

类的语法可以非常方便地定义应该存在于实例上的成员、应该存在于原型上的成员,以及应该存在于类本身的成员。

实例的属性除非显式定义在其本身(即定义在this对象上),否则都是定义在原型上。

每个实例都对应一个唯一的成员对象,这意味着所有成员都不会在原型上共享:

  1. class Person {
  2. constructor (x, y) {
  3. this.text = new Number(1);
  4. this.x = x
  5. this.y = y
  6. this.getText = () => {console.log(this.text)}
  7. }
  8. toString () {
  9. console.log(`${this.x}, ${this.y}`)
  10. }
  11. }
  12. const test1 = new Person('x', 'y'), test2 = new Person('x2', 'y2');
  13. console.log(test1.getText()) // Number {1}
  14. console.log(test2.getText()) // Number {1}
  15. console.log(test1.x, test1.y) // x y
  16. console.log(test2.x, test2.y) // x2 y2
  17. // console.log(test1.text === test2.text) // false
  18. // console.log(test1.getText === test2.getText) // false
  19. test1.text = '测试'
  20. console.log(test1.getText()) // 测试
  21. console.log(test2.getText()) // Number {1}
  22. test1.toString() // x, y
  23. test2.toString() // x2, y2
  24. test1.hasOwnProperty('x'); // true
  25. test1.hasOwnProperty('y'); // true
  26. test1.hasOwnProperty('getText'); // true
  27. test1.hasOwnProperty('toString'); // false
  28. test1.__proto__.hasOwnProperty('toString'); // true
  29. // 类的实例共享同一个原型对象
  30. console.log(test1.__proto__ === test2.__proto__) // true
  31. // 也可以使用ES6提供的 Object.getPrototypeOf 来获取prototype
  32. const test1Prototype = Object.getPrototypeOf(test1)
  33. test1.myName = '共享字段'
  34. // test2 中也是能获取到
  35. console.log(test2.myName) // 共享字段

x、y、text和getText都是实例对象test1自身的属性,所以hasOwnProperty()方法返回true,而toString()是原型对象的属性(因为定义在Person类),所以hasOwnProperty()方法返回false,这些都与 ES5 的行为保持一致。

类的所有实例共享同一个原型对象。这也意味着,可以通过实例的__proto__属性或Object.getPrototypeOf方法获取原型为“类”添加方法,这将会出现共享情况,必须相当谨慎,不推荐使用,因为这会改变“类”的原始定义,影响到所有实例。

类方法等同于对象属性,因此可以使用字符串、符号或计算的值作为键:

  1. const symbolKey = Symbol('test')
  2. class Person {
  3. stringKey () {
  4. console.log('stringKey')
  5. }
  6. [symbolKey] () {
  7. console.log('symbolKey')
  8. }
  9. ['calculation' + '1'] () {
  10. console.log('calculation')
  11. }
  12. }
  13. const a = new Person();
  14. a.stringKey() // stringKey
  15. a[symbolKey]() // symbolKey
  16. a.calculation1() // calculation

getter 与 setter 

在 class 内部可以使用 get set 关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为。

  1. class Person {
  2. constructor (test) {
  3. this.test = test || '默认值'
  4. }
  5. get prop () {
  6. return this.test
  7. }
  8. set prop (value) {
  9. console.log(`setter prop value: ${value}`)
  10. this.test = value
  11. }
  12. }
  13. const p = new Person('1')
  14. p.prop // 1
  15. p.prop = '2' // setter prop value: 2
  16. p.prop // 2

set函数和get函数是设置在属性的 Descriptor 对象上的,可以通过 Object.getOwnPrototyDescriptor 来获取指定属性的指定描述对象。

  1. const descriptor = Object.getOwnPropertyDescriptor(Person.prototype, 'prop')
  2. 'get' in descriptor // true
  3. 'set' in descriptor // true

Generator 方法

如果某个方法之前加上星号(*),就表示该方法是一个 Generator 函数:

  1. class Person {
  2. constructor(...args) {
  3. this.args = args;
  4. }
  5. * generatorFun () {
  6. for (let arg of this.args) {
  7. yield arg;
  8. }
  9. }
  10. }
  11. const a = new Person(1,2,3,4);
  12. const generatorNext = a.generatorFun().next
  13. generatorNext() // {value: 1, done: false}
  14. generatorNext() // {value: 2, done: false}
  15. generatorNext() // {value: 3, done: false}
  16. generatorNext() // {value: 4, done: false}
  17. generatorNext() // {value: undefined, done: true}

this 指向

类的方法内部如果含有this,它默认指向类的实例。但是某些情况是指向当前执行环境;

  1. class Person {
  2. constructor () {
  3. this.text = '1'
  4. }
  5. getText () {
  6. console.log(this.text)
  7. }
  8. }
  9. const a = new Person()
  10. a.getText() // 1
  11. const {getText} = a
  12. // this 指向为undefined class 默认严格模式
  13. getText() // TypeError: Cannot read properties of undefined (reading 'text')

上面找不到 this 问题,this会指向该方法运行时所在的环境,因为 class 内部是严格模式,所以 this 实际指向的是undefined。有两个方法解决当前问题:

第一、构造方法中绑定this:

  1. class Person {
  2. constructor() {
  3. this.text = '1'
  4. this.getText = this.getText.bind(this)
  5. }
  6. getText () {
  7. console.log(this.text)
  8. }
  9. }

第二、使用箭头函数:

  1. class Person {
  2. constructor() {
  3. this.text = '1'
  4. }
  5. getText = () => {
  6. console.log(this.text)
  7. }
  8. }

箭头函数内部的 this 总是指向定义时所在的对象。

第三、使用proxy 在获取方法的时候自动绑定this:

  1. function classProxy (target) {
  2. const map = new Map()
  3. // 读取拦截配置, 只需要配置 get
  4. const hanlder = {
  5. get(target, key) {
  6. const val = Reflect.get(target, key)
  7. // 要获取的是函数执行, 如果不是函数就直接返回 val
  8. if (typeof val !== 'function') return val
  9. if (!map.has(val)) {
  10. // 使用 bind改变运行函数的 this为拦截的实例对象
  11. map.set(val, val.bind(target))
  12. }
  13. return map.get(val)
  14. }
  15. }
  16. const proxy = new Proxy(target, hanlder)
  17. return proxy
  18. }
  19. class Person {
  20. constructor (text) {
  21. this.text = text
  22. }
  23. getText () {
  24. console.log(this.text)
  25. return this.text
  26. }
  27. }
  28. const person = classProxy(new Person('test'))
  29. const { getText } = person
  30. getText() // test

三、静态方法、静态属性及静态代码块

静态方法、静态属性及静态代码块(proposal-class-static-block)都是使用 static 关键字定义的属性、方法或块只能 class 自己用,不能通过实例继承。

静态方法中的this 指向的是 当前类,而不是指向实例对象。静态属性是当前类自身的属性。

  1. class Person {
  2. static staticProp = 'Person静态属性'
  3. constructor () {
  4. // 通过 类名 获取
  5. console.log(`output: ${Person.staticProp}`)
  6. // 也可以通过 构造函数的属性
  7. this.constructor.staticFun1()
  8. }
  9. static staticFun1 () {
  10. this.staticFun2()
  11. console.log(`output: 静态方法staticFun1,获取Person静态属性 ==> ${Person.staticProp}`)
  12. }
  13. static staticFun2 () {
  14. console.log(`output: 静态方法staticFun2,获取静态属性 ==> ${this.staticProp}`)
  15. }
  16. }
  17. Person.staticProp // 静态属性
  18. Person.staticFun1()
  19. // output: 静态方法staticFun2,获取静态属性 Person静态属性
  20. // output: 静态方法staticFun1,获取Person静态属性 ==> Person静态属性
  21. const a = new Person() // output: Person静态属性
  22. a.staticProp // undefined
  23. a.staticFun1 // undefined
  24. a.staticFun2 // undefined
  25. // 通过其原型构造函数还是能获取到 这些静态属性及方法 不推荐使用
  26. // a.__proto__.constructor.staticProp
  27. // a.__proto__.constructor.staticFun1()

静态代码块:

是在 Class 内创建了一个块状作用域,这个作用域内拥有访问 Class 内部私有变量的特权,在这个代码块内部,可以通过 this 访问 Class 所有成员变量,包括 # 私有变量,且这个块状作用域仅在引擎调用时初始化执行一次 ,决解以前初始化静态类属性需要设置一个静态变量初始化逻辑。

注意: static 变量或代码块都按顺序执行,父类优先执行,一个类中允许多个静态代码块存在。

  1. class Person {
  2. static staticProp = '静态属性'
  3. static staticPropArr = []
  4. static staticPropObj = {}
  5. static getStatic (name) {
  6. console.log(`获取:${name}`, name && this[name])
  7. return name && this[name]
  8. }
  9. static resetData (name, data) {
  10. name && (this[name] = data)
  11. console.log(`重置:${name}`, name && this[name])
  12. }
  13. static {
  14. console.log('静态代码块执行');
  15. this.getStatic('staticProp');
  16. this.getStatic('staticPropArr');
  17. this.getStatic('staticPropObj');
  18. this.resetData('staticProp', '重置静态属性');
  19. this.resetData('staticPropArr', ['重置静态数组']);
  20. this.resetData('staticPropObj', { text: '重置静态对象' });
  21. this.staticPropObj.staticBlock1 = '代码块中直接设置'
  22. console.log(this.staticPropObj)
  23. }
  24. }
  25. /**
  26. * 静态代码块执行
  27. 获取:staticProp 静态属性
  28. 获取:staticPropArr []
  29. 获取:staticPropObj {}
  30. 重置:staticProp 重置静态属性
  31. 重置:staticPropArr ['重置静态数组']
  32. 重置:staticPropObj {text: '重置静态对象'}
  33. {text: '重置静态对象', staticBlock1: '代码块中直接设置'}
  34. */

上面代码中可以看出,static 关键字后面不跟变量,而是直接跟一个代码块,就是 class static block 语法的特征,在这个代码块内部,可以通过 this 访问 Class 所有成员变量,包括 # 私有变量。

在这里提前使用一下私有变量,理论上 class 私有变量外部是访问不了的,但是有了静态代码块(class-static-block)之后,我们可以将私有属性暴露给外部变量:

  1. let privateValue
  2. export class Person {
  3. #value
  4. constructor(x) {
  5. this.#value = x
  6. }
  7. static {
  8. privateValue = (obj) => obj.#x;
  9. }
  10. }
  11. export function getPrivateValue (obj) {
  12. return privateValue(obj)
  13. }
  14. // 在另一个文件中
  15. import { Person, getPrivateValue } from 'xxx'
  16. const a = new Person('私有变量')
  17. getPrivateValue(a) // 私有变量

其实class-static-block本质上并没有增加新功能,我们完全可以用普通静态变量代替,只是写起来很不自然,所以这个特性可以理解为对缺陷的补充,或者是语法完善,个人认为现在越来越像java。

四、私有属性和私有方法

私有属性和私有方法,是只能在类的内部访问的方法和属性,外部不能访问,不可以直接通过 Class 实例来引用,其定义方式只需要在方法或属性前面添加 #

私有属性:

  1. class Person {
  2. #privateVar1;
  3. #privateVar2 = '默认值';
  4. constructor (text) {
  5. this.#privateVar1 = text || '--'
  6. console.log(this.#privateVar1)
  7. }
  8. getPrivateData1 (key) {
  9. // 这里是获取不了的
  10. console.log('传入key来获取私有变量:', this[key])
  11. console.log('获取私有变量', this.#privateVar2, this.#privateVar1)
  12. }
  13. static staticGetPrivateData (person, key) {
  14. console.log('静态方法获取私有变量:', person.#privateVar2, person.#privateVar1)
  15. // 下面是获取不到
  16. console.log('静态方法传入key来获取私有变量:', person[key])
  17. }
  18. }
  19. const a = new Person() // 不传 默认 --
  20. // output: --
  21. a.getPrivateData1('#privateVar1')
  22. // output: 传入key来获取私有变量:undefined
  23. // output: 获取私有变量: 默认值 --
  24. // 使用静态方法
  25. Person.staticGetPrivateData(a, '#privateVar1')
  26. // output: 静态方法获取私有变量: 默认值 --
  27. // output: 静态方法传入key来获取私有变量:undefined

从上面代码中我们可以看到,私有变量是只能内部读取或写入,不能通过动态key读取(外部调用就会报错)

注意:在class 中 公共属性 test 与 #test 是两个完全不同的值;

私有方法:

  1. class Person {
  2. #private;
  3. constructor () {
  4. this.#private = '私有变量'
  5. this.#methods() // 调用私有方法
  6. }
  7. #methods () {
  8. console.log('私有方法#methods:', this.#private)
  9. }
  10. static #staticMethods (person) {
  11. if (person) {
  12. console.log('静态私有方法#staticMethods person获取值', person.#private)
  13. person.#methods()
  14. }
  15. }
  16. init1 () {
  17. this.#methods()
  18. console.log('使用this')
  19. Person.#staticMethods(this)
  20. }
  21. init2 (person) {
  22. if (person) {
  23. console.log('使用传入实例')
  24. Person.#staticMethods(person)
  25. }
  26. }
  27. }
  28. const a = new Person()
  29. // output: 私有方法#methods: 私有变量
  30. // a.#methods() SyntaxError
  31. // a['#methods']() TypeError: a.#methods is not a function
  32. a.init1()
  33. // output: 私有方法#methods: 私有变量
  34. // output: 使用this
  35. // output: 静态私有方法#staticMethods person获取值 私有变量
  36. // output: 私有方法#methods: 私有变量
  37. a.init2(a)
  38. // output: 使用传入实例
  39. // output: 静态私有方法#staticMethods person获取值 私有变量
  40. // output: 私有方法#methods: 私有变量

从上面代码中我们可以看到,私有方法只能内部调用,在外部调用就会报错。

五、继承 extends

使用 extends 关键字,让子类继承父类的属性和方法。

  1. class Person {
  2. num = 1
  3. text = 'person'
  4. getNum = () => console.log(this.num, this)
  5. addNum () {
  6. console.log(++this.num, this)
  7. }
  8. }
  9. // 继承
  10. class Child extends Person {
  11. constructor () {
  12. super()
  13. this.getText()
  14. }
  15. getText = () => console.log(this.text, this)
  16. }
  17. const a = new Child() // output: person Child {}
  18. console.log(a instanceof Child) // output: true
  19. console.log(a instanceof Person) // output: true
  20. a.getText() // output: person Child {}
  21. a.getNum() // output: 1 Child {}
  22. a.addNum() // output: 2 Child {}
  23. a.getNum() // output: 2 Child {}
  24. a.text // person
  25. a.num // 2

从上面代码中,我们可以看出Child 类 继承了 Person 的属性及方法,在Child 中也是可以调用Person的方法及属性,注意 this 的值会反映调用相应方法的实例或者类。子类中(Child)如果设置了 constructor 方法 就必须调用 super() ,否则就会出现新建实例时报错,如果没有 constructor 构造函数,在实例化继承类时会调用 super() ,而且会传入所有传给继承类的参数(后面会详细讲解)。

  1. class Person {
  2. static staticText = 'staticText';
  3. #private = 'private'
  4. static staticMethods1 (person) {
  5. console.log('staticMethods1', this)
  6. person.#privateMethods()
  7. }
  8. #privateMethods () {
  9. console.log('#privateMethods', this)
  10. }
  11. }
  12. // 使用表达式格式 也是可以使用 extends 继承
  13. const Child = class extends Person {
  14. methods () {
  15. console.log('methods', Child.staticText)
  16. }
  17. }
  18. const a = new Child()
  19. a.methods() // output: methods staticText
  20. Child.staticMethods1(a)
  21. // output: staticMethods1 class Child {}
  22. // output: #privateMethods Child {}
  23. Person.staticMethods1(a)
  24. // output: staticMethods1 class Person {}
  25. // output: #privateMethods Child {}

使用表达式格式 也是可以使用 extends 继承,类 的静态方法与属性是可以继承的,其私有属性及方法是不能继承的,可以从继承的共有方法与静态方法 中获取其私有属性或调用其私有方法。

 super  关键字可以作函数使用,也可以作对象使用,但是其只能在继承类中使用,且只能在继承类的constructor 构造函数、实例方法和静态方法中使用。作为函数时是在 继承类的constructor 构造函数中使用,根据要求如果继承类中定义了constructor构造函数就必须要调用super方法(调用父类的constructor),否则就会报错。

  1. class Person {}
  2. class Child extends Person {
  3. constructor () {
  4. // 如果不调用 super() 就会报错
  5. // ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor  
  6. super() // 调用父级的constructor
  7. console.log(this) // Child {}
  8. }
  9. }

注意: constructor() 中必须super() 顶部首段执行代码,否则也是一样报错;

在使用 super() 时应该注意下面几个问题:

  1. super只能在继承类构造函数和静态方法中使用。
    1. class Person {
    2. constructor () {
    3. // 在非继承类 的constructor 中使用super 会报错
    4. super() // SyntaxError: 'super' keyword unexpected here
    5. }
    6. methods () {
    7. console.log(super.text) // undefined
    8. }
    9. static staticMethods () {
    10. console.log(super.text) // undefined
    11. }
    12. }
  2.  不能单独引用super关键字,要么用它调用构造函数,要么用它引用静态方法。
    1. class Person {}
    2. class Child extends Person {
    3. constructor () {
    4. super // SyntaxError: 'super' keyword unexpected here
    5. }
    6. methods () {
    7. console.log(super) // SyntaxError: 'super' keyword unexpected here
    8. }
    9. static staticMethods () {
    10. console.log(super) // SyntaxError: 'super' keyword unexpected here
    11. }
    12. }
  3. 调用super()会调用父类构造函数,并将返回的实例赋值给this
    1. class Person {}
    2. class Child extends Person {
    3. constructor () {
    4. super()
    5. console.log(this instanceof Person) // output: true
    6. }
    7. }
    8. new Child()
  4. super() 的行为如同调用构造函数,如果需要给父类构造函数传参,则需要手动传入。
    1. class Person {
    2. constructor (text) {
    3. this.text = text
    4. }
    5. }
    6. class Child extends Person {
    7. constructor (text) {
    8. super(text)
    9. }
    10. }
    11. // 这里注意 其text 会设置到Child 中
    12. const a = new Child('设置 text') // Child { text: '设置 text' }
    13. console.log(a.text) // output: 设置 text
  5. 如果没有定义类构造函数,在实例化继承类时会调用super(),而且会传入所有传给继承类的参数。
    1. class Person {
    2. constructor (text) {
    3. this.text = text
    4. }
    5. }
    6. class Child extends Person {}
    7. const a = new Child('设置 text'); // Child { text: '设置 text' }
    8. // 上面提到过 会默认 生成 constructor (...arge) {super(...arge)}
  6. 在类构造函数中,不能在调用super()之前引用this,文章上面已经有案例及说明。
  7. 如果在继承类中显式定义了构造函数,则要么必须在其中调用super(),要么必须在其中返回一个对象。
    1. class Person {
    2. methods () {}
    3. }
    4. class Child1 extends Person {}
    5. class Child2 extends Person {
    6. constructor () {
    7. super()
    8. }
    9. }
    10. class Child3 extends Person {
    11. constructor () {
    12. return {}
    13. }
    14. }
    15. const a = new Child1() // Child1 {}
    16. const b = new Child2() // Child2 {}
    17. const c = new Child3() // {} 指向 实例函数 返回的对象

    关于JS  Class 相关就介绍到这里,当然还有 Class的 mix-ins 混入及其他class相关知识,这边就不详细介绍了,有兴趣的同学可以自己去了解一下。

    文章内容参考了 《JavaScript高级程序设计》(第4版)https://book.douban.com/subject/35175321/

         掘金地址:
                JavaScript Class类详解 - 掘金

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号