尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
bind 之后函数属性为何消失?深入解析 JavaScript 函数绑定与函数对象属性
文档教程前端【免费下载链接】zh.javascript.info现代 JavaScript 教程The Modern JavaScript Tutorial以最新的 ECMAScript 规范为基准通过简单但足够详细的内容为你讲解从基础到高阶的 JavaScript 相关知识。项目地址https://gitcode.com/gh_mirrors/zh/zh.javascript.info点击查看免费下载bind是 JavaScript 中解决丢失this问题的内建手段但很多开发者在使用它时会遇到一个隐蔽的坑绑定之后原函数身上自定义的属性如计数器、辅助方法统统不见了访问到的是undefined。本文将结合《现代 JavaScript 教程》中的练习题bind 后的函数属性task.md 与 solution.md从函数对象本质、ECMAScript 的绑定函数外来对象Bound Function Exotic Object机制入手讲清属性为什么丢以及真需要时怎么办读完你将对bind的返回值和函数属性存储模型有完整的认知。题目回顾bind 之后函数属性会改变吗题目来自 task.md核心代码如下function sayHi() { alert( this.name ); } sayHi.test 5; let bound sayHi.bind({ name: John }); alert( bound.test ); // 输出将会是什么为什么问的是函数sayHi身上挂着一个自定义属性test 5经过bind得到bound之后bound.test是多少答案undefinedsolution.md 给出的答案非常干脆答案undefined。bind的结果是另一个对象。它并没有test属性。也就是说bound.test既不是5也不是报错而是undefined——bound是一个与sayHi完全不同的对象原函数身上的自定义属性一个都没有带过来。这个结论初看反直觉bound()调用起来明明就像sayHi一样工作this还被替换成了{name: John}为什么属性却蒸发了要理解这一点需要先回到函数对象的本质。第一层原理函数本质上是可调用的对象在 JavaScript 中函数的类型是对象详细讲解见 06-function-object/article.md 中的函数对象NFE章节。这意味着函数不仅是可调用的代码还可以被当作对象来使用可以给它添加/删除自定义属性例如sayHi.test 5可以按引用传递它它自带一些内建属性如name函数名和length形参个数rest 参数不计入。自定义属性经常被用于在函数身上附带状态。教程中就有典型例子用函数属性counter替代闭包来记录调用次数function sayHi() { alert(Hi); sayHi.counter; // 计算调用次数 } sayHi.counter 0; sayHi(); sayHi(); alert( Called ${sayHi.counter} times ); // Called 2 times这里的关键是counter、test这类属性是存储在sayHi这个函数对象自身的属性表里的它们不是局部变量也不存在于任何词法环境中。所以属性跟着函数对象走——对象是谁属性就在谁身上。第二层原理bind 返回的是全新的外来绑定函数对象既然属性存储在函数对象自身那么问题就变成bind返回的对象到底是不是原来的函数对象答案是否定的。bind章节article.md中明确说明func.bind(context)的结果是一个特殊的类似于函数的外来对象exotic object它可以像函数一样被调用并且透明地将调用传递给func并设定thiscontext。按 ECMAScript 规范bind返回的是一个绑定函数外来对象Bound Function Exotic Object它内部只保存了三样东西[[BoundTargetFunction]]被绑定的目标函数即原func[[BoundThis]]绑定的this上下文[[BoundArguments]]绑定的前置参数列表。当bound(...)被调用时引擎会取出这三项把[[BoundThis]]作为this、把[[BoundArguments]]与本次调用参数拼接后Call到[[BoundTargetFunction]]上。除此之外这个新对象和原函数之间没有任何属性共享——原函数自定义的test、counter、name等属性都不会出现在绑定函数上。同一章节的练习题 3-second-bind 及其 solution.md 也从侧面印证了这一点function f() { alert(this.name); } f f.bind( {name: John} ).bind( {name: Pete} ); f(); // John不是 Pete连续两次bind结果是第二次绑定完全无效。原因正如该答案所说f.bind(...)返回的绑定函数外来对象仅在创建时记忆上下文和参数一个函数不能被重新绑定re-bound。第一次bind生成的新对象已经固化了{name: John}第二次bind只是拿这个新对象再包一层调用链最终仍落到最内层的目标函数和最初绑定的this上。如果绑定函数的上下文还可以被外部环境改写那么 2-write-to-object-after-bind 的答案就不会是nullfunction f() { alert( this ); // null } let user { g: f.bind(null) }; user.g(); // 输出 null即便通过user.g()调用this依然是硬绑定的null。这说明绑定函数是一个独立的、硬编码了内部状态的新对象——它既不继承原函数属性也不允许事后修改绑定上下文。源码佐证bind 的底层实现就是包一层新函数在仓库的 head.html 中教程提供了一个兼容旧引擎的bind模拟实现ES5 写法可以直观地看到新对象从何而来function bind(func, context /*, args*/) { let bindArgs [].slice.call(arguments, 2); // (1) 收集绑定的前置参数 function wrapper() { // (2) 创建一个全新的 wrapper 函数 let args [].slice.call(arguments); let unshiftArgs bindArgs.concat(args); // (3) 前置参数 本次调用参数 return func.apply(context, unshiftArgs); // (4) 以 context 作为 this 调用原函数 } return wrapper; }可以看到bind内部创建的是一个全新的wrapper函数对象并返回它原函数的属性当然不会复制过来调用wrapper时通过func.apply(context, ...)把this固定为context参数则按绑定参数在前、调用参数在后的顺序拼接这正好解释了本练习中bound.test undefinedbound就是这样一个全新的wrapper对象它身上只实现了按固定上下文调用目标函数这件事。原生Function.prototype.bind的实现细节虽然更复杂涉及规范中的绑定函数内部槽但创建一个新函数对象、不继承原函数属性这一行为特征是等价的。工程影响哪些场景最容易踩中这个坑理解了上面的机制就能预判以下真实场景中的坑1. 用函数属性存状态/辅助功能时比如把counter属性挂在函数上统计调用次数06-function-object/article.md 中的经典写法一旦把函数bind后传给setTimeout、事件监听器或回调计数器就会归零——因为绑定函数身上根本没有这个属性访问到的永远是undefined。2. 把带属性函数传给第三方库时lodash 的_.bindAll(object, methodNames)article.md 中有介绍会在循环里用user[key].bind(user)批量重写对象方法。如果这些方法身上挂了自定义属性绑定后属性同样会丢失。3. 属性与变量的混淆教程在函数对象章节特意提醒过函数属性如sayHi.counter不是函数内的局部变量二者互不影响、是完全平行的两条线。绑定函数丢失的是属性这条线而闭包捕获的变量词法环境中的user等则不受影响——这也是为什么 5-question-use-bind 的解决方案里bind和箭头包装器都能修复this但只有bind能硬固定住对象引用askPassword(user.loginOk.bind(user), user.loginFail.bind(user))绑定的是当时的user对象而() user.loginOk()每次调用时才去读外部变量user若变量中途被改写就会调用到错误对象。实战补充如果确实需要保留属性怎么办bind不会复制属性但我们可以手动补回来常见做法有三种方式一绑定后重新挂载属性function sayHi() { alert( this.name ); } sayHi.test 5; let bound sayHi.bind({ name: John }); bound.test 5; // 手动把需要的属性挂到绑定函数上 alert( bound.test ); // 5方式二封装一层返回绑定 属性的工具函数function bindWithProps(func, context, props) { const bound func.bind(context); return Object.assign(bound, props); // 把原函数的属性合并到绑定函数上 }方式三改用闭包/词法变量承载状态如果状态只是为了计数或标记可以把状态放到函数外部词法环境中或用对象this承载这样bind只固定this状态仍由闭包变量或对象属性保存天然不受影响。函数属性与闭包变量的取舍教程在 06-function-object/article.md 的自定义属性一节有详细对比。总结bind返回的是全新的绑定函数外来对象调用时透明转发给目标函数并固定this与前置参数原函数对象自身的属性test、counter等存储在原对象上不会被绑定函数继承访问结果为undefined绑定函数的this是硬绑定的无法二次绑定、也无法被后续的调用方式改写参见 3-second-bind 与 2-write-to-object-after-bind需要属性时手动在绑定函数上重新挂载或用闭包/对象属性承载状态。想继续巩固这个知识点可以顺带完成同章节的其余练习5-question-use-bindbind与包装器的取舍和 6-ask-partial绑定参数的部分应用它们共同构成了对bind行为的完整认知闭环。赞分享文档教程前端【免费下载链接】zh.javascript.info现代 JavaScript 教程The Modern JavaScript Tutorial以最新的 ECMAScript 规范为基准通过简单但足够详细的内容为你讲解从基础到高阶的 JavaScript 相关知识。项目地址https://gitcode.com/gh_mirrors/zh/zh.javascript.info点击查看免费下载相关推荐深入解析 JavaScript 中 bind 之后函数属性为何丢失Function Property After Bind深入解析 JavaScript 中 bind 之后函数属性为何丢失Function Property After Bind 本篇围绕 Modern JavaS文档/教程前端深入解析 JavaScript 中 bind 与函数属性为什么绑定后 sayHi.test 变成了 undefined深入解析 JavaScript 中 bind 与函数属性为什么绑定后 sayHi.test 变成了 undefined Function.prototype.文档教程前端深入解析 bind 与函数属性为什么 bound.test 是 undefinedJavaScript 现代教程实战深入解析 bind 与函数属性为什么 bound.test 是 undefinedJavaScript 现代教程实战 Function.prototype文档/教程前端上一篇推荐文章深入探索Kubernetes与Vault的无缝集成——Kubernetes-Vault项目解析下一篇5分钟入门VeauryReact项目嵌入Vue3组件的完整教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED

