关于阅读 ES6- Class整理的问题

1、简述类

答:① 使用class关键字进行定义。
② 作用是用来作为生成实例对象的模板,代替ES5中使用构造函数来生成实例对象。
③ class是一个语法糖,让对象原型的写法更加清晰及更像面向对象的编程语法。
④ 类是构造函数的另一种写法,类的数据类型是函数,类本身就是指向构造函数,使用new关键字进行创建实例,同一个类new出来的实例,他们的__proto__是一样的

function P(x, y){
  this.x = x
  this.y = y
}
P.prototype.run = function(){console.log('run方法')}
var p = new P(1, 2)
// 用class类的方式可改写为下面形式
class P{
  constructor(x, y){
    this.x = x
    this.y = y
  }
  run(){console.log('run 方法')}
}
let p = new P(1, 2)
let p1 = new P(2,3)
typeof P // 'function'
P === P.prototype.constructor ===  p.contructor // true
p.contructor === P.prototype.constructor // true
p.prototype  === P.prototype.prototype // true
p.__proto__ === p1.__proto__ // true

2、简述类中添加方法

答:① 类中添加的方法都是不可枚举的,使用Object.keys(B.prototype)是查询不到的,可以通过Object.getOwnPropertyNames(B.prototype)。
② 可以通过Object.assgin()添加方法也可以直接在类中定义方法
③ ES6中的Class类中的toString方法是不可枚举,ES5中构造函数的原型对象上的toString方法是可枚举的
④ 类中的属性名可以用表达式,也可以使用字面量

let a = 'aaaaa'
class P{
 constructor(){}
  run(){} // 在类中直接使用字面量定义方法
  [a](){} // 表达式方式定义方法
}
Object.assign(P.prototype, {song(){}, dance(){}}) // 此方式添加类方法

3、类中constructor方法

答:① 是类中的默认方法,是必须的。
② 如果类中没有显示定义constructor方法,那么一个空的constructor()方法会被默认添加。
③ 当使用new关键字创建对象实例的时候,会自动调用这个方法。
④ constructor方法默认返回实例对象(this),用户可以自己指定返回另一个对象

4、类的实例属性和原型属性

答:定义在本身(this)上的属性是实例属性
定义在原型对象上(class)的属性是原型属性

class P{
  constructor(x){this.x = x} // x是实例属性
  toString(){} // toString是原型对象上的属性
}
let p = new P(1)
p.aaa = 222 // aaa是实例属性
p.hasOwnProperty('x') // true
p.hasOwnProperty('aaa') // true
p.hasOwnProperty('toString') // false
p.__proto__.hasOwnProperty('toString') // true

5、类中设置取值函数和存值函数

答:① 在类内部可以使用set 和 get关键字,对某个属性设置存值函数和取值函数,在函数内拦截属性的存取过程
② 存值函数和取值函数设置在属性的Descriptor对象上

class P{
  constructor(){}
  get num(){return 'getter'}
  set num(){console.log(123)}
}
let p = new P()
p.num// 'getter'
p.num= 444 // 123
let des = Object.getOwnPropertyDescriptor(P.prototype, 'num')
'get' in des // true
'set' in des // true

6、类声明的方式有哪些

答:可以直接声明,也可以使用表达式进行声明

class P{} // P.name的结果为 'P'
const P = class{} // P.name的结果为 'P'
const P = class PP{} // P.name的结果为 'PP'
let p = class P{
  constructor(num){this.num = num}
  say(){console.log(this.name)}
}(123) //立即执行的Class
p.say() // 123

7、使用类的注意事项

答:① 类和模块内部默认严格模式
② 类不存在变量提升(hoist),这样保证了继承的规范性
③ 类的name属性总是返回紧跟在class关键字后的类名
④ 类中可以定义Generator函数方法
⑤ 类内部的方法如果有this,默认指向类的实例。所以解构类中的方法,方法中的this会指向该方法运行时的环境,进而报错。可以在构造函数中绑定该方法的this

