首页 > 其他分享 >重拾JS-面向对象/原型以及原型链

重拾JS-面向对象/原型以及原型链

时间:2024-11-27 13:45:24浏览次数:6  
标签:对象 LOL JS 面向对象 Game 原型 new prototype 构造函数

简言

最近在做前端知识的复习和整理,有了一些自己新的体会。更多在于记录,通过反复的温习,写笔记消除自己以前学习知识点的误区

什么是面向对象?

要理解什么是面向对象,那么首先要知道什么是面向过程

面向过程

比如以做饭为例

graph TD 买菜 --> 切菜 --> 炒菜 --> 装盘

在上述流程图中,我们主要的关注点是在具体步骤的实施,着重点在过程(这一步要怎么什么,下一步要做什么)

面向对象

而面向对象的关注点则不是下一步要做什么,而是谁来做,着重点是做事的对象

面向对象和面向过程的区别

  1. 面向过程关注步骤,每次执行一个任务都需要将过程预先安排,性能损耗高
  2. 面向对象则关注个体对象,将实施步骤的能力赋予对象,由对象灵活安排,简化了对于流程的岔路准备

面向对象的优点

  1. 逻辑迁移更加灵活
  2. 代码复用性更高
  3. 高度模块化

而基于上述优点,在实际场景中,对象又可以做以下的事情:

  1. 对内存储数据和方法
  2. 对外提供模块和接口

对象

对象又分为简单对象以及函数对象,当然创建也就分为对象字面量创建以及构造函数创建

简单对象

const teacher = {
    name: 'xx',
    class: 'yuwen',
    teach: function(name) {
        return `开始教授${name}课程`
    }
}

简单对象的特点

  1. 本身具有开放性,体现在外部对其具有读写能力
  2. 不具备复制性,因为简单对象的内容被存放在堆中,并且会在栈中生成一个指向堆内容的地址,因此只能改变对象中的内容,却无法改变对象本身(地址不能变)

函数对象

在js中,构造实例的函数对象 我们也称其为 构造函数

构造函数 => 又可以理解成 constructor(构造器) => 构造 当前这个对象模板本身 的函数

主体是函数,用于构造对象模版

构造函数

在js,尤其是ES6之前,并没有类这个概念,因此我们创建一个对象,往往通过new一个构造函数来创建

new的过程,new是什么,new的原理(4点)

  1. 结构上:首先new一个构造函数会创建一个空对象,用于承载返回的对象实例
  2. 属性上:空对象上的原型对象(proto)会指向构造函数的prototype属性
  3. 关系上:将当前实例对象赋值给内部的this
  4. 生命周期上:会执行构造函数的初始化代码
function myNew(constructor, ...args) {
    // 边界处理
    if(typeof constructor!== "function") {
        throw new Error('请传入构造函数')
    }

    // 1. 结构上:首先new一个构造函数会创建一个空对象,用于承载返回的对象实例
    const obj = Object.create();
    
    // 2. 属性上:空对象上的原型对象(__proto__)会指向构造函数的prototype属性
    obj.__proto__ = constructor.prototype;
    
    // 3. 关系上:将当前实例对象赋值给内部的this
    // 4. 生命周期上:会执行构造函数的初始化代码
    const result = constructor.apply(obj, args);
    
    // 返回实例化对象
    return result instanceof Object ? result : obj;
}

function Person(name, age) { 
    this.name = name; 
    this.age = age; 
} 
const person1 = myNew(Person, 'Alice', 30); 
console.log(person1); // Person { name: 'Alice', age: 30 }

构造函数的特性 - prototype

函数对象(构造函数)有一个特性叫做 prototype,他是在函数对象(构造函数)被创建之初所具有的属性和方法的集合,同时可以于后期再prototype上叠加新的属性和方法

当使用new创建一个空对象时,会将空对象的__proto__关联到构造函数的prototype,因此空对象会被赋予新的属性和方法

例如:

// 人物具有以下特征
function Person(name, age) {
    this.name = name;
    this.age = age;
    this.say = function(words) {
        return words
    }
}

const person1 = new Person('xx', 12)
console.log(person1.say('Hello World'))

但是上述代码有个问题就在于,创建函数比创建变量的性能消耗更高
假如我们使用Person创建了成千上万个不一样的独立person,那么这些person都会有自己独立的say,这会给系统带来巨大的性能损耗

