函数属性与命名自省
length 与 name 如何反映函数形态?
function.length: 形参数量 (不含剩余参数与带默认值之后的参数);function.name: 函数名, 详见 函数名与提升;
函数的 name 还能从哪里推断出来?
- 语义: 规范称"上下文名称" (contextual name), 无显式名字时按上下文推断, 基础规则见 函数名与提升;
- 推断来源: 变量赋值取变量名, 默认参数值取形参名, 对象方法取属性名; 数组元素等无处取名的位置则得到空字符串;
let sayHi = function () {};
const user = { sayBye: function () {} }; // sayBye.name === "sayBye"
函数的 length 如何用于自省?
- 语义: 返回形参个数, 剩余参数不计入, 见上文"length 与 name 如何反映函数形态?";
- 用途: 让"接收函数的函数"按
handler.length决定调用方式, 属于按参数形态分派的多态手法; 如ask(question, ...handlers)中length === 0的处理器只在肯定回答时调用, 有参处理器两种回答都调用;
function many(a, b, ...more) {}
many.length; // 2
函数能挂自定义属性吗?与闭包如何取舍?
- 可以: 函数是对象, 属性可读可写, 常用于在函数自身上保存状态; 但函数属性与函数体内的局部变量互不影响, 是两个平行世界;
function counter() {
return counter.count++;
}
counter.count = 0;
counter.count = 10; // 状态可被外部改写
counter(); // 10
- 取舍: 闭包变量外部不可见, 只有内层函数能改; 函数属性外部可读写, 便于观察或重置;
- 生态用法: 库把辅助函数挂在主函数上 (如
_、$), 只占一个全局名字;
NFE 命名函数表达式解决了什么问题?
- 定义: 带名字的函数表达式
function func(who){}, 仍是表达式而非声明; - 特性: 名字可在函数内部自引用, 对外不可见;
- 价值: 递归若用外层变量名, 外部改赋或置空后递归即崩; 内部名由规范保证始终指向当前函数;
- 限制: 函数声明没有内部名语法, 需要可靠自引用时应改写成 NFE;
let sayHi = function func(who) {
if (!who) return func("Guest"); // 稳定自引用
};
let welcome = sayHi;
sayHi = null;
welcome(); // 仍能递归
new Function 创建函数有什么特点?
new Function(...)由字符串创建函数, 作用域与风险同eval, 详见 Math 与 Global;name: 未命名时其name为"anonymous", 见 函数名与提升;