class P{
 constructor(){
   this.name = 111
    this.run = this.run.bind(this) // 方法绑定实例(this),不然下面的解构会出错
  // 上面可用箭头函数实现  this.run  = () => this
  }
  run(){ console.log(this.name) }
}
let {run} = new P()

8、简述类的静态方法

答:① 在类中,方法前面加上static关键字,则该方法不会被实例继承,只能通过类自己调用,这种方法叫做静态方法。
② 静态方法中的this指的是类本身,而不是实例
③ 静态方法可以和非静态方法重名
④ 父类的静态方法可以被子类进行继承,在子类中通过super进行调用父类的静态方法

class Father{
  constructor(){}
  run(){console.log(this)} // 这里的this是类的实例
  static run(){console.log(this)} // 这里的this是类本身
}

class Child extends Father{
  static dance(){
    console.log(super.run()) // 通过super关键字调用父类的静态方法
  }
}
var f = new Father()
f.run() // 执行实例方法run
Father.run() // 执行类的静态方法

9、类中实例属性定义在哪里

答:可以定义在类的最顶层,也可以定义在constructor方法的this上

class F{
  a = 1
  constructor(){
    this.b = 2
    console.log(this.a, this.b)
  }
}

10、类的静态属性怎么定义

答:直接用 类名.静态属性名 的方式进行定义,或者在类中使用static关键字进行定义

class F{
   static n = 22 // 类的静态属性
}
F.num = 111// 类的静态属性

11、类的私有属性和私有方法怎么定义

答:私有属性和方法指的是只有在类的内部可以访问到的属性和方法。定义有四种解决方法,如下
① 在命名上加以区别,如用下划线+变量名方式(_bar),该方法不可靠。

class F{
 _dance(){}
}
② 将私有方法移出类
class F{
  run(){dance.bind(this)}
}
function dance(){} // 私有方法
③ 使用Symbol定义私有属性或方法的名字
class F{
 [Symbol('dance')](){}
}
④ 使用 #+变量名来定义私有属性
class F{
 #dance(){}
}

12、如何判断对象是否具有某个私有属性