因此我们会采用在prototype上叠加新的方法的方式,让所有的实例化对象都共享同一个say,这样能有效的节省内存,并且非常高效

// 人物具有以下特征
function Person(name, age) {
    this.name = name;
    this.age = age;
}

Person.prototype.say = function(words) {
     return words
}

const person1 = new Person('xx', 12)
console.log(person1.say('Hello World'))

prototype 和 proto 有什么区别呢

  1. prototype 可以看成是父辈的优点
  2. proto 是每一个对象都具有的与父辈的血缘关系,通过__proto__,实例化的对象才能继承到父辈的优点

由__proto__承载构造函数的prototype,则prototype就被继承下来了,因此就有了原型和原型链的概念

原型和原型链

实例化对象的原型(__proto__的指向)就是用于创建实例化对象的函数对象(构造函数)的prototype(原型)

原型链就是通过每一层对象的__proto__不断承载上一层构造函数的prototype而形成的一条自上而下的链路

image.png

继承

有了原型链,那么就可以在js中很方便的去实现继承,也就相当于爷爷辈有什么关系(prototype)能够一代传几代

原型继承(按上述一代传几代)

function Game() {
  this.name = 'LOL'
  this.skin = ['s']
}
Game.prototype.getName = function() {
  return this.name
}

function LOL() {}
LOL.prototype = new Game()
// 这里可以将constructor认为是出生证明,如果不改变出生证明的话,LOL.prototype.constructor === Game 这显然是不对的,不可能说我是A的子孙,但是我继承了B的优点,我就要去认B做父
LOL.prototype.constructor = LoL 


const lol1 = new LOL()
const lol2 = new LOL()
lol1.getName()

但原型继承有个很明显的缺点就是通过lol1.skin.push('ss')同时会令lol2.skin一起改变

虽然new一个构造函数能创建一个独立的实例化对象,但是在这里,由于LOL.prototype 通过new Game()获得了唯一的对象所拥有的特征。

而lol1 lol2虽然是不一样的两个对象,但由于 lol1.proto === lol2.proto === LOL.prototype,因此他们会共用原型(LOL.prototype)上的唯一属性,并且实例化lol1 lol2无法向LOL构造函数传参

image.png

构造函数继承

通过构造函数继承,解决子类属性共享的问题

function Game() {
  this.name = 'LOL'
  this.skin = ['s']
}
Game.prototype.getName = function() {
  return this.name
}

function LOL(...args) {
  // 因为new 创建对象时,会将实例化对象赋给构造函数内部的this,并且会按照声明周期执行构造函数的初始化代码
  Game.call(this, ...arg)  
}

const lol1 = new LOL()
const lol2 = new LOL()
lol1.skin.push('ss')

但构造函数也有问题:
子类只继承了构造函数内部的属性,但是构造函数原型链(constructor.prototype)上的属性和方法无法继承

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

function Game() {
  this.name = 'LOL'
  this.skin = ['s']
}
Game.prototype.getName = function() {
  return this.name
}

function LOL(...args) {
  Game.call(this, ...arg)  // 这里的this就分别是lol1和lol2,互相不干扰
}
LOL.prototype = new Game() // 继承原型链上的方法
LOL.prototype.constructor = LoL

const lol1 = new LOL()
const lol2 = new LOL()
lol1.skin.push('ss')

但组合继承也有个小问题:
因为new会在生命周期中执行构造函数的初始化代码,因此在上述代码中

  1. Game.call()
  2. new Game()

都会执行构造函数Game的初始化代码,如果实例化对象过多会产生性能问题
因此new Game()可以替换成Object.create(Game.prototype)也可以实现继承Game原型上的属性和方法

寄生组合继承

function Game() {
  this.name = 'LOL'
  this.skin = ['s']
}
Game.prototype.getName = function() {
  return this.name
}

function LOL(...args) {
  Game.call(this, ...arg)  
}
LOL.prototype = Object.create(Game.prototype) // 创建一个空对象承载Game原型上的属性和方法
LOL.prototype.constructor = LoL

const lol1 = new LOL()
const lol2 = new LOL()
lol1.skin.push('ss')

标签:对象,LOL,JS,面向对象,Game,原型,new,prototype,构造函数
From: https://www.cnblogs.com/laowangcoding/p/18572173

