前端面试复习2---面向对象编程/原型及原型链

面向对象编程/原型及原型链

面向对象

为什么要面向对象?

  1. 优势:简化我们对行为岔路的预备。
  2. 特点:逻辑迁移更加灵活,代码复用性高,高度模块化的体现。

对象是什么?

  1. 对单个物体的简单抽象。
  2. 对象是一个容器,封装了属性和方法,属性代表对象的状态,方法代表对象的行为。
  3. 模块、接口 => 对象
简单对象

对外开放,属性和方法可以被直接修改

const Course = {
  teacher: '小明',
  startCourse(name) {
    return `开始${name}`
  },
}
函数对象

构造函数的实例化对象

  1. 需要一个模板,表征了一个类物品的共同特征,从而生成对象。
  2. 构造函数即对象模板,构造函数可以理解为类。
  3. js 本质上并不是直接基于类,基于构造函数 + 原型链 => constructor + prototype
  4. 需要使用 new 来实例化对象。
  5. 构造函数体内使用的 this 指向所要生成的实例。
  6. 可以做初始化传参。
function Course(name) {
  this.name = name
  this.teacher = '小明'
  this.teachestartCourse = function () {
    return `开始${this.name}`
  }
}

const 语文 = new Course('语文') // 实例化一个对象
面试提问 1:如果构造函数不实例化,可以作为对象生成器使用吗?

答案:不可以

function Course(name) {
  this.name = name
  this.teacher = '小明'
  this.teachestartCourse = function () {
    return `开始${this.name}`
  }
}
console.log(Course.teacher) // undefined
面试提问 2:如果项目需要使用,又不想被外界感知,通常如何解决?

答案: 单例模式 => 全局维护统一实例 => 解决对象实例化生成问题

function Course() {
  const isClass = this instanceof Course
  if (!isClass) {
    return new Course()
  }
  this.name = '语文'
  this.teacher = '小明'
  this.teachestartCourse = function () {
    return `开始${this.name}`
  }
}
console.log(Course().name) // 语文
面试提问 3:new 是什么? / new 原理 / new 的时候做了什么?
  1. 结构上:创建一个空的对象,作为返回的对象实例
  2. 属性上:将空对象的__proto__指向constructorprototype属性
  3. 关系上:将当前实例对象赋给内部的 this
  4. 生命上周期:执行了构造函数的初始化代码
手写代码模拟 new
function _new() {
  const obj = {} // 创建一个空的对象,作为返回的对象实例
  const constructor = Array.prototype.shift.call(arguments)
  obj.__proto__ = constructor.prototype // 将空对象的原型指向构造函数的 prototype属性
  const result = constructor.apply(obj, arguments) // 把构造函数的 this 指向刚刚创建的对象实例,同时执行构造函数初始化代码

  // result || obj 这里这么写考虑了构造函数显示返回 null 的情况
  return typeof result === 'object' ? result || obj : obj
}

function person(name, age) {
  this.name = name
  this.age = age
}
const p = _new(person, '小明', 10)
console.log(p)
面试提问 4:constructor是什么? - 构造器
  1. 每个对象实例化时,都会自动拥有一个constructor属性
  2. constructor属性继承自原型对象,并指向当前构造函数
  3. constructor代表了实例化对象是被谁生成的
面试提问 5:使用构造函数继承属性就没有问题吗? / 会有什么性能问题?
  1. 构造函数中的方法实例化后会存在于每个生成的实例内,
  2. 而方法往往需要统一模板化,重复挂载只会浪费资源。
  3. 如何进行优化:把方法挂在构造函数的prototype属性上,让实例对象自动通过自身的__proto__属性去引用方法
面试提问 6:前面提到的原型对象又是什么? - prototype
function Course() {}
const course1 = new Course()

/**
1. 构造函数:用来初始化创建对象的函数 - Course
2. 自动给构造函数赋予了一个属性 prototype(原型对象),该属性等于实例对象的 __proto__
   Course.prototype === course1.__proto__ // true
3. 每个对象实例化时,都会自动拥有一个 constructor 属性
4. constructor属性继承自原型对象,并指向当前构造函数
   ourse1.constructor => course1.__proto__.constructor => Course.prototype.constructor => Course
*/

PS: 只有构造函数有 prototype 属性

面试提问 7:原型对象有自己的原型吗? - 有
function Course() {}
const course1 = new Course()

// Course.prototype.__proto__ => Object.prototype

继承

1. 重写 原型对象 继承

  1. 在原型对象上的属性和方法都能被实例共享
  2. 本质:重写原型对象,将父类实例对象的属性和方法,作为子对象原型的属性和方法
  3. 缺点:1. 子类的 prototype 来自父类的一个实例对象,其中一个子类实例对象改了继承而来属性或方法,其他子类实例对象继承而来的属性或方法也就被修改了。2. 实例化子类时,无法向父类传参。
function Course() {
  this.name = 'English'
}
Course.prototype.getName = function () {
  return this.name
}
function English() {}
English.prototype = new Course()
English.prototype.constructor = English
const english = new English()
const name = english.getName()
console.log(name) // English

2. 构造函数继承/拷贝继承

  1. 在子类的内部直接调用父类继承
  2. 缺点:无法通过原型链继承原型对象上共享的方法
function Course(name) {
  this.name = name
}
Course.prototype.getName = function () {
  return this.name
}
function English(name) {
  Course.call(this, name)
}
const english = new English('新概念英语')
console.log(english.name) // 新概念英语
const name = english.getName() // Uncaught TypeError: english.getName is not a function

3. 组合继承

  1. 组合继承:构造函数继承 + 重写原型对象继承
  2. 缺点:父类被调用了两次
function Course(name) {
  this.name = name
}
Course.prototype.getName = function () {
  return this.name
}
function English(name) {
  Course.call(this, name)
}
English.prototype = new Course()
English.prototype.constructor = English
const english = new English('新概念英语')
const name = english.getName()
console.log(name)

4. 寄生组合继承

  1. Course.prototype为原型,通过Object.create()创建一个方新的对象赋值给English.prototype
function Course(name) {
  this.name = name
}
Course.prototype.getName = function () {
  return this.name
}
function English(name) {
  Course.call(this, name)
}
English.prototype = Object.create(Course.prototype)
English.prototype.constructor = English
const english = new English('新概念英语')
const name = english.getName()
console.log(name)

5. 多重继承

  1. 通过Object.assign()合并多个父类的原型对象给当前子类的原型对象
function School(school) {
  this.school = school
}
Course.prototype.getSchool = function () {
  return this.school
}

function Course(name) {
  this.name = name
}
Course.prototype.getName = function () {
  return this.name
}

function English(school, name) {
  School.call(this, school)
  Course.call(this, name)
}
English.prototype = Object.create(School.prototype)
Object.assign(English.prototype, Course.prototype)
English.prototype.constructor = English

const english = new English('胜利小学', '新概念英语')
const school = english.getSchool()
const name = english.getName()
console.log(school, name)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值