相关推荐

LocalSend AppImage:一份文件跑遍所有发行版

LocalSend AppImage:一份文件跑遍所有发行版

LocalSend AppImage:一份文件跑遍所有发行版 【免费下载链接】localsend An open-source cross-platform alternative to AirDrop 项目地址: https://gitcode.com/GitHub_Trending/lo/localsend LocalSend 是开源的 AirDrop 替代方案,走局域网传文…

📅 2026/10/7 16:03:19
Star History 月度精选 2025 年 12 月:React 生态四大工具深度解读

Star History 月度精选 2025 年 12 月:React 生态四大工具深度解读

开发工具数据可视化 【免费下载链接】star-history The de facto GitHub star history graph. 项目地址: https://gitcode.com/gh_mirrors/st/star-history 点击查看 免费下载 导读:2025 年 React 正式进入 Linux Foundation,其开源治理走向…

📅 2026/10/7 16:03:19
ponytail插件与技能机制详解:从原理到实操的完整指南

ponytail插件与技能机制详解:从原理到实操的完整指南

1. 从“ponytail”这个标题说起:它到底是什么第一次看到“ponytail”这个词,很多人脑子里蹦出来的画面大概是扎起来的马尾辫。但在技术圈和工具链语境里,它早就不是发型那么简单了。最近一段时间,“ponytail skill”“ponytail 插…

