6.函数是特殊的对象2 - JS

在第一部分中,主要总结了函数作为一个对象的常见属性(name/length)、如何自定义属性以及如何使用函数构造器(Function)。

这里总结函数作为对象的常见方法(apply/call/bind/toString)。


使用 call 绑定 this

call 方法指定 this 并且逐个提供参数。

基本语法

  • thisArg,调用 f 时指定的 this 值:
    • 非严格模式下,null/undefined 将被替换为全局对象,
    • 原始值(String/Number/...)将被转换为对象;
  • arg1, arg2, /* …, */ argN 函数的参数,逐个传入。
f.call(thisArg)
f.call(thisArg, arg1)
f.call(thisArg, arg1, arg2)
f.call(thisArg, arg1, arg2, /* …, */ argN)

简单案例

下面一个案例:普通调用时,this 指向全局;call 调用时,this 指向具体对象。

function log() {console.log(this.xxx);console.log(this);
}
let o = { xxx: 100, yyy: 200 }log();			// undefined或其他(globalThis.xxx) - globalThis/Window
log.call(o)		// 100 - { xxx: 100, yyy: 200 }
log.call()		// undefined或其他(globalThis.xxx) - globalThis/Window
log.call(null)	// undefined或其他(globalThis.xxx) - globalThis/Window

上面的例子中,undefined/null 将转换成全局对象,但在严格模式下:

"use strict"
function log() {console.log(this);
}log.call()			// undefined
log.call(null)		// null

借用方法

一个对象的方法,给另外一个对象使用。达到目的方案如下:

  1. 直接通过对象调用,并且通过 call 指定其他的对象;
  2. 先将方法赋值成为一个函数,在通过 call 的调用指定其他的对象;
  3. 先将方法赋值成为一个函数,并对 call 进行自绑定。
const cat = {name: 'Kitty',showName () { console.log(this.name); }
}
const dog = {name: '阿旺'
}cat.showName.call(dog)			// '阿旺'let showName = cat.showName
showName.call(dog)				// '阿旺'let boundedShowName = showName.call.bind(showName)
boundedShowName(dog)			// '阿旺'

对象原型链上的方法也可以借用。下面一个例子中,借用数组原型链上的切片方法。

let arr = [1, 2, 3, 4];
const slice = Array.prototype.slice;
const boundedSlice = slice.call.bind(slice);slice.call(arr, 1)		// [2, 3, 4]
boundedSlice(arr, 1)	// [2, 3, 4]

apply 方法

applycall 方法能够达到几乎等效目的。

基本语法

  • thisArg,调用 f 时指定的 this 值:
    • 非严格模式下,null/undefined 将被替换为全局对象,
    • 原始值(String/Number/...)将被转换为对象;
  • argsArray,类数组对象,调用 f 时的参数(或者如果不需要向函数提供参数,则为 null/undefined)。
f.apply(thisArg)
f.apply(thisArg, argsArray)

apply VS call

大多数情况下,使用 callapply 均可,apply 可能会更快,因为大多数 JS 引擎在内部对其进行了优化。下面一个等效表达中:

  • arr 期望是一个类数组对象;
  • itor 期望是一个可迭代对象。
f.apply(thisArg, arr)
f.call(thisArg, ...itor)

可以看出,apply/call 两者几乎相同,不同的是参数方式,参数在 call() 中逐个作为列表传递,而在 apply() 中它们会组合在一个类数组对象中。

Math.max.apply(null, [1, 3, 5])		// 5
Math.max.call(null, 1, 3, 2)		// 3

简单案例

下面一个例子中:在一个数组后添加若干元素。

let arr = ["a", "b"];
arr.push.apply(arr, [0, 1, 2]);
arr				// ["a", "b", 0, 1, 2]arr.push.call(arr, 3, 4)
arr				// ["a", "b", 0, 1, 2, 3, 4]arr.push(arr, ...[5, 6])
arr				// ["a", "b", 0, 1, 2, 3, 4, 5, 6]

呼叫转移

将所有参数、上下文一起传递给另一个函数被称为“呼叫转移”。下面是一种最简单的表达,在外部看来 f()w() 没有声明区别。

