call, call.call, call.call.call, 你也许还不懂这疯狂的call

2024-04-11 05:28
文章标签 call 疯狂 也许

本文主要是介绍call, call.call, call.call.call, 你也许还不懂这疯狂的call,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

Function.prototype.call 我想大家都觉得自己很熟悉了,手写也没问题!!
你确认这个问题之前, 首先看看 三千文字,也没写好 Function.prototype.call,

看完,你感觉还OK,那么再看一道题:
请问如下的输出结果

function a(){ console.log(this,'a')
};
function b(){console.log(this,'b')
}
a.call.call(b,'b')  

如果,你也清晰的知道,结果,对不起,大佬, 打扰了,我错了!

本文起源:
一个掘友加我微信,私聊问我这个问题,研究后,又请教了 阿宝哥
觉得甚有意思,遂与大家分享!

结果

结果如下: 惊喜还是意外,还是淡定呢?

String {"b"} "b"

再看看如下的代码:2个,3个,4个,更多个的call,输出都会是String {"b"} "b"

function a(){ console.log(this,'a')
};
function b(){console.log(this,'b')
}
a.call.call(b,'b')  // String {"b"} "b"
a.call.call.call(b,'b')   // String {"b"} "b"
a.call.call.call.call(b,'b')  // String {"b"} "b"

看完上面,应该有三个疑问?

  1. 为什么被调用的是b函数
  2. 为什么thisString {"b"}
  3. 为什么 2, 3, 4个call的结果一样

结论:
两个以上的call,比如call.call(b, 'b'),你就简单理解为用 b.call('b')

分析

为什么 2, 3, 4个call的结果一样

a.call(b) 最终被调用的是a,
a.call.call(b), 最终被调用的 a.call
a.call.call.call(b), 最终被执行的 a.call.call

看一下引用关系

a.call === Function.protype.call  // true
a.call === a.call.call  // true
a.call === a.call.call.call  // true

基于上述执行分析:
a.call 被调用的是a
a.call.calla.call.call.call 本质没啥区别, 被调用的都是Function.prototype.call

为什么 2, 3, 4个call的结果一样,到此已经真相

为什么被调用的是b函数

看本质就要返璞归真,ES 标准对 Funtion.prototye.call 的描述

Function.prototype.call (thisArg , …args)

When the call method is called on an object func with argument, thisArg and zero or more args, the following steps are taken:

  1. If IsCallable(func) is false, throw a TypeError exception.
  2. Let argList be an empty List.
  3. If this method was called with more than one argument then in left to right order, starting with the second argument, append each argument as the last element of argList.
  4. Perform PrepareForTailCall().
  5. Return Call(functhisArgargList).

中文翻译一下

  1. 如果不可调用,抛出异常
  2. 准备一个argList空数组变量
  3. 把第一个之后的变量按照顺序添加到argList
  4. 返回 Call(functhisArgargList)的结果

这里的Call只不是是一个抽象的定义, 实际上是调用函数内部 [[Call]] 的方法, 其也没有暴露更多的有用的信息。

实际上在这里,我已经停止了思考:

a is a function, then what a.call.call really do? 一文的解释,有提到 Bound Function Exotic Objects , MDN的 Function.prototype.bind 也有提到:

The bind() function creates a new bound function, which is an exotic function object (a term from ECMAScript 2015) that wraps the original function object. Calling the bound function generally results in the execution of its wrapped function.

Function.prototype.call 相反,并没有提及!!! 但不排查在调用过程中有生成。

Difference between Function.call, Function.prototype.call, Function.prototype.call.call and Function.prototype.call.call.call 一文的解释,我觉得是比较合理的

function my(p) { console.log(p) }
Function.prototype.call.call(my, this, "Hello"); // output 'Hello'

Function.prototype.call.call(my, this, "Hello"); means:

Use my as this argument (the function context) for the function that was called. In this case Function.prototype.call was called.