📅 2026/10/7 16:03:19
MORE NEWS

更多资讯

📰

Spring Boot旅游推荐系统实战:协同过滤与高并发设计

1. 项目概述 1.1 旅游推荐系统到底在解决什么问题 先别急着看技术选型,咱们把场景想清楚。做毕设也好,做个人项目练手也罢,最容易犯的错就是一上来就堆功能列表。你仔细看这个标题:基于 Spring Boot 的旅游推荐系统,带…

📰

MAC地址完全指南:从原理到修改与排查

1. 从一台连不上网的电脑说起 搞网络这么多年,被问得最多的一个问题就是:"MAC地址到底是什么?"起因通常是用户换了路由器、改了密码、绑了白名单之后,设备突然连不上网络。排查到最后,发现是MAC地址变了或者…

📰

Python游戏碰撞检测全攻略:从AABB到空间分区与优化实践

做游戏时,角色穿墙、子弹打不中敌人、明明看到图形重叠却判定为没碰上……这一系列问题,根源几乎都指向同一个东西:碰撞检测。我在写Python小游戏的头两年里,被这四个字折磨得够呛,后来才慢慢摸清它涉及的几种算法、边…

📰

USB转串口芯片选型指南:CH340、CP2102、FT232、PL2303实测对比与避坑

调试串口这件事,看起来简单,实际上手就会发现坑不少。我自己这些年做嵌入式开发、工控板调试、路由器刷机、Arduino 和 ESP32 折腾,手头攒了一大把 USB 转串口模块,CH340、CP2102、FT232、PL2303 这几个型号基本都用过一轮。最直观…

📰

工业基础设施底座:Linux与数据库的选型、部署与运维实战

1. 工业基础设施的底层逻辑:为什么是Linux加数据库1.1 从一条产线停机说起前两年我参与过一个离散制造车间的数字化改造项目,产线上有十几台数控设备,数据采集网关跑的是某商业实时操作系统,后台数据落在一个单机版的关系型数据库…

📰

Java Web项目容器化部署全流程:Docker镜像构建、MySQL编排与排错实战

项目写完,离上线还差最后一公里。这最后一公里,就是把Java Web项目从开发机搬到服务器上,让它在另一台机器上也能稳定跑起来。我跟着黑马程序员的Java Web课程做项目时,第一次手动部署就把自己折腾得够呛:本地跑得好好…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