function f() { console.log(arguments[0]) }
function w() { f.apply(this, arguments) }f()		// 1
w()		// 1

使用 bind 创建一个 Bounded Function - 绑定函数

bind 方法创建一个新函数,当调用该新函数时,它会调用原始函数(目标函数-Target Function):

  • 将其 this 上下文绑定为指定值;
  • 同时,还可以绑定一系列参数(会插到新函数传入参数的前面)。

基本语法

  • thisArg,调用绑定函数给原始函数 f 时指定的 this 值:
    • 非严格模式下,null/undefined 将被替换为全局对象,
    • 原始值(String/Number/...)将被转换为对象,
    • 如果使用 new 创建绑定函数,忽略该值;
  • arg1, arg2, /* …, */ argN,在调用原始函数 f 时,逐个插入到传入绑定函数的参数前的参数。。
f.bind(thisArg)
f.bind(thisArg, arg1)
f.bind(thisArg, arg1, arg2)
f.bind(thisArg, arg1, arg2, /* …, */ argN)

bind 参数的绑定顺序

给函数指定 this 值,并不复杂。下面一个案例具体讲述 bind 参数的绑定顺序问题。

  • boundedFn1 成功地绑定了 this 对象 为一个字符串 xy,并且将两个其余参数绑定,在调用时:
    • 目标函数 fnthis 指定为 xy
    • 通过 bind 绑定的其他参数插入在目标函数参数列表的前面,相当于 fn('a', 'b', '1', '2')
  • boundedFn2 失败地绑定了 this 对象 为一个字符串 xxyy,并且将两个其余参数绑定,在调用时:
    • 目标函数 fnthis 指定仍然xy(因为 boundedFn1 已经是一个绑定函数,this 的指定不会被覆盖);
    • 通过 bind 绑定的其他参数插入在目标函数参数列表的前面,以及第一次绑定参数的后面,相当于 fn('a', 'b', 'c', 'd', '1', '2')
function fn(...args) {console.log(this, args.join(''));
}let boundedFn1 = fn.bind('xy', 'a', 'b')
boundedFn1('1', '2')		// String {'xy'} 'ab12'let boundedFn2 = boundedFn1.bind('xxyy', 'c', 'd')
boundedFn2('1', '2')		// String {'xy'} 'abcd12'

一般的,对于多层绑定函数,this 指定是第一层;其他参数按照层数,依次插入在目标函数的参数列表前面(fn(...argsFloor1, /* ... */, argsFloorX, ...args))。

使用 toString 方法获取函数源码字符串

  1. 函数作为对象,重写了从 Object 继承来的 toString() 方法。toString 方法返回一个包含用于定义函数的源文本段的字符串。
  2. 对于内置函数、由 bind 创建的绑定函数、非 JavaScript 函数,调用 toString(),返回一个看起来像原函数的字符串。
function f() {console.log(111)
}f.toString()				// 'function g() { \n    console.log(111) \n}'
g.bind(null).toString()		// 'function () { [native code] }'
Math.max.toString()			// 'function max() { [native code] }'

实际源代码与 toString() 结果比较

注意:toString 方法可以修改。

function f() { }
class A {a() { }
}
function* g() {	}f.toString()			// "function f() { }"			普通函数
A.toString()			// "class A { a() { } }"		类
g.toString()			// "function* g() { }"			生成器
((a) => a).toString()		// "(a) => a"				箭头函数
({ a() {} }.a).toString()	// "a() {}"					方法
({ *a() {} }.a).toString()  // "*a() {}"				生成器方法
({ [x]() {} }[x]).toString()// "[x]() {}"				计算方法
Function.prototype.toString.toString()	// "function toString() { [native code] }"
f.bind(null).toString()					// "function () { [native code] }"
(Function("a", "b")).toString()			// function anonymous(a\n) {\nb\n}
(Object.getOwnPropertyDescriptor({ get a() {} }, "a").get).toString()	// "get a() {}"
(Object.getOwnPropertyDescriptor({ set a(x) {} }, "a").set).toString()	// "set a(x) {}"