So, Function.prototype.call would be called with my as its context. Which basically means - it would be the function to be invoked.

It would be called with the following arguments: (this, "Hello"), where this is the context to be set inside the function to be called (in this case it’s my), and the only argument to be passed is "Hello" string.

重点标出:
So, Function.prototype.call would be called with my as its context. Which basically means - it would be the function to be invoked.

It would be called with the following arguments: (this, "Hello"), where this is the context to be set inside the function to be called (in this case it’s my), and the only argument to be passed is "Hello" string

翻译一下:
Function.prototype.call.call(my, this, "Hello")表示: 用my作为上下文调用Function.prototype.call,也就是说my是最终被调用的函数。

my带着这些 (this, "Hello") 被调用, this 作为被调用函数的上下文,此处是作为my函数的上下文, 唯一被传递的参数是 "hello"字符串。

基于这个理解, 我们简单验证一下, 确实是这样的表象

// case 1:
function my(p) { console.log(p) }
Function.prototype.call.call(my, this, "Hello"); // output 'Hello'// case 2:
function a(){ console.log(this,'a')
};
function b(){console.log(this,'b')
}
a.call.call(b,'b')  // String {"b"} "b"

为什么被调用的是b函数, 到此也真相了。

其实我依旧不能太释怀, 但是这个解释可以接受,表象也是正确的, 期望掘友们有更合理,更详细的解答。

为什么thisString {"b"}

在上一节的分析中,我故意遗漏了Function.prototype.call的两个note

NOTE 1: The thisArg value is passed without modification as the this value. This is a change from Edition 3, where an undefined or null thisArg is replaced with the global object and ToObject is applied to all other values and that result is passed as the this value. Even though the thisArg is passed without modification, non-strict functions still perform these transformations upon entry to the function.

NOTE 2: If func is an arrow function or a bound function then the thisArg will be ignored by the function [[Call]] in step 5.

注意这一句:

This is a change from Edition 3, where an undefined or null thisArg is replaced with the global object and ToObject is applied to all other values and that result is passed as the this value

两点:

  1. 如果thisArgundefined 或者null, 会用global object替换
    这里的前提是 非严格模式
"use strict"function a(m){console.log(this, m);  // undefined, 1
}a.call(undefined, 1)
  1. 其他的所有类型,都会调用 ToObject进行转换
    所以非严格模式下, this肯定是个对象, 看下面的代码:
Object('b') // String {"b"}

note2的 ToObject 就是答案

到此, 为什么thisSting(b) 这个也真相了

万能的函数调用方法

基于Function.prototype.call.call的特性,我们可以封装一个万能函数调用方法

var call = Function.prototype.call.call.bind(Function.prototype.call);

示例

var person = {hello() { console.log('hello', this.name) }
}call(person.hello, {"name": "tom"})  // hello tom

写在最后

如果你觉得不错,你的一赞一评就是我前行的最大动力。

技术交流群请到 这里来。
或者添加我的微信 dirge-cloud,一起学习。

引用

sec-function.prototype.call
Bound Function Exotic Objects
Function.prototype.bind
a is a function, then what a.call.call really do?
Difference between Function.call, Function.prototype.call, Function.prototype.call.call and Function.prototype.call.call.call
Javascript Function.prototype.call()
Can’t use Function.prototype.call directly

这篇关于call, call.call, call.call.call, 你也许还不懂这疯狂的call的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/893182

相关文章

AutoGen Function Call 函数调用解析(一)

目录 一、AutoGen Function Call 1.1 register_for_llm 注册调用 1.2 register_for_execution 注册执行 1.3 三种注册方法 1.3.1 函数定义和注册分开 1.3.2 定义函数时注册 1.3.3  register_function 函数注册 二、实例 本文主要对 AutoGen Function Call

Python __call__ 用法 作用

当一个对象为可被调用对象时,callable(object)返回为True,否则为False: Python中的对象有可被调用和不可被调用之分。 def func_test():print("func_test run")class class_test():def __init__(self):pass# func_test is callable return Trueprint("fu

