JavaScript中x.call被判定为function却无法执行的原因
x.call看起来是函数却无法执行? 这个问题确实有点反直觉,我来一步步拆解背后的原因:
首先先看你给出的核心代码:
const x = Object.create(function() {}); console.log(x.call) // ƒ call() { [native code] } console.log(typeof x.call) // "function" console.log(x.call instanceof Function) // true x.call() // Uncaught TypeError: x.call is not a function
1. 先搞清楚x.call的来源
你说的没错,x.call是从原型链继承来的——Object.create(function() {})创建的对象x,它的原型([[Prototype]])是那个匿名函数,而函数的原型又指向Function.prototype,所以x会继承Function.prototype.call这个方法。这也是为什么typeof x.call返回function,instanceof Function也返回true。
2. 问题出在call方法的执行逻辑上
Function.prototype.call这个方法的设计初衷,是让函数对象来调用的。当一个函数fn执行fn.call(context)时,call方法内部的this指向的是fn本身——也就是一个合法的函数对象。
但当你执行x.call()时,情况就变了:此时call方法内部的this指向的是x,而x是什么?它只是一个普通对象(虽然原型是函数,但它本身不是函数类型)。
我们可以把call的内部逻辑简化成这样来理解:
Function.prototype.call = function(context, ...args) { // 第一步就会检查调用者(也就是this)是不是函数 if (typeof this !== 'function') { throw new TypeError('this is not a function'); } // 后续才是绑定上下文、执行函数的逻辑 };
所以当x.call()执行时,call内部检测到this(也就是x)的类型是object,不是function,直接就抛出了你看到的错误。
3. 验证一下:手动绑定this就能正常执行
如果我们把call方法的this手动绑定到一个函数上,它就能正常工作了:
// 把call的this绑定到一个匿名函数上 x.call.call(function() { console.log("我能执行啦!") }); // 输出:我能执行啦!
这里的原理是:第一个call是x继承来的Function.prototype.call,而call本身也是个函数,所以它也继承了Function.prototype.call(第二个call),通过第二个call把this绑定到真正的函数上,就满足了call方法的执行条件。
4. 关于const x = Object.create(Array)的变体
道理是完全一样的:x的原型是Array构造函数,继承了call方法,但x本身是普通对象,不是函数,调用x.call()时this指向x,不符合call方法的要求,所以同样报错。
总结一下:不是x.call本身不是函数,而是你调用它的方式导致它内部的this指向了非函数对象,触发了call方法内部的类型检查。
内容的提问来源于stack exchange,提问作者Sasha