关于什么的计算方法/属性:计算变量不能点式读取。

let [x, y, z] = ['ad', 'cc', 'gg']
let o = { [x]: 11, [y]: function() {}, [z] (){} }o.ad	// 11
o.cc	// f () {}
o.gg	// ƒ [z](){}
o[x]	// 11
o[y]	// f () {}
o[z]	// ƒ [z](){}
o.x		// undefined
o.y		// undefined
o.z		// undefined

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

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

相关文章

一起学量化之Aroon指标

Aroon指标是由Tushar Chande于1995年开发的技术分析工具,旨在识别股票是否处于趋势中及趋势的强度。它通过分析股票价格在一定周期内创下的新高和新低来预测趋势的变化,这基于一种观念:强势趋势通常伴随着频繁的新高或新低。 1. Aroon指标的组成 Aroon指标由两个部分组成:…

ADC--模拟量转换成数字量

目录 一、ADC硬件组成七大部分: 二、单次转换,连续转换,不连续采样模式,扫描模式区别 1、举例(5种组合情况) 2、模拟看门狗中断的作用: 三、MCU使用ADC步骤 一、ADC硬件组成七大部分: ①输入电压&#…

103.网游逆向分析与插件开发-网络通信封包解析-网络完成端口模型的流程

内容参考于:易道云信息技术研究院VIP课 上一个内容:解读喊话道具数据包并且利用Net发送 通过之前的内容,把游戏的网络通信中关于发送数据的分析好了(任意涉及网络的应用程序里发送数据的数据包定位与分析都可以参考之前的内容&a…

Java实战:构建智能工作量统计系统

✍✍计算机编程指导师 ⭐⭐个人介绍:自己非常喜欢研究技术问题!专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目:有源码或者技术上的问题欢迎在评论区一起讨论交流! ⚡⚡ Java实战 |…

如何清除谷歌浏览器的缓存?这里有详细步骤

如果你想解决加载或格式化问题,以改善你在谷歌Chrome上的浏览体验,那么清除缓存和cookie是一个很好的开始。以下是删除它们的方式和操作。 删除缓存和cookie时会发生什么 当你访问一个网站时,它有时会保存(或记住)某…

MybatisPlus大量数据批量新增优化

首先我们如果for循环依次遍历插入数据效率是十分底下的&#xff0c;大概耗时5分钟左右。 for循环插入测试代码&#xff1a; Test void testSaveOneByOne() {long b System.currentTimeMillis();for (int i 1; i < 100000; i) {userService.save(buildUser(i));}long e …

DS:八大排序之堆排序、冒泡排序、快速排序

创作不易&#xff0c;友友们给个三连吧&#xff01;&#xff01; 一、堆排序 堆排序已经在博主关于堆的实现过程中详细的讲过了&#xff0c;大家可以直接去看&#xff0c;很详细,这边不介绍了 DS&#xff1a;二叉树的顺序结构及堆的实现-CSDN博客 直接上代码&#xff1a; …

算法||实现典型数据结构的查找、添加和删除数据 并分析其时间和空间复杂度

实现典型数据结构的查找、添加和删除数据 并分析其时间和空间复杂度 线性结构&#xff1a; 数组&#xff1a;是一种线性表数据结构&#xff0c;它用一组连续的内存空间&#xff0c;来存储一组具有相同类型的数据。 查找数据 &#xff1a;随机访问 流程图 /** 查询元素下标…

QML | 属性特性(property)

一、属性特性 属性是对象的一个特性,可以分配一个静态的值,也可以绑定一个动态表达式。属性的值可以被其他对象读取。一般而言,属性的值也可以被其他对象修改,除非显式声明不允许这么做,也就是声明为只读属性。 1.定义属性特性 属性可以在C++中通过注册一个类的Q_PROPERT…

anaconda安装路径默认在D盘,但安装环境的envs路径跑到C盘,修改为D盘

安装的anaconda环境&#xff0c;路径是在anaconda安装目录下的envs中&#xff08;D:\APPFile\Anaconda3\envs&#xff09;&#xff0c;然而&#xff0c;这次创建的却是在 C:\Users\xxx.conda\envs 中。 首先&#xff0c;找到用户目录下的.condarc文件&#xff08;C:\Users\use…

随想录刷题笔记 —二叉树篇7 617合并二叉树 700二叉搜索树中的搜索 98验证二叉搜索树

617合并二叉树 递归&#xff1a;如果root1和root2其中有一个为空&#xff0c;则将另一个的结点直接赋值即可——将该节点和子树都直接赋值过去了。 如果都不是空&#xff0c;就需要重新建立一个结点再进入递归。 class Solution {public TreeNode mergeTrees(TreeNode root1…

力扣题目训练(12)

2024年2月5日力扣题目训练 2024年2月5日力扣题目训练476. 数字的补数482. 密钥格式化485. 最大连续 1 的个数148. 排序链表164. 最大间距 2024年2月5日力扣题目训练 2024年2月5日第十二天编程训练&#xff0c;今天主要是进行一些题训练&#xff0c;包括简单题3道、中等题2道和…

LeetCode--代码详解 231.2的幂

231. 2的幂 题目 给你一个整数 n&#xff0c;请你判断该整数是否是 2 的幂次方。如果是&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 如果存在一个整数 x 使得 n 2x &#xff0c;则认为 n 是 2 的幂次方。 示例 1&#xff1a; 输入&#xff1a;n 1…

一起学量化之DMI指标

DMI指标(Directional Movement Index)是由J. Welles Wilder在1978年开发的,用于分析资产价格动向和趋势强度的技术指标。DMI包括两条主要的线:正向指标(+DI)和负向指标(-DI),以及衡量趋势强度的平均方向指数(ADX)。 1. DMI指标的组成 +DI(正向指标):测量上升趋势…

项目第一次git commit后如何撤销

问题描述&#xff1a; # 1. 新建gitcode目录&#xff0c;然后在目录下 git init# 2. 用idea打开目录后&#xff0c;新建.gitignore文件后 git add .git commit -m "init project"git log# 3. 就出现如下图情况目的&#xff1a;向撤销该次代码提交 # 仅撤销 git com…

Stable Diffusion教程——常用插件安装与测试(一)

前言 随着Stable Diffusion不断演进&#xff0c;越来越多的开发者开始涉足插件开发。尽管网络上存在大量教程&#xff0c;但它们通常零散分布&#xff0c;逐个学习和查找非常耗时&#xff0c;使人感觉每天都在劳累思考。这里总结了Stable Diffusion常用的插件安装与测试方法。…

表的操作【mysql数据库】

目录 一、创建表 二、查看表 三、修改表 改表名&#xff1a; 新增一列&#xff1a; 修改某列的属性&#xff1a; 删除某列&#xff1a; 改列名 四、删除表 一、创建表 二、查看表 desc&#xff1a;查看表的详细信息 查看建表时的详细信息&#xff1a; 三、修改表 改表…

第15讲 用户投票实现

用户投票实现 新建用户投票详情表t_vote_detail create table t_vote_detail (id int (11),vote_id int (11),vote_item_id int (11),vote_date datetime ,openid varchar (600) ); /* SQLyog Ultimate v11.33 (64 bit) MySQL - 5.7.18-log : Database - db_vote3 **********…

springboot/ssm自动售货机系统Java在线购物商城管理系统

springboot/ssm自动售货机系统Java在线购物商城管理系统 开发语言&#xff1a;Java 框架&#xff1a;springboot&#xff08;可改ssm&#xff09; vue JDK版本&#xff1a;JDK1.8&#xff08;或11&#xff09; 服务器&#xff1a;tomcat 数据库&#xff1a;mysql 5.7&…

Java多线程系列——(一)概述

简介 在计算机编程中&#xff0c;多线程是一种重要的概念&#xff0c;允许程序同时执行多个任务&#xff0c;提高程序的效率和性能。Java作为一门广泛应用于软件开发的编程语言&#xff0c;也提供了丰富的多线程支持。本文将简要介绍Java多线程的基本概念、使用方法以及相关特…