Function.prototype与函数静态属性

背景

今天在写一个柯里化函数的时候,不小心遇到了一个错误,导致无法输出正确结果,代码如下:

javascript 复制代码
function curry(...args) {
  function _adder(...args2) {
    args.push(...args2);
    return _adder;
  }
  _adder.prototype.toString = function() {
    return args.reduce((a, b) => a + b);  
  }
  return _adder;
}

这个函数在使用的时候只需要这样调用:

javascript 复制代码
curry(1, 2, 3, 4, 5, 6)(7)(8).toString()

按照设想,应该会打印出正确结果36,但却打印出了如下结果:

javascript 复制代码
"function _adder(...args2) {
    args.push(...args2);
    return _adder;
}"

他把函数体打印了出来,这是为什么呢?

探究

原来我在函数体里把_addertoString方法绑到了他的原型上,这样只有他的实例能调用,或者自身通过以下方式调用:

javascript 复制代码
curry(1, 2, 3, 4, 5, 6)(7)(8).prototype.toString();

36

因此想要实现背景里的调用方式,上述柯里化函数应当改写为如下形式:

javascript 复制代码
function curry(...args) {
  function _adder(...args2) {
    args.push(...args2);
    return _adder;
  }
  _adder.toString = function() {
    return args.reduce((a, b) => a + b);  
  }
  return _adder;
}

也就是给函数添加静态方法toString,这样通过显式调用toString或者通过隐式类型转换的方式(toPrimitive)都可以计算柯里化的值。

那么Fuction.prototype与普通函数的关系是怎样的呢? Object是Function的实例,函数也是Object的实例,这两个东西高度抽象,互为实例。

javascript 复制代码
Object instanceof Function
true
Function instanceof Object
true

当我们通过类名直接在函数上添加属性或者方法的时候,这个属性就称之为类属性或者静态属性,只能函数本身调用,new 出来的对象不能调用。

所有的函数都是Function的实例:

javascript 复制代码
function test() {}
undefined
test instanceof Function
true
test.__proto__
ƒ () { [native code] }
Function.prototype
ƒ () { [native code] }
test.__proto__ === Function.prototype
true

另附instanceof内部原理:

javascript 复制代码
function instance_of(L, R) {//L 表示左表达式,R 表示右表达式 

    var O = R.prototype;   // 取 R 的显示原型 

    L = L.__proto__;  // 取 L 的隐式原型

    while (true) {    

        if (L === null)      

             return false;   

        if (O === L)  // 当 O 显式原型 严格等于  L隐式原型 时,返回true

             return true;   

        L = L.__proto__;  

    }

}

其实这和函数的实现原理有关,当我们通过如下方式声明函数时:

javascript 复制代码
let f = new Function();

实际上就构造了一个函数出来,并且该函数是可执行的,只不过没有函数参数和函数体而已,我们可以改写为如下形式:

javascript 复制代码
var func = new Function('arg1', 'arg2', 'return arg1 + arg2');
undefined
func(1, 2)
3
// 实际上等同于
function func(arg1, arg2) {
  return arg1 + arg2;
}

注意:尽管可以使用 Function 构造函数创建函数,但最好不要使用它,因为用它定义函数比用传统方式要慢得多。不过,所有函数都应看作 Function 类的实例。 这就很好解释了,因为函数进行了new操作,而new操作的内部原理如下:

javascript 复制代码
function new(fn) {
  let target = {};
  target.__proto__ = fn.prototype;
  let res = fn.call(target);
  if(typeof res == 'object' || typeof res == 'function') {
    return res;
  }
  return target;
}

总结

Function.prototype是所有function(函数)的原型,当我们通过属性访问符'.'或者中括号的形式去获取函数的静态属性的时候,首先查看的是自身有没有该属性,没有再去Function.prototype上去查找,直到找到Object.prototype,原型链的终端。因为prototype本身也是对象,所以也是Object的实例。

如果我们需要给所有函数定义公共方法,那么可以考虑Function.prototype

相关推荐
To_OC7 小时前
别再串行写 await 了,Promise.all 才是并行请求的正确打开方式
前端·javascript·promise
To_OC7 小时前
LC 17 电话号码的字母组合:我的回溯算法,就是从这道题开窍的
javascript·算法·leetcode
vipbic8 小时前
中后台越做越乱后,我用插件化把它救回来了
前端·vue.js
Hyyy8 小时前
Computer Use 适合做什么,不适合做什么——一次真实使用后的思考
前端
小和尚同志8 小时前
前端 AI 单元测试思考与落地
前端·人工智能·aigc
invicinble9 小时前
c端系统,其实更像一个信息展示平台
前端
你怎么知道我是队长10 小时前
JavaScript的变量和数据类型介绍
开发语言·javascript·ecmascript
李姆斯10 小时前
管理是否可以被完全量化
前端·产品经理·团队管理
名字还没想好☜11 小时前
Next.js 中间件实战:鉴权、重定向与 A/B 分流
开发语言·前端·javascript·中间件·react·next.js
广州灵眸科技有限公司11 小时前
瑞芯微RV1126B开发板(EASY-EAI-PI2) INI文件操作
java·前端·javascript·网络·人工智能