【Javascript】面向对象编程,this,原型与原型链,类与实例,class,实现Map,stack,Queue ,Set

news2024/9/21 2:46:38

❤️ Author: 老九
☕️ 个人博客:老九的CSDN博客
🙏 个人名言:不可控之事 乐观面对
😍 系列专栏:

文章目录

  • 对象中的方法/this
    • this使用
    • bind函数
  • 原型
    • 原型链
  • 类与实例
    • class
    • class语法
    • 补充
  • Map
    • 实现Map
    • 实现stack
    • 实现Queue
    • 实现Set

对象中的方法/this

  • 面向对象:核心思想就是可以为每种事务(东西),在编程语言中建立一个对应的类型,对应到语言里就是一个class
  • 反引号:中间可以插入${}作为动态字符串
  • 其实对象中函数的调用就是方法,foo和bar就当作obj对象的方法调用
  • 其中foo的this就是指代的obj对象,bar的this指代的也是obj这个对象
<script>
  obj = {
    foo: function () { },
    bar: function () { },
  }

  let rabbit = {}
  rabbit.speak = function (line) {
    console.log(`the rabbit says ${line}`)
  }
</script>

this使用

  • this是函数的一个隐含参数,其值不是传入的,而是取决于该函数的调用方式,不取决于函数在哪定义和在哪调用
  • f() = obj.foo
    f() // 当函数调用,里面的this指向window
    obj.foo() //当obj的方法调用,里面的this指向obj
    他们两个的this是不同的
  • 这个this指代的是添加的属性对应的对象
