JS中call(),apply()是什么,call(),apply()的原理是什么?如何手写一个call(),apply()?Symbol是什么,怎么用Symbol调优?含详细解析

news2024/9/21 19:06:29

🎉call()

💕call()的参数

thisArg:在调用 func 时要使用的 this 值
arg1, …, argN (可选) 函数的参数

✨call()的描述:

首先声明 func是一个函数,person是一个对象
针对这段代码:func.call(person,‘a1’,‘a2’)
调用func方法并传递两个参数’a1’,‘a2’ ,以及把func中的this设置为person对象

🍧call()的代码解释

 function greet (a,b) {
	  console.log(this)
      console.log(this.animal, "的睡眠时间一般在", this.sleepDuration, "之间",a, b)
    }

    const obj = {
      animal: "猫",
      sleepDuration: "12 到 16 小时",
    }

    greet.call(obj, "哦", "!!!") // {animal: '猫', sleepDuration: '12 到 16 小时'}
    //猫 的睡眠时间一般在 12 到 16 小时 之间 哦 !!!

🎐 apply()

apply()和call()的差别只是传递的第一个参数之后的参数的形式不同,call()是分开写,apply是数组

func.call(person,1,2,3)  
func.apply(person,[1,2,3])

所以在这里详细讲解一下call()方法的实现
apply()的实现只是在call()接受参数的时候使用剩余参数
apply()使用一个数组,别的都一样

手写代码见本文末尾

🎡手写Call() :myCall()

❤️步骤

手写Call() 分为四步

  1. 定义myCall方法,加在Function原型上,使得所有函数都能点出来使用
  2. 设置this并调用原函数
  3. 接收剩余参数并返回结果
  4. 使用Symbol调优

🎶前置知识 this指向问题

  1. 全局执行环境中,指向全局对象window(非严格模式、严格模式)

  2. 函数内部,取决于函数被调用的方式
    2.1. 直接调用的this值:

    • 非严格模式:全局对象(window)
    • 严格模式:undefined

    2.2 对象方法调用的this值:

    • 调用者
  3. 开启严格模式

    • 脚本开启: ‘use strict’
    • 函数内部开启:‘use strict’
    • 注意:'use strict’写在代码顶端
 // ------------- 1.全局执行环境 -------------
    //  严格模式,非严格模式 全局对象(window)
    // 'use strict'
    // console.log(this)

    // ------------- 2.函数内部 -------------
    // 2.1 直接调用-非严格模式
    // function func() {
    //   console.log(this) // window
    // }
    // func()
    // 2.1 直接调用-严格模式
    // function func() {
    //   'use strict'
    //   console.log(this) // undefined
    // }
    // func()

    // 2.2 对象方法调用
    const food = {
      name: '猪脚饭',
      eat() {
        'use strict'
        console.log(this)
      }
    }
    // 非严格模式,严格模式
    food.eat() // 调用者 Object {eat: ƒ eat(),name: "猪脚饭"}

再来看一下MDN的解释 MDN地址

✨下面这点很重要

o.f() 就使得 函数 f 中的 this 指向 对象 o

在这里插入图片描述
先定义一个对象o,在定义一个函数independent(),然后把函数追加到对象o中和上述可以实现一样的效果
在这里插入图片描述

🎀第一步:定义myCall方法,加在Function原型上,使得所有函数都能点出来使用

在Function对象的原形上通过"."的方式添加myCall属性,并给这个对象赋值一个函数

  Function.prototype.myCall = function () {
      console.log("this is myCall")
    }
    function greet () { }
    greet.myCall()// this is myCall

🎶第二步:设置this并调用原函数

🎐图解

在这里插入图片描述
由于给person多加了一个f属性,所以后面需要使用 delete关键词 把f属性删掉

🎏代码

     
    Function.prototype.myCall = function (thisArg) {
      thisArg.f = this //这个this就是原函数func(因为 根据前置知识的讲解 func.mycall()使得函数myCall的this指向func )
      // ,这段代码是在person(thisArg在这里就是person)对象上面增加一个属性,属性名为f 属性值为func(){...}
      thisArg.f()//根据前置知识 f的this是thisArg,在这里f是func 这样就完成了第二步
 
      

    }
    // ------------- 测试代码 -------------
    const person = {
      name: 'zhangsan'
    }
    function func (numA, numB) {
      console.log(this)
      console.log(numA, numB)
      return numA + numB
    }
    const res = func.myCall(person) // {name: 'zhangsan', f: ƒ}


