call、apply、bind的区别与应用场景

一、概念

为什么会有call和apply?

call和apply两个方法的作用基本相同,它们都是为了改变某个函数执行时的上下文(context)而建立的, 他的真正强大之处就是能够扩充函数赖以运行的作用域。通俗一点讲,就是改变函数体内部this 的指向

举个栗子:

 

javascript

复制代码

window.color = "red"; var o = {color: "blue"}; function sayColor(){ alert(this.color); } sayColor();//red sayColor.call(this);//red,把函数体sayColor内部的this,绑到当前环境(作用域)(这段代码所处的环境) sayColor.call(window);//red,把函数体sayColor内部的this,绑到window(全局作用域) sayColor.call(o);//blue

解释: 上面的栗子,很明显函数sayColor是在全局作用域(环境/window)中调用的,而全局作用域中有一个color属性,值为"red",sayColor.call(this)这一行代码就是表示把函数体sayColor内部的this,绑到当前环境(作用域),而sayColor.call(window)这一行代码就是表示把函数体sayColor内部的this,绑到window(全局作用域),之所以这两行的输出都是"red"就是因为他当前作用域的this就是window(this === window); 最后,sayColor.call(o)这一行代码就表示把函数体sayColor内部的this,绑到o这个对象的执行环境(上下文)中来,也就是说sayColor内部的this——>o

二、call( thisValue , arg1, arg2, ... )

 

javascript

复制代码

window.color = "red"; var o = {color: "blue"}; function sayColor(){ alert(this.color); } sayColor.call(this);//red sayColor.call(window);//red sayColor.call(); sayColor.call(null); sayColor.call(undefined); sayColor.call(o);//blue

注意:如果call方法没有参数,或者参数为null或undefined,则等同于指向全局对象

应用场景

  • 判断对象类型
 

javascript

复制代码

var arr = []; Object.prototype.toString.call(arr); // [object Array] //把函数体Object.prototype.toString()方法内部的this,绑到arr的执行环境(作用域)

  • 同样是检测对象类型,arr.toString()的结果和Object.prototype.toString.call(arr)的结果不一样,这是为什么?

这是因为toString()为Object的原型方法,而Array ,function等引用类型作为Object的实例,都重写了toString方法。不同的对象类型调用toString方法时,根据原型链的知识,调用的是对应的重写之后的toString方法(function类型返回内容为函数体的字符串,Array类型返回元素组成的字符串.....),而不会去调用Object上原型toString方法,所以采用arr.toString()不能得到其对象类型,只能将arr转换为字符串类型;因此,在想要得到对象的具体类型时,应该调用Object上原型toString方法。 参考: developer.mozilla.org/zh-CN/docs/…

手撕call

 

javascript

复制代码

var foo = { count: 1 }; function bar() { console.log(this.count); } bar.myCall(foo); // 1 -------------------------------------------------------------------- Function.prototype.myCall = function(context) { // 取得传入的对象(执行上下文),比如上文的foo对象,这里的context就相当于上文的foo // 不传第一个参数,默认是window, var context = context || window; // 给context添加一个属性,这时的this指向调用myCall的函数,比如上文的bar函数 context.fn = this;//这里的context.fn就相当于上文的bar函数 // 通过展开运算符和解构赋值取出context后面的参数,上文的例子没有传入参数列表 var args = [...arguments].slice(1); // 执行函数(相当于上文的bar(...args)) var result = context.fn(...args); // 删除函数 delete context.fn; return result; };

详见ECMA规范:tc39.es/ecma262/mul…

三、apply( thisValue , [arg1, arg2, ...] )

很明显,我们看标题的可以知道call和apply的一个区别了,它们两个唯一的区别就是传参列表的不同,apply是接收的参数是一个数组。

手撕apply

 

javascript

复制代码

var foo = { count: 1 }; function bar() { console.log(this.count); } bar.myApply(foo); // 1 -------------------------------------------------------------------- Function.prototype.myApply = function(context) { var context = context || window; context.fn = this; var result; // 判断第二个参数是否存在,也就是context后面有没有一个数组 // 如果存在,则需要展开第二个参数 if (arguments[1]) { result = context.fn(...arguments[1]); } else { result = context.fn(); } delete context.fn; return result; }

详见ECMA规范:tc39.es/ecma262/mul…

应用场景

  1. 找出数组中最大或最小的元素
 

javascript

复制代码

var a = [10, 2, 4, 15, 9]; Math.max.apply(Math, a); // 15 Math.min.apply(null, a); // 2 /* ES6的方法 */ Math.max(...[10, 2, 4, 15, 9]); // 12 等同于Math.max(10, 2, 4, 15, 9);

  1. 可以将一个类似(伪)数组的对象(比如arguments对象)转为真正的数组。 前提: 被处理的对象必须有length属性,以及相对应的数字键。
 

javascript

复制代码

