js面试题(二)

52 阅读3分钟

1.es5继承有哪些方式?

原型链继承

<!-- 父类 -- >
function Super(name) {
    this.name = name
}
Super.prototype = {
    name: '我要走走看看',
    age: 18,
    hobby: { a: '篮球' },
}
 <!-- 子类 -- >
function Sub() {}

Sub.prototype = new Super() // 子类的原型等于父类的实例

const sub = new Sub()
console.log(sub.age) // 18

缺点:

  1. 子类无法想父类的构造函数传参.  new Sub( name )父类也接收不到
  2. 子类修改父类原型中引用类型的值时,原型的属性发生改变,导致之后的实例的值都发生改变
const sub1 = new Sub()
const sub2 = new Sub()
sub1.hobby.a = '羽毛球'  // 这里修改了sub1的hobby的值
console.log(sub1.hobby) // { a: '羽毛球' }
console.log(sub2.hobby) // { a: '羽毛球' }

借用构造函数继承

// 父类
function Super(name) {
    this.name = name
}
Super.prototype = {
    name: '我要走走看看',
    age: 18,
    hobby: { a: '篮球' },
}
// 子类
function Sub(name) {
    Super.call(this,name)// 通过  call / apply 继承父类构造函数内的属性和方法
}
const sub = new Sub('猫咪')
console.log(sub.name)  // 猫咪
console.log(sub.age)   // undefined

优点 :

  1. 解决了子类实例向父类传参的缺点
  2. 解决了子类实例修改父类原型属性或者方法的问题
  3. 可以继承多个构造函数属性(call多个)。

缺点:

  1. 无法继承父类原型的方法和属性
  2. 每个新实例都有父类构造函数的副本,无法复用,臃肿。

组合继承 (j原型链继承 + 构造函数继承 )

// 父类
function Super(name) {
    this.name = name
}
Super.prototype = {
    name: '我要走走看看',
    age: 18,
    hobby: { a: '篮球' },
}
// 子类
function Sub(name) {
    Super.call(this,name)   // 1: 调用第一次
}
Sub.prototype = new Super() // 2: 调用第二次

优点:可传参 + 继承了父类的原型
缺点:两次调用父类的构造函数(耗内存)

寄生组合式继承(常用)

function Super(name) {
    this.name = name
    this.age = 21
}
Super.prototype = Object.assign({
    sayName: () => {
        console.log(this.name)
    },
    address: '上海',
    hobby: ['唱歌'],
})

function Sub(name) {
    Super.call(this, name) // 继承父类构造函数的属性
}
Sub.prototype = Object.create(Super.prototype) // 继承父类的原型
const sub1 = new Sub('姓名1')
console.log(this.name) // 姓名1

既可以传参又实现了原型的复用

2.Symbol简单理解

  1. Symbol值通过Symbol函数,对象的属性名现在可以有两种类型,一种是原来就有的字符串,另一种就是新增的 Symbol 类型。而Symbol类型的属性名都是独一无二的,保证不会与其他属性名发生冲突。
  2. 注意:Symbol函数前不能用new命令,这是因为生成的 Symbol 是一个原始类型的值,不是对象。
let s = Symbol();
typeof s;//Symbol

注意

  • Symbol函数的参数只是表示对当前 Symbol 值的描述,因此相同参数的Symbol函数的返回值是不相等的。
  • Symbol值不能与其他类型的值进行运算
  • Symbol值可以显式转为字符串
  • Symbol值可以转为布尔型,但不能转为数值。
  1. 属性名的遍历。 Symbol 作为属性名,该属性不会出现在for...infor...of循环中,也不会被Object.keys()Object.getOwnPropertyNames()JSON.stringify()返回。但是,它也不是私有属性,有一个Object.getOwnPropertySymbols方法,可以获取指定对象的所有 Symbol 属性名。
  • Object.getOwnPropertySymbols():返回一个数组,成员是当前对象的所有用作属性名的 Symbol 值。
  • Reflect.ownKeys():返回所有类型的键名。
let obj = {
  [Symbol('my_key')]: 1,
  enum: 2,
  nonEnum: 3
    };
Reflect.ownKeys(obj)
//  ["enum", "nonEnum", Symbol(my_key)]
  1. Symbol的方法:
  • Symbol.for()(重新使用同一个Symbol值), 它接受一个字符串作为参数,然后搜索有没有以该参数作为名称的 Symbol 值。如果有,就返回这个 Symbol 值,否则就新建并返回一个以该字符串为名称的 Symbol 值。
let s1 = Symbol.for('foo');
let s2 = Symbol.for('foo');
s1 === s2 // true
  • Symbol.Keyfor()返回一个已登记的Symbol类型的值Key。
let s1 = Symbol.for("foo");
Symbol.keyFor(s1) // "foo"
let s2 = Symbol("foo");
Symbol.keyFor(s2) // undefined