YoloV10改进策略:下采样改进|自研下采样模块(独家改进)|疯狂涨点|附结构图

文章目录 摘要自研下采样模块及其变种第一种改进方法 YoloV10官方测试结果改进方法测试结果总结 摘要 本文介绍我自研的下采样模块。本次改进的下采样模块是一种通用的改进方法,你可以用分类任务的主干网络中,也可以用在分割和超分的任务中。已经有粉丝用来改进ConvNext模型,取得了非常好的效果,配合一些其他的改进,发一篇CVPR、ECCV之类的顶会完全没有问题。 本次我将这个模

Qt 错误qt.network.ssl: QSslSocket: cannot call unresolved function ERR_get_error

今天运行程序中有一个从网页下载文件到本地的功能运行输出下列错误         qt.network.ssl: QSslSocket: cannot call unresolved function ERR_get_error     qt.network.ssl: QSslSocket: cannot call unresolved function TLSv1_client_method

js apply和call方法的使用说明和区别

在JavaScript中,apply和call都是函数对象的方法,它们允许你改变函数调用时的上下文(即this值),并且可以显式地传入参数。这两者的主要区别在于如何传递参数。 call 方法 call方法使用一系列的参数来传递给目标函数。 语法 func.call(thisArg, arg1, arg2, ...argN); thisArg:在函数被调用时,作为this值被传递给目标函

当电商“邂逅”微信支付 比想象还疯狂

微信支付在为移动电商带来高效解决方案的同时,也促进了新的移动电商生态的形成。” 12月6日,在腾讯“当电商‘邂逅’移动支付”把脉沙龙上,微信团队与电商代表、外部专家就 微信支付与电商的现状和前景展开研讨,指出当前的“微信价”现象代表了电商企业对微信支付商业模式的探索。未来,微信支付有望发展成为移动互联网时代的一项基础能力,其与微信平台的紧密结合,将帮助电商企业构建起移动互联网时代的全新商业生态。

P1616 疯狂的采药(完全背包模板)

//这是一道完全背包的题,并且需要用一维数组优化空间,否则会MLE #include <bits/stdc++.h>using namespace std;//t表示可以用来采药的时间(相当于背包容量)//m表示草药的数目(相当于物品数量)int t, m; //m<=10^4,t<=10^7 //w[i]表示采摘第i种草药需要花费的时间(相当于背包模型中物品的体积) //v[i]

World of Warcraft [CLASSIC][80][Grandel] Call to Arms: Arathi Basin

PVP战场阿拉希盆地15人 /i 开局队伍分配:圣骑士飙车光环 /i [铁匠铺]坦克、治疗3个、输出6个(10人组) /i [伐木场]坦克、治疗、输出2(4个人组) /i [农场]留一个守(1个人组) /i  不要恋战,不要几个人打1个人,猎人假死脱战支援 /i  防守的人做个宏:NC请求支援,点几下就是几个人 /i 三点防守分配

【Python报错已解决】`SyntaxError: can‘t assign to function call`

🎬 鸽芷咕:个人主页  🔥 个人专栏: 《C++干货基地》《粉丝福利》 ⛺️生活的理想,就是为了理想的生活! 文章目录 引言:一、问题描述:1.1 报错示例:1.2 报错分析:1.3 解决思路: 二、解决方法:2.1 方法一:检查函数返回值2.2 步骤二:返回可变对象 三、其他解决方法四 总结 引言: 在Python编程中,你

RabbitMQ练习(Remote procedure call (RPC))

1、RabbitMQ教程 《RabbitMQ Tutorials》https://www.rabbitmq.com/tutorials 2、环境准备 参考:《RabbitMQ练习(Hello World)》。 确保RabbitMQ、Sender、Receiver容器正常安装和启动。 root@k0test1:~# docker run -it --rm --name rabbitmq