<script>
  a = [1, 2, 3]
  a.push2 = function (val) {
    this[this.length] = val
    return this.length	
  }
   a.forEach2 = function (action) {
   for (var i = 0; i < this.length; i++) {
     action(this[i])
   }
</script>
  • 这个speak是对象属性的简写,speak属性来源于能够读到的一个speak函数的变量名
    -

  • js语言中,函数从来不属于一个对象,this也不会因为这种属于而一成不变

  • 在js中,如果两个对象分别有一个属性指向同一个函数,则该函数和这两个对象之间的关系是对等的

<script>
  function speak(line) {
    console.log(`the ${this.type} rabbit says ${line}`)
  }

  let whiteRabbit = {
    type: "white",
    speak
  }
  let hungryRabbit = {
  	type : "hungry",
  	speak
  }
  speak.call(hungryRabbit,"woshizhu")
</script>
  • 在call方法和apply方法中,第一个参数传的就是this,call和apply唯一的区别就是第二个参数apply传的是数组,而call后面传的是一个一个的参数,由此来看apply更好用一些,因为如果数组可以是一个变化的值,而call的话只能是传固定的参数
    在这里插入图片描述
  • 所以,如果一个函数中创建了另一个函数,那么内层函数是拿不到外层函数的this的,除非是外层函数通过call方法,把外层函数的this和内层函数指向同一个函数
  • this不能出现在等号的左边,即this本身不能被赋值,this的绑定关系不能被改变。
  • 箭头函数是没有this的,在箭头函数里读this,就跟在读一个箭头函数内部没有声明的变量一样。读箭头函数的this,就是读箭头函数外面函数的this
  • 由于call/apply并不能改变箭头函数里的this,所以一般都不会使用call和apply来调用箭头函数。apply传不定数量参数的功能由展开运算符。。。来提供。

bind函数

  • bind函数的第一个参数是绑定this,如果bind的this已经被绑定了,那么就不能再次更改绑定了
    在这里插入图片描述

原型

在这里插入图片描述

  • 从这里我们可以看到,js从一个空对象里取出了一个属性;如果你在一个对象上找不到对应的属性,就会在这个原型对象上找,原型就相当于是一个后备源;所以任何一个对象都有toString的方法,这个方法就是在原型上的
  • Object.getPrototypeOf({})这个可以获得对象的原型,object.prototype这个是获取object上的一个属性,这个属性指向原型对象,所以他俩是相等的;数组的原型的原型是对象的原型;最终Object.getPrototypeOf(Object.getPrototypeOf({}))的原型是null
  • Object.getPrototypeOf([]) === Array.prototype
  • Object.getPrototypeOf(function(){}) === Function.prototype
  • Array.prototype和Function.prototype都以Object.prototype为原型
    在这里插入图片描述
    在这里插入图片描述
  • 这个双中括号phototype就是原型,双中括号代表对象的内部的状态
  • 如果想要看到函数的内部状态,用console.dir()
    在这里插入图片描述
    在这里插入图片描述
  • 还有一点就是不会因为给一个对象的属性赋值而赋值到其原型对象上

原型链

  • js就是通过原型链的方式实现了面向对象的
  • 原型链在js中其实也只是指针(对象的指向关系 )
  • 原型链存在的作用一方面是为了继承,另一方面是为了节省空间;如果一组对象都想有某些方法/属性,则把这些共用的方法/属性放到它们的原型上,可以节省存储空间

在这里插入图片描述

类与实例

  • 补充一个语法:当在对象的属性中,一个属性指向一个函数(必须是function函数),可以直接将冒号和function删除
    在这里插入图片描述
  • 怎样自己创建原型对象呢?通过Object.create(xxx)
    在这里插入图片描述

class

  • class就是定义了一个类型,构造函数就是一个所有的基础
<script>
  var protoRabbit = {
    speak(line) {
      console.log(`the ${this.type} rabbit says ${line}`)
    }
  }

  function makeRabbit(type) {
    let rabbit = Object.create(protoRabbit)
    rabbit.type = type
    return rabbit
  }

  var killRabbit = makeRabbit('killer')
  var yellowRabbit = makeRabbit('yellowRabbit')
</script>
  • 这样写等价于上面那么写,更加方便
  • 原型里面就写一些基础的公共方法,然后构造函数就传值,然后用new创建对象
<script>
  function Rabbit(type) {
    this.type = type
  }

  Rabbit.prototype = {
    speak(line) {
      console.log(`the ${this.type} rabbit says ${line}`)
    }
  }
new Rabbit(yellow);
</script>

在这里插入图片描述

  • 构造函数的prototype属性 仅仅是作为其构造出来的实例的原型;其实所有的普通函数(非箭头函数)都有prototype属性,但我们一般只关心构造函数的prototype属性
  • 除了undifined和null,任何值都有原型,即_ proto 属性, proto _属性就是原型,要区别于原型和构造函数的原型属性
    也就是说Object.getPrototypeOf(Rabbit) == Function.prototype
    在这里插入图片描述
  • 上面的那个叫原型属性,下面的那个叫原型,一般如果是构造方法的话,我们就设置上面的那个,也就是设置所谓的原型属性

class语法

  • 有一个原型属性的构造函数就可以称为一个类,比上面的写法更简单 ,上面的方法是在2015年之前写的,也就是es6之前
<script>
  class Rabbit{
    //构造函数
    constructor(type){
      this.type = type
    }
    speak(line){
      console.log(`ths ${this.type} rabbit says ${line}`)
    }
  }
  let killerRabbit = new Rabbit("killer")
  //相当于,class就相当于函数
  //let killerRabbit = new class{}
</script>
  • 如果把Rabbit typeof一下,那它就是一个function。,上面的speak方法就是放在Rabbit.prototype上面的

补充

  • obj自己的x会覆盖原型上的x

在这里插入图片描述

  • 如果想代替Array.prototype的toString方法,使用object.prototype的toString方法,可以通过Object.prototype.toString.call(ary)来实现,这个输出的是’[object Array]',因此Object.prototype.toString这个函数,给他的this传入什么,它就将该值转换为[object xxx]的字符串,其中xxxx是this缩指向的对象的类型名。所以这个函数可以用来做类型判断,还有typeof也可以做类型判断;但是这个toString无法判断用户自定义的类型(toString.call(rabbit)),这个值是[object,object],本来输出的应该是[object, rabbit];
<script>
	function isArray(val){
	return Object.prototype.toString.call(val) === '[Object Array]'
</script>

Map

  • 通过一个值映射到另一个值,虽然对象上可以实现map的功能,但是如果读出的一个key是不存在的,但是原型上存在,这样就会出现歧义了。一个方法是创建一个没有原型的对象(obj = Object.create(null));
<script>
  let ages = new Map()
  ages.set("Boris", 39)
  ages.set("Liang", 22)
  ages.set("Julia", 62) // obj['Julia'] = 62
  ages.get('Liang') //obj['Liang']
  ages.has('Liang') //'Liang' in obj
</script>
<script>
  const val = 'hello world !!'
  function lmp(val) {
    let map = new Map()
    for (let i = 0; i < val.length; i++) {
      if (!map.has(val[i])) {
        map.set(val[i], 1)
      } else {
        map.set(val[i], map.get(val[i]) + 1)
      }
    }
    let res = []
    let max = 0
    map.forEach((value, key) => {
      if (value > max) {
        res = [key]
        max = value
      } else if (value == max) {
        res.push(key)
      }
    })
    return [max, res]
  }
  let a = lmp(val)
</script>
  • set,get,has是map的其中一些接口,map这些操作的性能都是O(1)的,是非常快的。
  • ages.clear():清除所有的映射对;ages.delete(key):删除一组映射对; ages.forEach((val,key) => {xxx}):遍历这个映射,可以类比于数组,传入的值是数组的值和下标;ages.size:这是一个属性,不是一个方法,可以拿到map里有几组;
  • Object.keys(obj):这个可以返回一个对象的自有属性有哪些,原型属性是无法返回的, 这些自有属性组成了一个数组返回。
<script>
  let obj = {}
  obj.foo = 1
  obj.bar = 2

  function keys(obj) {
    var res = []
    for (let key in obj) {
    if(Object.prototype.hasOwnProperrty.call(obj,key)){
      res.push(key)
      console.log(key, obj[key])
    	}
    }
    return res
  }
</script>
  • 有一个方法是obj.hasOwnProperty()方法是继承来的,可以判断该对象有没有对应的自有属性;但是in运算符不同,只要obj上有这个属性,不管是不是原型属性都算上

在这里插入图片描述
在这里插入图片描述

实现Map

<script>
  class Map2 {
    constructor() {
      this.keys = [] // 用来存储每组映射的key
      this.vals = []// 用来存储每组映射的value
    }
    get(key) {
      var keyIdx = this.keys.indexOf(key)
      if (keyIdx >= 0) {
        return this.vals[keyIdx]
      }
    }
    set(key, val) {
      var keyIdx = this.keys.indexOf(key)
      if (keyIdx >= 0) {
        this.vals[keyIdx] = val
      } else {
        this.keys.push(key)
        this.vals.push(val)
      }
      return this
    }
    has(key) {
      var keyIdx = this.keys.indexOf(key)
      if (keyIdx >= 0) {
        return true
      }
      return false
    }
    delete(key) {
      var keyIdx = this.keys.indexOf(key)
      if (keyIdx >= 0) {
        //splice函数,从keyIdx位置,删除一项
        this.keys.splice(keyIdx, 1)
        this.vals.splice(keyIdx, 1)
        return true
      }
      return false
    }
    clear() {
      this.keys = []
      this.vals = []
    }
    size() {
      return this.keys.length
    }
  }
</script>

  • 我们不希望外界的人设置keys和vals,在构造函数外面写上#xxx,就变成私有类字段,外界就无法访问了
    在这里插入图片描述

实现stack

<script>
  class stack {
    //带警号的要在外面先声明
    #size = 0
    #top = null
    // constructor() {
    //   this.#top = null // 用于存储栈内元素的链表;由于头节点是栈顶,所以是top
    //   this.#size = 0
    // }
    push(val) {
      let Node = {
        val: val,
        next: null
      }
      Node.next = this.#top
      this.#top = Node
      this.#size++
      return this
    }
    //返回栈顶元素,并将其出栈
    pop() {
      if (this.#top) {
        let res = this.#top.val
        this.#top = this.#top.next
        this.#size--
        return res
      }
    }
    size() {
      return this.#size 
    }
    //查看栈顶元素的值,但不出栈
    peek() {
      if (this.#top) {
        return this.#top.val
      }
    }
  }

</script>

实现Queue

实现Set

  • 数组是可重复的有序集合,set是无序的不可重复的集合
  • let = new Set(),有add方法,has方法,delete方法,clear方法,size方法

————————————————————————
♥♥♥码字不易,大家的支持就是我坚持下去的动力♥♥♥
版权声明:本文为CSDN博主「亚太地区百大最帅面孔第101名」的原创文章

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/172382.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【国产GD32芯片解析中科微北斗+GPS模块经纬度数据详细教程-附完整代码工程】

国产GD32芯片解析中科微北斗GPS模块经纬度数据详细教程-附完整代码工程简介准备工作PC端需要用到的工具代码下载地址GD32F103C8T6最小系统板代码实现GD32串口引脚定义如下&#xff1a;串口的初始化串口0初始化代码&#xff1a;串口1初始化代码串口的输入串口0的输入代码如下&am…

非标准包 game.rgss3a 的打开方法 | 2023 年实测

写在前面&#xff1a;最近在玩 RPG 游戏&#xff0c;想拆一个 Game.rgss3a 包&#xff0c;在网上找了很久的拆包方法&#xff0c;感觉写的比较凌乱&#xff0c;我来给大家整理一下吧。不过我本人的技术能力也很差&#xff0c;不确定说的是不是对的&#xff0c;就当是给大家提供…

中国智造助推跨境电商企业迈向全球市场

现今&#xff0c;跨境电商行业发展的如火如荼&#xff0c;中国智造也在不断助推跨境电商企业迈向全球市场。业内人员在新常态下的思想也有了一些改变&#xff0c;现在的跨境电商都是“平台物流”&#xff0c;在物流环节&#xff0c;也需要我们的专业团队去进行整合&#xff0c;…

GD32F450寄存器和库函数

GD32F4xx用户手册 GD32F450xx数据手册 GD32F3x0固件库使用指南 一、寄存器介绍 1. 存储器映射表 GD32是一个32位的单片机&#xff0c;它的地址范围为2的32次方&#xff0c;也就是4GB的地址空间。 为了降低不同客户在相同应用时的软件复杂度&#xff0c;存储映射是按Corte…

python能做的100件事03-python爬虫

文章目录1. scrapy介绍2 新建爬虫项目3 新建蜘蛛文件4 运行爬虫5 爬取内容5.1分析网页结构5.2 关于Xpath解析5.3 接着解析电影数据5.4 下载缩略图5.5 完整代码6 最后说明本例基于python3和scrapy爬虫框架&#xff0c;不再介绍python的基础知识和爬虫的基本知识。1. scrapy介绍 …

制药企业的发展趋势--行业公司数据调研

制药行业是国家重点培育发展的战略产业。制药行业的发展对人民健康、医药科技和社会进步等方面都有着十分重要的作用。下面笔者将阐述近年来制药行业发展的现状及趋势&#xff0c;并对制药行业的研发、销售等多维度的信息进行展示与解读。中国制药企业现状目前&#xff0c;中国…

分享103个PHP源码,总有一款适合您

PHP源码 分享103个PHP源码&#xff0c;总有一款适合您 下面是文件的名字&#xff0c;我放了一些图片&#xff0c;文章里不是所有的图主要是放不下...&#xff0c; 103个PHP源码下载链接&#xff1a;https://pan.baidu.com/s/1_T5IzwgcntFuyqulehbSzQ?pwdv6ds 提取码&#…

vue-router 使用与原理分析

简介 Vue Router 是Vue.js的官方路由。与Vue.js核心深度集成&#xff0c;让用Vue.js构建单页应用&#xff08;SPA&#xff09;变得更加简单。 使用 创建 1、在安装好Vue Router依赖后&#xff0c;在App.vue中引入router-view&#xff0c;它是渲染的容器 <div id"…

ARM X210开发板的软开关按键问题

一、X210 开发板的软启动电路详解 《x210bv3.pdf》 (1) 210 供电需要的电压比较稳定&#xff0c;而外部适配器的输出电压不一定那么稳定&#xff0c;因此板载了一个文稳压器件 MP1482. 这个稳压芯片的作用就是外部适配器电压在一定范围内变化时稳压芯片的输出电压都是 5V。 (2)…

Redis 主从复制

目录一、简介二、复制功能三、将服务器设置为从服务器3.1、手动设置3.2、REPLICAOF 配置选项3.3、取消复制四、查看服务器的角色4.1、查看主服务器4.2、查看从服务器五、其他配置5.1、无硬盘复制5.2、降低数据不一致情况出现的概率5.3、可写的从服务器5.4、选择性命令传播六、w…

SpringBoot统一功能处理

目录 一、统一用户的登录验证 1.1 Spring AOP 实现用户统一登录验证 1.2Spring拦截器实现统一用户的登录验证 1.3 实例演示&#xff08;通过url访问controller层的方法&#xff09; 二、统一异常处理 三、统一数据格式返回 3.1 统一数据返回格式的优点 3.2 统一数据返回…

旺季到来,跨境电商卖家年末冲刺!

又是一年年末时&#xff0c;随着新年的到来&#xff0c;在年底这段时间里&#xff0c;对于跨境电商卖家来说&#xff0c;又是一个关键节点。而现在&#xff0c;卖家们也将迎来一年一度的旺季收官&#xff0c;在此过程中卖家需要做好哪些准备做好年末冲刺呢&#xff1f; 在许多…

量化策略——准备3 数据、Backtrader回测框架与quantstats评价指标

我们一般使用AKShare这个库来获取股票数据或策略中用得到的数据&#xff1a; AKShare github主页&#xff1a;https://github.com/akfamily/akshare 使用Backtrader框架作为回测的框架&#xff1a; Backtrader github主页&#xff1a;https://github.com/mementum/backtrader …

【CTF】git源码泄露和代码审计

目录 源码获取 函数绕过 解法一&#xff1a; 解法二&#xff1a; 参考文章&#xff1a; 源码获取 这里做的web题目是buuctf中的&#xff1a;[GXYCTF2019]禁止套娃 打开页面&#xff0c;查看源码是没有可以利用的点的。开始进行目录扫描 这里使用的工具是dirsearch。直接…

前端js实现多次添加累加文件上传和选择删除(django+js)- 编辑回显文件并上传 (二)

前言 后端返回的是文件地址&#xff0c;并不是文件流或base64编码字符串&#xff0c;而修改数据的接口又只接受文件。 本篇文章主要是基于累加文件上传介绍的。 添加上传文件文章链接&#xff1a;https://blog.csdn.net/qq_43030934/article/details/128726549?spm1001.2014.…

jmeter 压测java代码

一、背景 直接压测、调用java工程中的方法。&#xff08;没有http等的入口&#xff09; 二、java项目改造 一个java项目&#xff0c;想要压测其中的几个方法。我们需要在该工程中&#xff0c;添加一个压测入口的类&#xff0c; 这个类必须继承或者实现jmeter提供的接口/类。…

C语言萌新如何使用scanf函数?

&#x1f40e;作者的话 如果你搜索输入输出函数&#xff0c;那么你会看到输入输出流、Turbo标准库、标准输出端、stdout什么什么乱七八糟的&#xff0c;作为一个萌新&#xff0c;哪懂这些&#xff1f; 本文介绍萌新在前期的学习中&#xff0c;常用的输入输出函数及其功能~ 跳…

HTML5+CSS3小实例:炫彩的发光字特效

前言&#xff1a; 今天我们向大家精选了一款HTML5CSS3文字特效&#xff0c;文字特效有超酷的动画类型&#xff0c;不多说&#xff0c;一起来看看。 描述&#xff1a; 这款文字特效既有倒影的效果&#xff0c;又有随机的颜色&#xff0c;看起来非常的炫酷。全文基于 HTML5CSS3 完…

log4j.properties自定义日志配置

一、通用的写法log4j.properties# 设置root logger等级为ALL&#xff0c;且appender有A1和FILE log4j.rootLoggerALL, A1,A3#设置com.example.test logger log4j.logger.com.example.testDEBUG,A1,A3 # 取消继承父类 log4j.additivity.com.example.testfalse# 设置个控制台输出…

即时通讯开发之TCP 连接的建立与中止

TCP 是一个面向连接的协议,所以在连接双方发送数据之前,都需要首先建立一条连接。这和前面讲到的协议完全不同。前面讲的所有协议都只是发送数据 而已,大多数都不关心发送的数据是不是送到,UDP 尤其明显,从编程的角度来说,UDP 编程也要简单 的多----UDP 都不用考虑数据分片。 书…