答:① 使用try…catch判断 try{obj.#foo}catch{console.log(该私有属性不存在)}
② 在类的内部使用 V8引擎提供的in运算符 #foo in obj
③ 注意 in运算符只能用在类的内部来检测是否具有私有属性,并且只对其实例返回true
④ 子类可以从父类那里继承(extends)私有属性,也可以使用in进行判断.
⑤ 通过 Object.create() Object.setPrototype()方法进行继承的,是不会进行私有属性的继承

class FF{
  #foo = 1
  static test(f){console.log(#foo in f)}
}
class F extends FF{}
class B{#foo = 22}
var ff = new FF()
var ff1 = Object.create(ff)
var ff2 = {}
Object.setPrototypeOf(ff2, ff)
FF.test(ff) // true  只对本身的实例才返回true
FF.test(ff1) // false  Object.create继承无效
FF.test(ff2) // false Object.setPrototypeOf继承无效
FF.test({})  // false   只对非本身的实例或对象无效
FF.test(new B()) // false  只对非本身的实例或对象无效
FF.test(new F()) // true  子类继承父类私有属性

13、类的静态块

答:① 主要是对静态属性进行初始化的地方。如果是静态属性直接写在类的顶部,那么每次创建实例,就会进行初始化一遍,比较耗费内存
② 在类中调用静态属性或者方法可以使用this也可以使用类本身去调用
③ 静态块可以将私有属性与类的外部代码进行分享

class F{
  #a = 11 // 私有属性
  static{
    static foo = 1  // 静态属性
    static num = 2  // 静态属性
    console.log(this.foo)  // 调用静态属性
    console.log(F.num)  // 调用静态属性
  }
}

14、类中的new.target属性作用

答;① 用来判断这个构造函数或者类的实例化是通过new关键字或者是Reflect.constructor()调用的
② new.target不能再函数外进行使用,会报错

function P(){
  if(new.target !== undefined){ // 如果new.target不等于构造函数本身
       console.log('是通过new关键字进行实例化的')
  }else{
       console.log('必须使用new命令生成实例')
  }
}

class F{
   constructor(){
       if(new.target === F){ // 如果new.target不等于构造函数本身
       console.log('是通过new关键字进行实例化的')
  }else{
       console.log('必须使用new命令生成实例')
  }
   }
}
class Child extends F{
     constructor(){}
}
var p = Object.create(P) // '必须使用new命令生成实例'
var f = new F() // '是通过new关键字进行实例化的'
var c = new C() // '必须使用new命令生成实例'

15、Class的继承

答:① 通过extends关键字实现继承,子类继承父类的属性和方法(除了私有属性和私有方法),父类的私有属性和方法可以借助父类的方法传递给子类
② 子类中可以通过super表示父类的构造函数,可用来新建一个父类的实例对象
③ ES6中规定,子类的constructor方法中必须调用super(),否则会报错。因为子类的this对象必须先通过父类的构造函数完成塑造,进而得到父类相同的属性和方法,再子类自己进行自我加工,添加自己的属性和方法。
④ 子类中只有调用了super()方法,才能使用this关键字,否则报错
⑤ 子类中如果没有显示定义constructor方法,那么类中会默认添加,并且在constructor方法中默认调用 super()
⑥ 可以通过Object.getPrototypeOf(子类)获取子类的父类

class F{}
class Child extends F{
   #foo = 1 // 私有属性,不能被子类直接拿到
   constructor(){
      this.a = 2 // 报错
      super() // 如果没有这行语句,那么 new Child()创建实例就会报错
      this.x = 1 // 正确  
   }
   getFoo(){return this.#foo} // 私有属性可以借助方法传递给子类
}

16、ES5和ES6中的继承机制有什么不同吗

答:① ES5中是先创建子类的实例对象,然后再将父类的方法添加到这个对象上,即‘实例在前,继承在后’
② ES6中是先将父类的属性和方法添加到一个空对象上,然后将这个对象作为子类的实例,即‘继承在前,实例在后’

class F{
   constructor(){
      console.log(1)
   }
}
class Child extends F{
   constructor(){
      super() 
      console.log(2)
   }
}
let c = new Child() // 先输出 1,后输出2.子类构造函数调用super()时,会执行一次父类构造函数
c instanceof Child // true
c instanceof F // true

17、简述类中super关键字

答:① super关键字既可以当函数来使用,也可以当对象来使用。
② super作为函数的时候,代表父类的构造函数,super()内部的this指向当前类,而不是父类
③ super()作为函数只能用在子类的构造函数constructor()中,否则报错
④ super作为对象的时候,在子类的普通方法中super指向父类的原型对象,在子类的静态方法中super指向父类
⑤ super作为对象的时候,在子类的普通方法中,方法内部this是当前类的实例,在子类的静态方法中,方法内部this是当前类
⑥ 在使用super关键字的时候,要显示的指定它是对象还是函数,不然会报错

class F {
  constructor() {
    this.x = 1
    console.log(new.target.name);
  }
}
class Child extends F {
  constructor() {
    super(); //super()内部的this指向当前类 Child,而非父类F
    console.log(super.x) ; // 1
    console.log(super) // 报错,要显示指定super是对象还是函数
  }
}
new F() // 此时 new.target.name指的是 F
new Child() // 此时 new.target.name指的是 Child

18、类的__proto__属性和prototype属性

答:① ES5中每个对象都有__proto__属性,指向对应的构造函数的prototype属性。
② Class中,子类的__proto__属性指向父类,表示构造函数的继承
③ Class中,子类prototype属性的__proto__属性指向父类的prototype属性,表示方法的继承

class F{}
class Child extends F{}
let f = new F()
let c = new Child()
Child.__proto__ === F // true
Child.prototype.__proto__ === F.prototype
f.__proto__ === c.__proto__.__proto__  // true

19、Mixin是什么

答:指的是多个对象合并成一个新对象,新对象具有各个对象的接口。
可以使用扩展运算符实现。

let a = {name: 1}
let b = {num: 0}
let c = {...a, ...b} // {name: 1, num: 0}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值