var ArrayLike = { // 一个类似数组的对象 0: 'a', 1: 'b', 2: 'c', length: 3 } //接收的是对象,返回的是数组 Array.prototype.slice.apply({0: 1, length: 1}) // [1] Array.prototype.slice.apply({0: 1}) // [] Array.prototype.slice.apply({0: 1, length: 2}) // [1, undefined] Array.prototype.slice.apply({length: 1}) // [undefined] //(切下)[].slice(1, n),返回索引为1到索引为n-1的数组 /* ES6的方法 */ Array.from(ArrayLike); // ["a", "b", "c"] // 没有部署Iterator接口的类似数组的对象,扩展运算符就“无法”将其转为真正的数组,但Array.from()可以 ...ArrayLike; // Found non-callable @@iterator // 只要是部署了Iterator接口的数据结构,Array.from 都能将其转为数组

  1. 数组追加
 

javascript

复制代码

var arr1 = [1,2,3]; var arr2 = [4,5,6]; [].push.apply(arr1, arr2); console.log(arr1); // [1, 2, 3, 4, 5, 6] console.log(arr2); // [4, 5, 6] /* ES6的方法 */ arr1.push(...arr2); // [1, 2, 3, 4, 5, 6]

  1. 数组合并
 

javascript

复制代码

var arr1 = [1, 2, { id: 1, id: 2 }, [1, 2]]; var arr2 = ['ds', 1, 9, { name: 'jack' }]; // var arr = arr1.concat(arr2);//简单做法 Array.prototype.push.apply(arr1,arr2) console.log(arr1); /* ES6的方法 */ [...arr1,...arr2]

四、bind( thisArg[, arg1[, arg2[, ...]]])

developer.mozilla.org/zh-CN/docs/…

call和apply它们两个是改变this的指向之后立即调用该函数,而bind则不同,它是创建一个新函数,我们必须手动去调用它。

MDN说法: bind()方法创建一个新的函数,在调用时设置this关键字为提供的值。并在调用新函数时,将给定参数列表作为原函数的参数序列的前若干项。(虽然这句话我还不太懂)

  • bind()是ES5新增的一个方法
  • 传参和call或apply类似
  • 不会执行对应的函数,call或apply会自动执行对应的函数
  • bind会返回对函数的引用

举个栗子

 

javascript

复制代码

var a ={ name : "Cherry", fn : function (a,b) { console.log( a + b) } } var b = a.fn; b.call(a,1,2);//立即调用该函数 b.bind(a,1,2)();//手动调用(),它返回一个原函数的拷贝(新的,不是原函数),并拥有指定的this值和初始参数。

 

javascript

复制代码

var obj = { name:'JuctTr', age: 18 }; /** * 给document添加click事件监听,并绑定ExecuteFun函数 * 通过bind方法设置ExecuteFun的this为obj */ //document.addEventListener('click',ExecuteFun.call(obj),false); document.addEventListener('click',ExecuteFun.bind(obj),false); function ExecuteFun(a,b){ console.log(this.name, this.age); }

如果把bind换成call或apply,页面控制台会直接输出JuctTr 18,因为call和apply是改变this的指向之后立即执行ExecuteFun函数,而bind它是返回一个新的函数

应用场景

手撕bind

 

javascript

复制代码

Function.prototype.myBind = function (context) { if (typeof this !== 'function') { throw new TypeError('Error') } var _this = this // 获取除context之外,后面的参数 // var args = Array.prototype.slice.call(arguments, 1); // ES5的写法 var args = [...arguments].slice(1) // ES6的方式 // 返回一个函数 return function F() { // 因为返回了一个函数,我们可以 new F(),所以需要判断 if (this instanceof F) { return new _this(...args, ...arguments) } // 组合参数 // var innerArgs = Array.prototype.slice.call(arguments); // ES5的写法 // const finalArgs = args.concat(innerArgs) // ES5的写法 // return _this.apply(context, finalArgs) // ES5的写法 return _this.apply(context, args.concat(...arguments)) //ES6的方式 } }

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

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

相关文章

在project模式下自定义Implementation Strategies

Implementation Settings定义了默认选项,当要定义新的implementation runs时会使用这些选项,选项的值可以在Vivado IDE中进行配置。 图1展示了“Settings”对话框中的“implementation runs”对话框。要从Vivado IDE打开此对话框,请从主菜单中…

网络通信(一)

网络编程 互联网时代,现在基本上所有的程序都是网络程序,很少有单机版的程序了。 网络编程就是如何在程序中实现两台计算机的通信。 Python语言中,提供了大量的内置模块和第三方模块用于支持各种网络访问,而且Python语言在网络…

QT实现NTP功能

一.NTP基础 1.NTP定义 NTP(Network Time Protocol,网络时间协议)是由RFC 1305定义的时间同步协议,用于分布式设备(比如电脑、手机、智能手表等)进行时间同步,避免人工校时的繁琐和由此引入的误…

Web日志/招聘网站/电商大数据项目样例【实时/离线】

Web服务器日志分析项目 业务分析 业务背景 ​ 某大型电商公司,产生原始数据日志某小时达4千五万条,一天日志量月4亿两千万条。 主机规划 (可略)日志格式: 2017-06-1900:26:36101.200.190.54 GET /sys/ashx/ConfigH…

设计模式:枚举如何实现单例模式

枚举实现单例模式是在Java 1.5中引入的一种创建单例的方式,它不仅能避免多线程同步问题,还能防止反序列化重新创建新的对象。 原理 枚举单例的原理基于Java枚举类型的特性: 自由序列化:枚举实例的序列化机制由JVM保证,每个枚举类型及其定义的枚举变量在JVM中都是唯一的。…

预处理指令详解

前言 上一节我们了解了文件操作的相关内容,本节我们来了解一下预处理指令,那么废话不多说,我们正式开始今天的学习 预定义符号 在C语言中,设置了一些预定义的符号,可以供我们直接使用,预定义符号是在程序…

一场人生的风险控制,商业社会识人指南

一、资料前言 本套社会识人资料,大小679.94M,共有37个文件。 二、资料目录 识人的终极目的:一整场人生的风险控制.pdf 信任的搭建:更多的时间与维度.pdf 没有搞不定的人!角色人格与全面人格.pdf 政治不正确的正确…

程序员为什么不能一次性写好,需要一直改Bug?

程序员为什么不能一次性写好,需要一直改Bug? 我有一问: 你为什么不上清华呢,高考答满分不就行了? 程序员在软件开发过程中可能会遇到需要不断修改Bug的情况,这主要是由以下几个原因造成的: 复杂…

Linux简单介绍

Linux简单介绍 编译器VMware虚拟机Ubuntu——LinuxOS为什么使用LinuxOS? 目录结构Windows目录结构Linux操作系统home是不是家目录? Linux常用命令终端命令行提示符与权限切换命令tab 作用:自动补全上下箭头pwd命令ls命令mkdir命令touch命令rm…

智能革命:ChatGPT3.5与GPT4.0的融合,携手DALL·E 3和Midjourney开启艺术新纪元

迷图网(kk.zlrxjh.top)是一个融合了顶尖人工智能技术的多功能助手,集成了ChatGPT3.5、GPT4.0、DALLE 3和Midjourney等多种智能系统,为用户提供了丰富的体验。以下是对这些技术的概述: ChatGPT3.5是由OpenAI开发的一个自然语言处理模型&#x…

第17章 反射机制

一 反射(Reflection)的概念 1.1 反射的出现背景 Java程序中,所有的对象都有两种类型:编译时类型和运行时类型,而很多时候对象的编译时类型和运行时类型不一致。 Object obj new String(“hello”); obj.getClass() 例如:某些变…

C++多线程:单例模式与共享数据安全(七)

1、单例设计模式 单例设计模式,使用的频率比较高,整个项目中某个特殊的类对象只能创建一个 并且该类只对外暴露一个public方法用来获得这个对象。 单例设计模式又分懒汉式和饿汉式,同时对于懒汉式在多线程并发的情况下存在线程安全问题 饿汉…

深入理解计算机系统 家庭作业 2.75

/* 书中的公式是w位的公式(mod 就是为了截断成w位),我们现在做的是2w位中的前w位 注意书上这句话:由于模运算符,所有带有权重和的项都丢掉 对应到本题 该项除以后还是超过了2w位所以被丢弃了,因为题目说了只有2w位 这个式子除以就是我们想要的最终结果 函数signed_high_p…

前端学习<四>JavaScript基础——03-常量和变量

常量(字面量):数字和字符串 常量也称之为“字面量”,是固定值,不可改变。看见什么,它就是什么。 常量有下面这几种: 数字常量(数值常量) 字符串常量 布尔常量 自定义…

【独立开发前线】Vol.35 绝大部分人独立开发失败的原因

我自己是一名独立开发者,我也见过几百个独立开发者,有成功的,也有失败的。 成功者的特质各不相同,但失败的都有相同的几点; 程序员做独立开发是很有优势的,通过编程的能力,可以快速实现产品。…

Vol.34 Good Men Project:一个博客网站,每月90万访问量,通过付费订阅和广告变现

今天给大家分享的案例网站是:Good Men Project,这是一个专门针对男性成长的博客网站,内容包括人际关系、家庭、职业发展等话题。 它的网址是:The Good Men Project - The Conversation No One Else Is Having 流量情况 我们先看…

高分卫星助力台湾省花莲县地震应急救援

4月3日7时58分,在台湾省花莲县海域(北纬23.81度,东经121.74度)发生7.3级地震,震源深度12公里。接中国地震局地震预测研究所应急需求,国家航天局对地观测与数据中心(以下简称“中心”&#xff09…

各省-科技创新、研发强度数据集(2007-2022年)

01、数据简介 研发强度是科学技术发展的重要指标,可以反映出一个国家或地区的科技实力和创新能力。研发强度越高,就意味着该地区的科技创新能力越强。 本数据是对全国各省份研发强度统计,数据年份2007至2022年,采用科技支出占GD…

C#仿OutLook的特色窗体设计

目录 1. 资源图片准备 2. 设计流程: (1)用MenuStrip控件设计菜单栏 (2)用ToolStrip控件设计工具栏 (3)用StatusStrip控件设计状态栏 (4)ImageList组件装载树节点图…

SQLyog连接数据库8.0版本解析错误问题解决方案

问题描述: 解决方案: alter userrootlocalhostidentified with mysql_native_password by 密码; 再次连接就可以了。