现代JavaScript(ES5及以后)为函数添加属性的惯用实现方式
实现兼具调用能力与自定义属性的函数(类似jQuery用法)
你想要的其实就是让函数本身成为一个可调用的对象,同时还能挂载自定义属性/方法——就像jQuery那样,既能jQuery(selector)调用,又能通过jQuery.ajax()访问它的属性方法。下面分别说说ES5和ES2015+里的理想实现方式:
ES5 实现方式
在ES5里,函数本身就是一种特殊的对象,所以最直接的方式就是给返回的函数实例直接添加属性,完全符合你代码里注释的思路:
"use strict"; function create_func(some, params) { var func = function(otherstuff) { console.log("inner", some, otherstuff); return 4; }; // 直接给函数挂载自定义属性 func.params = params; // 还可以挂载方法,模拟jQuery的扩展方式 func.someMethod = function() { console.log("这是函数自带的方法"); }; return func; } var f = create_func(1,2); console.log(f.params); // 输出 2 var returnval = f(3); // 输出 "inner", 1, 3 console.log(returnval); // 输出 4 f.someMethod(); // 输出 "这是函数自带的方法"
原理很简单:函数在JS里是Function类型的实例,属于引用类型,所以你可以像操作普通对象一样给它添加任意属性和方法,同时它保留了可调用的特性。如果需要多个这类函数共享方法,也可以修改函数的prototype,但如果只是给当前返回的函数加专属属性,直接挂载到函数本身更直接。
ES2015+ 实现方式
ES6及以后除了兼容ES5的直接挂载方式,还能用更简洁的语法,以下是几种常用方案:
方式1:箭头函数+直接挂载属性
箭头函数本身也是对象,同样可以直接添加属性,写法更简洁:
"use strict"; const createFunc = (some, params) => { const func = (otherstuff) => { console.log("inner", some, otherstuff); return 4; }; func.params = params; return func; }; const f = createFunc(1,2); console.log(f.params); // 输出 2 const returnval = f(3); // 输出 "inner", 1, 3 console.log(returnval); // 输出 4
方式2:用Object.assign批量挂载属性
如果需要给函数挂载多个属性/方法,用Object.assign可以更优雅地批量处理:
"use strict"; const createFunc = (some, params) => { const func = (otherstuff) => { console.log("inner", some, otherstuff); return 4; }; // 批量挂载属性和方法 return Object.assign(func, { params: params, logParams() { console.log("当前params值:", this.params); }, anotherMethod() { return "这是另一个方法的返回值"; } }); }; const f = createFunc(1,2); f.logParams(); // 输出 "当前params值: 2" console.log(f.anotherMethod()); // 输出 "这是另一个方法的返回值"
方式3:类+可调用实例(进阶场景)
如果你的场景需要更复杂的继承、实例管理,可以用类结合Function构造函数创建可调用实例,但这种方式要注意代码安全性(避免动态代码注入风险),适合大量这类可调用对象的场景:
class CallableFunction extends Function { constructor(some) { // 调用Function构造函数创建可调用实例 super("otherstuff", `console.log("inner", ${JSON.stringify(some)}, otherstuff); return 4;`); this.some = some; } // 用get/set封装属性 set params(val) { this._params = val; } get params() { return this._params; } } const createFunc = (some, params) => { const func = new CallableFunction(some); func.params = params; return func; }; const f = createFunc(1,2); console.log(f.params); // 输出2 const returnval = f(3); // 输出"inner",1,3 console.log(returnval); // 输出4
本质上,这类需求的核心都是利用JS里**“函数是特殊对象”**的特性——函数不仅可以被调用,还能像普通对象一样拥有自己的属性和方法,这也是jQuery这类库能同时作为函数和命名空间的核心原因。
内容的提问来源于stack exchange,提问作者lahwran
相关产品推荐
相关产品推荐

