先记住这个答案
name 属性主要来自两个地方。一是代码里写明的名字:函数声明 function foo() {},以及具名函数表达式 const a = function foo() {},名字都是 foo。二是引擎推断:匿名函数或箭头函数在定义时就被赋给一个变量、写在对象字面量的属性上、或者作为参数的默认值,引擎会把变量名或属性名设成它的 name。不在这些位置定义的匿名函数,name 是空字符串。bind 返回的函数和 getter、setter 还会带上 bound 、get 、set 这样的前缀。
- 写了名字就用写的名字,具名表达式也一样
- 赋给变量或对象字面量属性时自动推断
obj.fn = function () {}这种赋值推断不出名字bind加bound前缀,getter 加get前缀
各种写法的 name 对照
把常见写法放在一起打印,规则一眼就能看清。空字符串用 JSON.stringify 包一下,不然打印出来是一个空行。
bindJavaScriptfunction declared() {}
const expression = function () {}
const arrow = () => {}
const obj = { method() {}, get size() { return 1 } }
obj.later = function () {} // 赋值给已有对象的属性
const bound = declared.bind(null)
console.log(declared.name)
console.log(expression.name, arrow.name)
console.log(obj.method.name)
console.log(Object.getOwnPropertyDescriptor(obj, 'size').get.name)
console.log(JSON.stringify(obj.later.name)) // 推断不出来
console.log(bound.name)
console.log(JSON.stringify((() => {}).name)) // 没有任何名字来源查看输出与解释
declared
expression arrow
method
get size
""
bound declared
""expression 和 arrow 自己都没写名字,是从 const 左边的变量名推断出来的。对象字面量里的 method() {} 取属性名。getter 的 name 是 get size,要通过属性描述符才能拿到这个函数本身,直接读 obj.size 会触发 getter 返回 1。
最容易被问住的是 obj.later。它明明也是「赋值给一个属性」,name 却是空字符串。原因是推断只在左边是一个变量名,或者在对象字面量、类里定义时发生,obj.later = ... 这种给已有对象的属性赋值不在推断范围内。
推断只发生在定义那一刻
名字是函数被创建时就定下的,之后再怎么赋值都不会改。比如 const a = () => {} 之后再写 const b = a,b.name 还是 'a'。
- 具名函数表达式的名字优先
const handler = function onClick() {}这种写法,handler.name是'onClick',写明的名字优先于推断。这个名字还有一个作用:它只在函数体内部可见,可以用来递归调用自己,外面改了handler变量也不影响递归。- 默认参数和解构默认值也会推断
function run(cb = () => {}) {}里,默认的箭头函数name是'cb'。解构写法const { onLoad = () => {} } = options也一样,名字是'onLoad'。
容易答错的地方
- 以为匿名函数的
name一定是空的 - 只要定义时被赋给变量或写在对象字面量里,就会有名字。现在大部分箭头函数都是
const fn = () => {}这样定义的,所以它们在调用栈里都能显示出名字。 - 拿
name做业务判断或缓存 key - 生产构建会压缩变量名,
name可能变成t、n这样的单字母。不同函数也可能同名。需要稳定标识时,给函数显式加一个字段,或者直接用函数本身做Map的 key。 - 以为可以直接给
name赋值 name是只读的,fn.name = 'x'不会生效,严格模式下会抛TypeError。它是可配置的,真要改只能用Object.defineProperty。
面试官还会怎么问?
new Function() 创建的函数叫什么?
叫 'anonymous',这是规范里的固定值,和匿名函数表达式的空字符串不一样。
name 有什么实际用处?
主要是调试。报错时的调用栈、性能面板、React DevTools 里显示的组件名都用到它。如果组件直接写成 export default () => {},按规范它的 name 是 'default',DevTools 里看不出是哪个组件,所以很多团队的 lint 规则要求组件必须具名。
类的 name 是什么?
class User {} 的 name 是 'User',匿名类 const Admin = class {} 同样会推断成 'Admin'。如果类里定义了静态方法 static name() {},它会覆盖掉这个属性,User.name 就变成了一个函数。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。