call、apply、bind 深度解析 核心区别总结
特性
call
apply
bind
执行方式
立即执行
立即执行
返回绑定函数
参数格式
参数列表 (arg1, arg2)
参数数组 [arg1, arg2]
参数列表 + 后续参数
返回值
函数执行结果
函数执行结果
绑定this的新函数
使用场景
明确参数个数时
动态参数个数时
需要延迟执行时
详细对比与示例 1. call 方法 语法 :func.call(thisArg, arg1, arg2, ...)
1 2 3 4 5 6 7 8 function greet (message ) { console .log (`${message} , ${this .name} !` ); } const user = { name : 'Alice' };greet.call (user, 'Hello' );
2. apply 方法 语法 :func.apply(thisArg, [argsArray])
1 2 3 4 5 6 7 8 9 10 11 function introduce (job, hobby ) { console .log (`我是${this .name} , 职业${job} , 爱好${hobby} ` ); } const user = { name : 'Bob' };introduce.apply (user, ['工程师' , '登山' ]); Math .max .apply (null , [1 , 5 , 3 ]);
3. bind 方法 语法 :const boundFunc = func.bind(thisArg, arg1, arg2, ...)
1 2 3 4 5 6 7 8 9 10 11 function showScore (subject ) { console .log (`${this .name} 的${subject} 成绩:${this .score} ` ); } const student = { name : 'Carol' , score : 95 };const showMathScore = showScore.bind (student, '数学' );setTimeout (showMathScore, 1000 );
bind 实现原理 基础版 bind 实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Function .prototype .myBind = function (context, ...bindArgs ) { const self = this ; return function (...callArgs ) { const allArgs = bindArgs.concat (callArgs); return self.apply (context, allArgs); }; }; const boundFunc = greet.myBind (user, 'Hello' );boundFunc ();
增强版(支持 new 操作) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 Function .prototype .myBind = function (context, ...bindArgs ) { const self = this ; const boundFunc = function (...callArgs ) { const allArgs = bindArgs.concat (callArgs); const isNewCall = this instanceof boundFunc; return self.apply ( isNewCall ? this : context, allArgs ); }; boundFunc.prototype = Object .create (self.prototype ); return boundFunc; }; function Person (name ) { this .name = name; } const BoundPerson = Person .myBind (null , 'Dave' );const p = new BoundPerson ();console .log (p instanceof Person );
bind 后的 this 能否修改? 关键结论 bind 创建的函数无法通过 call/apply 再次修改 this 绑定 原因:bind 返回的是绑定函数(Bound Function),其内部已固定 this 指向
验证实验 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 const obj1 = { name : '对象1' };const obj2 = { name : '对象2' };function showName ( ) { console .log (this .name ); } const boundFunc = showName.bind (obj1);boundFunc.call (obj2); boundFunc.apply (obj2); const reboundFunc = boundFunc.bind (obj2);reboundFunc ();
原理分析
绑定函数特性 ECMAScript 规范规定:绑定函数在调用时会忽略传入的 this 值,始终使用创建时绑定的 this
**内部属性 [[BoundThis]]** 绑定函数内部维护一个 [[BoundThis]] 属性,存储原始绑定值
调用优先级 绑定函数的执行优先级:[[BoundThis]] > call/apply 参数 > 默认绑定
特殊情况:new 操作符 1 2 3 4 5 6 7 8 9 10 function User (name ) { this .name = name; } const Admin = User .bind ({ role : 'admin' });const admin = new Admin ('SuperUser' );console .log (admin.name ); console .log (admin.role );
原理 : 当使用 new 操作符调用绑定函数时:
创建新对象(忽略 [[BoundThis]])
执行构造函数初始化
新对象的原型指向绑定函数的 prototype
实际应用场景 场景1:事件处理(React) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Button extends React.Component { constructor ( ) { super (); this .handleClick = this .handleClick .bind (this ); } handleClick ( ) { console .log (this .props .id ); } render ( ) { return <button onClick ={this.handleClick} > 点击</button > ; } }
场景2:部分参数应用 1 2 3 4 5 6 7 8 9 10 11 12 function log (level, message ) { console .log (`[${level} ] ${message} ` ); } const logError = log.bind (null , 'ERROR' );const logInfo = log.bind (null , 'INFO' );logError ('数据库连接失败' ); logInfo ('用户登录成功' );
场景3:定时器回调 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Timer { constructor ( ) { this .seconds = 0 ; this .tick = this .tick .bind (this ); } start ( ) { setInterval (this .tick , 1000 ); } tick ( ) { this .seconds ++; console .log (`已运行:${this .seconds} 秒` ); } }
原型链关系图 1 2 3 4 5 6 7 8 9 10 graph TD A[原生函数] --> B[bind创建] B --> C[绑定函数] C --> D[[内部属性]] D --> E[[BoundThis]] D --> F[[BoundArguments]] D --> G[[BoundTargetFunction]] H[new操作] --> I[创建新对象] I --> J[忽略BoundThis] J --> K[执行构造函数]
面试要点总结
bind 后的函数特性
无法通过 call/apply 修改 this 绑定
参数可以部分应用(柯里化)
使用 new 调用时忽略绑定的 this
设计原理
绑定函数内部维护 [[BoundThis]] 属性
执行时优先使用 [[BoundThis]]
规范定义的绑定函数行为不可变
最佳实践
类组件中在构造函数提前绑定
避免在渲染中创建绑定函数(性能优化)
利用参数绑定简化函数调用