🎄第三步:接收剩余参数并返回结果

使用…args接收剩余参数,并用解构赋值的方法把参数传递给调用者


    Function.prototype.myCall = function (thisArg, ...args) {
      thisArg.f = this  
      const res = thisArg.f(...args) //args=>[2,8] ...args=>2,8 把剩余参数传给func (numA=2,numB=8)
      delete thisArg.f //删除person中新加的f属性
      return res
    }
   

    // ------------- 测试代码 -------------
    const person = {
      name: 'zhangsan'
    }
    function func(numA, numB) {
      console.log(this) //Object{name:zhangsan}
      console.log(numA, numB) //2 8
      return numA + numB
    }
    const res = func.myCall(person, 2, 8)
    console.log('返回值为:', res) // 返回值为: 10


在这里插入图片描述

🎉 测试

    Function.prototype.myCall = function (thisArg, ...args) {
      thisArg.f = this
      const res = thisArg.f(...args) 
      delete thisArg.f 
      return res
    }

// ------------- 测试代码 -------------
    const student = {
      name: 'lisi'
    }
    function func2 (a, b, c) {
      console.log(this)
      console.log(a, b, c)
      return a + b + c
    }
    const res2 = func2.myCall(student, 1, 2, 3)
    console.log('返回值:', res2)

在这里插入图片描述

✨第四步:使用Symbol调优

🎶关于Symbol

Symbol的MDN链接

symbol 是一种基本数据类型(primitive data type)。
Symbol() 函数会返回 symbol类型的值,该类型具有静态属性和静态方法。

每个从 Symbol() 返回的 symbol 值都是唯一的。一个 symbol 值能作为对象属性的标识符;

🍧symbol的使用

直接使用Symbol()创建新的 symbol 类型,并用一个可选的字符串作为其描述。这个描述只是为了看着方便没有实际用处。(MDN解释:对 symbol 的描述,可用于调试但不是访问 symbol 本身。)

var sym1 = Symbol();
var sym2 = Symbol("foo");
var sym3 = Symbol("foo");
// 这三个都不相等
   Function.prototype.myCall = function (thisArg, ...args) {
       
      const key = Symbol('key')// 使用Symbol创建一个唯一的symbol值 作为对象的标识符
      // thisArg.key 是给thisArg对象增加一个名为字符串'key'的属性
      thisArg[key] = this //thisArg[key] 是把key作为一个变量 实际传过去的是Symbol('key')
      const res = thisArg[key](...args)//接受到原函数的返回值
      delete thisArg[key]
      return res //把原函数的返回值返回出去
    }
    
    

🎉测试

   Function.prototype.myCall = function (thisArg, ...args) {
       
      const key = Symbol('key')// 使用Symbol创建一个唯一的symbol值 作为对象的标识符
      // thisArg.key 是给thisArg对象增加一个名为字符串'key'的属性
      thisArg[key] = this //thisArg[key] 是把key作为一个变量 实际传过去的是Symbol('key')
      const res = thisArg[key](...args)//接受到原函数的返回值
      delete thisArg[key]
      return res //把原函数的返回值返回出去
    }
   // ------------- 测试代码 -------------
 	const student = {
      name: 'lisi'
    }
    function func2 (a, b, c) {
      console.log(this)
      console.log(a, b, c)
      return a + b + c
    }
    const res2 = func2.myCall(student, 1, 2, 3)
    console.log('返回值:', res2)

这个是谷歌浏览器的结果,谷歌这点有点显示bug
在这里插入图片描述

Edge就没有,下图是Edge浏览器的结果
在这里插入图片描述

