【JavaScript】面向对象编程/原型及原型链

面向对象

为什么要面向对象?

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

对象是什么?

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

简单对象

  • 对外开放,属性和方法可以被直接修改
js 复制代码
const Course = {
     teacher :'张三',
     leader:'cc',
     startCourse : name =>{
         return `开始${name}课`
     }
 }
 // A
Course.teacher = '李四';
Course.startCourse('react')
// B
Course.startCourse('vue');
// 以上会修改对象里面的数据,为了防止修改,所以使用函数对象

函数对象

构造函数的实例化

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

提问1:如果构造函数不实例化,可以作为对象生成器使用吗?

答案:不可以

js 复制代码
function Course(name) {
  this.name = name
  this.teacher = '小明'
  this.teachestartCourse = function () {
    return `开始${this.name}课`
  }
}
console.log(Course.teacher) // undefined

提问2:如果项目需要使用,又不想被外界感知,通常如何解决?

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

js 复制代码
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的时候做了什么?

  • 结构上:创建一个空的对象,作为返回的对象实例。
  • 属性上:将生成空对象的原型对象指向了构造函数的prototype属性。
  • 关系上:将当前实例对象赋给了内部的this。
  • 生命周期上:执行了构造函数的初始化代码。

手写代码模拟 new

js 复制代码
function usernew(obj,...args){
    const newObj = Object.create(obj.prototype) 
    const result = obj.apply(newObj,args)
    return typeof result === 'object'? result:newObj
}

 function person(name, age) {
  this.name = name;
  this.age = age

}
const p = usernew(person '小明',10)
console.log(p)

提问4:constructor是什么?

构造器

  • 每个对象实例化时,都会自动拥有一个constructor属性。
  • constructor属性继承原型对象,并指向当前的构造函数。
  • constructor代表了实例化对象是被谁生成的。

提问5:使用构造函数继承属性就没有问题吗?会有什么性能问题?

  • 构造函数中的方法实例化后会存在于每个生成的实力内
  • 而方法往往需要统一模块化,重复挂载只会浪费资源
  • 如何进行优化:把方法挂在构造函数的prototype属性上,让实例对象自动通过自身的__proto__属性去引用方法

提问6:前面提到的原型对象又是什么? - prototype

js 复制代码
 function Course() {}
 const course1 = new Course();
 const course2 = new Course();
 // 1. Course - 用来初始化创建对象的函数 | 类
 course1.__proto__ === Course.prorotype
 // 2. course1 - 根据原型创建出来的实例
    course1.constructor === Course

prototype 只有构造函数有prototype属性。

js 复制代码
function Course() {}
const course1 = new Course()
  • 构造函数:用来初始化创建对象的函数|类 - Course
  • 自动给构造函数赋予了一个属性 prototype(原型对象),该属性等于实例对象的 __proto__ course1.__proto__ === Course.prototype // true
  • 每个对象实例化时,都会自动拥有一个 constructor 属性,(course1是根据原型创建出来的实例)course1.constructor => Course
  • constructor属性继承自原型对象,并指向当前构造函数 course1.constructor => course1.__proto__.constructor => Course.prototype.constructor => Course

提问7:原型对象有自己的原型吗?有

js 复制代码
function Course() {}
const course1 = new Course()

course1.__proto__.__proto__  ===  Object.prototype
Course.prototype.__proto__   ===  Object.prototype
course1.__proto__.__proto__.__proto__  === null

原型链

原型&原型链

原型

  • prototype:Js通过构造函数来创建一个对象,每个构造函数内部都会有一个原型prototypr属性,它指向另外一个对象,这个对象包含了可以由该构造函数的所有实例共享的属性和方法。
  • proto:当使用构造函数创建一个实例对象后,可以通过__proto__访问到prototype属性。
  • constructor:实例对象通过这个属性可以访问到构造函数

原型链

  • 每个实例对象都有一个__proto__属性指向它的构造函数的原型对象,而这个原型对象也会有自己的原型对象,一层一层向上,直到顶级原型对象null,这样就形成了一个原型链。
  • 当访问对象的一个属性或方法时,当对象身上不存在该属性方法时,就会沿着原型链向上查找,直到查找到该属性方法位置。
  • 原型链的顶层原型是Object.prototype,如果这里没有就只指向null

继承