相关文章

  • 用文字“画出”状态图:用 AI+Mermaid.js 解决对象状态变化的处理问题
    什么是状态图状态图用于描述对象在其生命周期内的状态变化及其处理,例如业务办理流程、病情处置等。什么是MermaidMermaid.js是一个开源项目,它允许你通过简单的语法来绘制图表。无论你是开发者、学生还是普通用户,它都能帮助你将复杂的信息以直观和易懂的方式呈现出来。什么是......
  • 浅谈Vue.js
    支持一对一答疑的购买网址Vue.js简介Vue.js的作者为EvanYou(尤雨溪),曾任职于GoogleCreativeLab,虽然是Vue是一个个人项目,但在发展前景上个人认为绝不输于Google的AngularJs,下面我会将Vue与Angular(Angular1.0+版本)做一些简单的比较。Vue的主要特点就和它官网(http://cn.vue......
  • JS实现 ZIP 压缩包的导入解析
    功能目标在前端实现ZIP压缩包的解析,将文件名通过下拉框展示,并支持查看所选文件的内容。使用技术JSZip:解析ZIP文件的库。FileReader:读取用户上传的文件。HTML和JavaScript:实现交互和动态内容展示。实现步骤1.引入JSZip通过CDN或npm引入JSZip:<scriptsrc=......
  • 什么是JSON,有什么特点
    什么是JSON?JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式,易于人阅读和编写,同时也易于机器解析和生成。它基于JavaScript的子集,但独立于语言,被广泛用于服务器与Web应用之间的数据传输。JSON格式最初由DouglasCrockford在2001年提出,并迅速成为互联网上......
  • RTSP播放器EasyPlayer.js播放器关于报“RuntimeError: abort(OOM). Build with -s ASS
    随着互联网技术的不断进步,HTML5逐渐取代了传统的HTML4,成为现代网站开发不可或缺的一部分。HTML5不仅简化了标记语言的复杂度,还引入了许多新的API和元素,极大地丰富了网页的表现力。特别是在多媒体领域,HTML5提供了原生支持音频和视频播放的功能,使得开发者无需依赖第三方插件如Flash,......
  • js如何把网页加入收藏夹功能?
    在JavaScript中,你可以使用window.external.AddFavorite方法将当前网页添加到用户的收藏夹。然而,这种方法只在InternetExplorer中有效,并且由于安全限制,现代浏览器通常已弃用或禁用此功能。对于现代浏览器,更可靠的方法是使用HTML5的链接元素<a>,并设置其rel属性为sidebar。虽然这并......
  • 请用js实现一个promise的方法
    //BasicPromiseExample:ResolvingavalueafteradelayfunctionmyPromise(delay,value){returnnewPromise((resolve,reject)=>{setTimeout(()=>{resolve(value);//Resolvewiththeprovidedvalue},delay);});}//Exa......
  • (分享源码)计算机毕业设计必看必学 上万套实战教程手把手教学JAVA、PHP,node.js,C++、pyth
    摘 要随着互联网大趋势的到来,社会的方方面面,各行各业都在考虑利用互联网作为媒介将自己的信息更及时有效地推广出去,而其中最好的方式就是建立网络管理系统,并对其进行信息管理。由于现在网络的发达,果园信息统计管理系统的信息通过网络进行信息管理掀起了热潮,所以针对果园信......
  • (分享源码)计算机毕业设计必看必学 上万套实战教程手把手教学JAVA、PHP,node.js,C++、pyth
    摘 要科技进步的飞速发展引起人们日常生活的巨大变化,电子信息技术的飞速发展使得电子信息技术的各个领域的应用水平得到普及和应用。信息时代的到来已成为不可阻挡的时尚潮流,人类发展的历史正进入一个新时代。在现实运用中,应用软件的工作规则和开发步骤,采用Java技术建设物......
  • Threejs的三维坐标系
    在三维空间中,所有的物体和相机都需要基于一个统一的坐标系来进行定位和操作。理解坐标系的基本概念,对于创建稳定、准确的三维效果至关重要。基础Three.js采用的是右手坐标系,这意味着如果你将右手的三个手指伸直,分别指向X、Y和Z轴的方向,你的拇指指向的方向即为X轴,食指指向......