✨手写apply() :myApply()

   Function.prototype.myApply = function (thisArg, args) {//相较于myCall() 只是 ...args=>args
      const key = Symbol('key')
      thisArg[key] = this
      const res = thisArg[key](...args)
      delete thisArg[key]
      return res
    }
    // ------------- 测试代码 -------------
    const student = {
      name: 'lisi'
    }
    function func2 (a, b, c) {
      console.log(this)
      console.log(a, b, c)
      return a + b + c
    }
    const res2 = func2.myApply(student, [1, 2, 3])
    console.log('返回值:', res2)

🎉测试

在这里插入图片描述

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

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

相关文章

python中如何使用正则表达匹配\本身?(文末赠书)

点击上方“Python爬虫与数据挖掘”,进行关注 回复“书籍”即可获赠Python从入门到进阶共10本电子书 今 日 鸡 汤 将军向宠,性行淑均。 大家好,我是皮皮。 一、前言 前几天在Python钻石群【空】问了一个Python正则表达式的问题,一起…

CSS笔记(黑马程序员pink老师前端)定位

定位可以让盒子自由的在某个盒子内移动位置或者固定在屏幕中某个位置,并且可以压住其他盒子。 定位 定位模式 边偏移 定位模式说明static静态定位,按标准流特性摆放,没有边偏移,很少用relative相对定位,相对自身原有位置移动,原有位置继续占有(不脱标…

《好笑的爱》阅读笔记

《好笑的爱》阅读笔记 是暑期认识的一位川大的同学推荐的,说他喜欢的一个作家是米兰昆德拉,喜欢他的短篇小说集《好笑的爱》。于是去武汉中心书城顺便买了两本书,另外还买了一本是《帷幕》,至今还没有看。 这本书总共包含了几篇短…

JDK8的 ConcurrentHashMap 源码分析

目录 1. 导读 2. ConcurrentHashMap 成员变量解读 3. ConcurrentHashMap 初始化 3.1 ConcurrentHashMap 无参构造源码解读 3.2 ConcurrentHashMap 带参构造源码解读 3.3 tableSizeFor 方法作用解读 3.4 ConcurrenthashMap初始化总结 4. ConcurrentHashMap 添加元素方法…

14 - 多线程之锁优化(下):使用乐观锁优化并行操作

前两讲讨论了 Synchronized 和 Lock 实现的同步锁机制,这两种同步锁都属于悲观锁,是保护线程安全最直观的方式。 我们知道悲观锁在高并发的场景下,激烈的锁竞争会造成线程阻塞,大量阻塞线程会导致系统的上下文切换,增…

springBoot-使用idea创建项目添加依赖并实现数据查询

一、使用idea创建springBoot项目 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://mave…

Python小知识 - Python装饰器

Python装饰器 在Python中&#xff0c;装饰器是一个特殊的函数&#xff0c;可以将其他函数包装在装饰器函数中&#xff0c;并且将被包装的函数作为参数传递给装饰器函数。 使用装饰器的好处是可以自动在被包装的函数前后执行一些额外的代码&#xff0c;比如在函数执行前后打印日…

Linux之防火墙

目录 什么是防火墙 分类&#xff1a; Netfilter(数据包过滤) 防火墙无法完成的任务 iptables 与 firewalld 区别 iptables iptables执行原则 规则链 概念 分析 流程图 规则链分类 iptables 流量处理动作 iptables表 四种规则表 安装iptables 预处理 管理命令 …

SpringBoot整合RabbitMQ图文过程以及RabbitTemplate常用API介绍

&#x1f9d1;‍&#x1f4bb;作者名称&#xff1a;DaenCode &#x1f3a4;作者简介&#xff1a;啥技术都喜欢捣鼓捣鼓&#xff0c;喜欢分享技术、经验、生活。 &#x1f60e;人生感悟&#xff1a;尝尽人生百味&#xff0c;方知世间冷暖。 &#x1f4d6;所属专栏&#xff1a;Sp…

GUI知识点总结(二)(java)

文章目录 &#x1f412;个人主页&#x1f3c5;JavaSE系列专栏&#x1f4d6;前言&#xff1a;&#x1f993;事件 &#x1f3e8;Adapter 适配器&#x1f415;对话框&#x1f98d;showMessageDialog()&#xff1a;消息对话框&#x1f98d;showConfirmDialog()&#xff1a;确认对话…

ChatGPT 插件 “Consensus“ 实现论文搜索功能;数据工程在语言建模中的重要性

&#x1f989; AI新闻 &#x1f680; ChatGPT 插件 “Consensus” 实现论文搜索功能 摘要&#xff1a;OpenAI 推出了一个名为 “Consensus” 的插件&#xff0c;可在 ChatGPT 上进行论文搜索。用户只需用一句话描述自己想了解的问题&#xff0c;插件就能从 2 亿篇论文中搜索并…

Tomcat架构设计源码剖析

Tomcat架构设计&源码剖析 Tomcat 架构设计 Tomcat的功能&#xff08;需求&#xff09; 浏览器发给服务端的是一个 HTTP 格式的请求&#xff0c;HTTP 服务器收到这个请求后&#xff0c;需要调用服务端程序来处理&#xff0c;所谓的服务端程序就是你写的 Java 类&#xff…

实现 js 中所有对象的深拷贝(包装对象,Date 对象,正则对象)

通过递归可以简单实现对象的深拷贝&#xff0c;但是这种方法不管是 ES6 还是 ES5 实现&#xff0c;都有同样的缺陷&#xff0c;就是只能实现特定的 object 的深度复制&#xff08;比如数组和函数&#xff09;&#xff0c;不能实现包装对象 Number&#xff0c;String &#xff0…

如何压缩图片大小?缩小图片体积跟我学

在日常生活中&#xff0c;我们常常需要处理图片&#xff0c;但是由于图片大小过大&#xff0c;常常带来许多不便。那么&#xff0c;如何压缩图片大小呢&#xff1f;下面就为大家介绍三个方法&#xff0c;让你轻松解决这个问题。 一、使用图片编辑软件 市面上有许多图片编辑软件…

使用HTTP代理上网安全吗?

HTTP代理是一种代理服务器&#xff0c;它可以充当客户端和服务器之间的中介&#xff0c;以帮助客户端访问服务器上的资源。虽然使用HTTP代理可以带来一些便利&#xff0c;但是在安全方面也存在一些问题。 HTTP代理的安全问题 窃取用户信息 如果HTTP代理服务器不受信任&#xff…

【计算机网络】https协议

目录 概念的准备 什么是加密 为什么需要加密 常见的加密方式 对称加密 非对称加密 数据摘要(数字指纹) 数字签名 https的工作过程 方案一&#xff1a;只使用对称加密 方案二&#xff1a;只使用非对称加密 方案三&#xff1a;双方都采用非对称加密 方案四&#xff…

【Spring Boot 源码学习】深入 FilteringSpringBootCondition

走近 AutoConfigurationImportFilter 引言往期内容主要内容1. match 方法2. ClassNameFilter 枚举类3. filter 方法 总结 引言 前两篇博文笔者带大家从源码深入了解了 Spring Boot 的自动装配流程&#xff0c;其中自动配置过滤的实现由于篇幅限制&#xff0c;还未深入分析。 …

2023国赛数学建模C题思路模型 - 蔬菜类商品的自动定价与补货决策

# 1 赛题 在生鲜商超中&#xff0c;一般蔬菜类商品的保鲜期都比较短&#xff0c;且品相随销售时间的增加而变差&#xff0c; 大部分品种如当日未售出&#xff0c;隔日就无法再售。因此&#xff0c; 商超通常会根据各商品的历史销售和需 求情况每天进行补货。 由于商超销售的蔬菜…

uniapp使用webview将页面转换成图片支持h5、app、小程序

uniapp使用webview将页面转换成图片支持h5、app、小程序 在uniapp项目中新建主页和webview页面 index.vue代码 <template><view><!-- 微信小程序要设置src为网络路径 --><web-view src"/hybrid/html/webview.html"></web-view><…

十四、内置模块path、邂逅Webpack和打包过程、css-loader

一、内置模块path &#xff08;1&#xff09;path介绍 &#xff08;2&#xff09; path常见的API 这里重点讲一下path.resolve()。 看上面的例子&#xff0c;从右往左开始解析&#xff0c;所以一开始解析的就是 /abc.txt &#xff0c;这个时候就会把它当成一个绝对路径了&am…