重写原型对象继承

  • 在原型对象上的属性和方法,都能被实例共享
  • 本质:重写原型对象,将父类实例对象的属性和方法,作为子对象原型的属性和方法
  • 缺点:
  1. 父类属性一旦赋值给子类的原型属性,此时属性属于子类的共享属性了。( 子类的 prototype 来自父类的一个实例对象,其中一个子类实例对象改了继承而来属性或方法,其他子类实例对象继承而来的属性或方法也就被修改了 )
  2. 实例化子类时,无法向父类传参。
js 复制代码
function Game() {
    this.name = 'lol';
    this.skin = ['s'];
}
Game.prototype.getName = function() {
    return this.name;
}

// LOL
function LOL() {};
LOL.prototype = new Game();
LOL.prototype.constructor = LOL; //重写构造函数,指回父
const game1 = new LOL();
const game2 = new LOL();
game1.skin.push('ss');
//本质:重写原型对象,将父类实例对象的属性和方法,作为子对象原型的属性和方法,同时重写构造函数

构造函数继承/拷贝继承

  • 在子类的构造函数内部调用父类的构造函数
  • 缺点:无法通过原型链继承原型对象上共享的方法
js 复制代码
function Game(arg) {
     this.name = 'lol';
     this.skin = ['s'];
}
Game.prototype.getName = function() {
    return this.name;
}
function LOL(arg) {
    Game.call(this, arg);
}
const game3 = new LOL('arg');
// 解决了共享属性问题 + 子向父传参的问题

组合继承

  • 组合继承:构造函数继承 + 重写原型对象继承
  • 缺点:父类被调用了两次
js 复制代码
function Game(arg) {
    this.name = 'lol';
    this.skin = ['s'];
}
Game.prototype.getName = function() {
    return this.name;
}
function LOL(arg) {
    Game.call(this, arg);
}
LOL.prototype = new Game();
LOL.prototype.constructor = LOL;
const game4 = new LOL('arg');

寄生组合继承

Course.prototype为原型,通过Object.create()创建一个方新的对象赋值给LOL.prototype

js 复制代码
function Game(arg) {
    this.name = 'lol';
    this.skin = ['s'];
}
Game.prototype.getName = function() {
    return this.name;
}

function LOL(arg) {
    Game.call(this, arg);
}
LOL.prototype = Object.create(Game.prototype);
LOL.prototype.constructor = LOL;
const game5 = new LOL('arg');

多重继承

通过Object.assign()合并多个父类的原型对象给当前子类的原型对象

js 复制代码
function Game(arg) {
    this.name = 'lol';
    this.skin = ['s'];
}
Game.prototype.getName = function() {
    return this.name;
}
function Store() {
    this.shop = 'steam';
}
Game.prototype.getPlatform = function() {
    return this.shop;
}
function LOL(arg) {
    Game.call(this, arg);
    Store.call(this, arg);
}
LOL.prototype = Object.create(Game.prototype);
Object.assign(
    Store.prototype,
    LOL.prototype
);
LOL.prototype.constructor = LOL;
相关推荐
香芋芋圆4 小时前
AI 冲击内卷之下,普通前端如何破局?WebGIS—— 低门槛突围赛道
前端·javascript·人工智能·学习·职场发展
INS_KF5 小时前
【编程笔记】成员函数中两个 const 的区别(const Data &getData() const;)
前端·javascript·笔记
宿6745 小时前
vue3-async
前端·javascript·vue.js
excel7 小时前
Nuxt + Twin CSS 中宽度超过屏幕时底部出现空白的原因与解决方案
前端·javascript
一个游离的指针9 小时前
函数管道:消除深度嵌套调用
前端·javascript
柚yuzumi10 小时前
前端优化,从少触发一次开始:防抖与节流
前端·javascript
默_笙10 小时前
🚤 CSS 布局的"圈地运动":BFC 就是浏览器的独立领地
前端·javascript
用户9385156350711 小时前
大模型记忆指南:从短时缓存到永久记忆,手把手带你吃透 LangChain Memory 管理
javascript·人工智能
xqchen11 小时前
Web Components 普及困境深度解析:技术标准与工程实践的落差
javascript
爱丶不疚11 小时前
写给前端工程师的现代 Python 工程化最佳实践:从 pnpm 到 uv,从 CommonJS 到 src-layout
javascript·python·typescript