逐步实现js的
bind
方法
- 原函数无参数版
bind
- 原函数带参数版
bind
- 区分原函数是否作为构造函数
一. 原函数无参数版 bind
- 原函数作为新_context对象内的属性,在对象中调用,即可使用_context的作用域
- 根据
bind
要求返回闭包
-
ES6之前
Function.prototype.myBind = function(_context){ // _context检测 _context = _context? _context: window; let that = this; // 按要求返回闭包(函数) return function(){ // 函数挂入_context内 _context.__fn = that; // 函数调用 let result = _context.__fn(); // 删除_context内的新函数 delete _context.__fn; return result; } }
-
ES6之后
Function.prototype.myBind = function(_context){ // _context检测 _context = _context? _context: window; // 按要求返回闭包(函数) return () => { // 函数挂入_context内 let fnKey = Symbol(); _context[fnKey] = this; // 函数调用 let result = _context[fnKey](); // 删除_context内的新函数 delete _context[fnKey]; return result; } }
二、原函数带参数版 bind
原函数的参数可能在
bind
时指定,也可能在调用绑定新作用域后的新函数中指定,也可能都有。
- 在
bind
中接收参数,并在闭包中于闭包参数合并为完整参数,再一起提供给新函数。- ES6之前没有解构语法,使用 恶魔方法
eval
进行函数调用
-
ES6之前
Function.prototype.myBind = function(_context){ // _context检测 _context = _context? _context: window; let that = this; // 参数获取 let outerArgs = []; let argumentsOut = arguments; // 与闭包中的arguments区分开 for(let i=1, len=arguments.length; i<len; i++){ outerArgs.push('argumentsOut[' + i + ']'); } // 按要求返回闭包(函数) return function(){ // 参数补充 let innerArgs = []; for(let i=0, len=arguments.length; i<len; i++){ innerArgs.push('arguments[' + i + ']'); } let args = outerArgs.concat(innerArgs); // 函数挂入_context内 _context.__fn = that; // 函数调用 let result = eval('_context.__fn(' + args + ')'); // 删除_context内的新函数 delete _context.__fn; return result; } }
-
ES6之后(箭头函数获取函数参数)
Function.prototype.myBind = function(_context, ..._args){ // _context检测 _context = _context? _context: window; // 参数获取 let outerArgs = _args? _args: []; // 按要求返回闭包(函数) return (..._args) => { // 参数补充 let args = _args? outerArgs.concat(_args): outerArgs; // 函数挂入_context内 let fnKey = Symbol(); _context[fnKey] = this; // 函数调用 let result = _context[fnKey](...args); // 删除_context内的新函数 delete _context[fnKey]; return result; } }
三、区分原函数是否作为构造函数
当原函数作为构造函数时,
bind()
用于绑定作用域的第一个参数将会失效,后续参数将继续作为构造函数的参数继续被使用。function F(_arg1){ this.x = _arg1; this.fn = function(){ console.log(this.x) } } let Func = F.bind({x: 100}, 1); let fo = new Func(); fo.fn(); // 1
- 新增原函数是否作为构造函数的判断
注:箭头函数不能作为构造函数
-
ES6之前
Function.prototype.myBind = function(_context){ // _context检测 _context = _context? _context: window; let that = this; // 参数获取 let outerArgs = []; let argumentsOut = arguments; // 与闭包中的arguments区分开 for(let i=1, len=arguments.length; i<len; i++){ outerArgs.push('argumentsOut[' + i + ']'); } // 按要求返回闭包(函数) return function F(){ // 参数补充 let innerArgs = []; for(let i=0, len=arguments.length; i<len; i++){ innerArgs.push('arguments[' + i + ']'); } let args = outerArgs.concat(innerArgs); if(this instanceof F) { // 构造函数 return eval('new that(' + args + ')'); } // 函数挂入_context内(这里最好做个键备份) _context.__fn = that; // 函数调用 let result = eval('_context.__fn(' + args + ')'); // 删除_context内的新函数 delete _context.__fn; return result; } }
-
ES6之后
Function.prototype.myBind = function(_context, ..._args){ // _context检测 _context = _context? _context: window; // 参数获取 let outerArgs = _args? _args: []; // 按要求返回闭包(函数) return function F(..._args) { // 箭头函数不能作为构造函数 // 参数补充 let args = _args? outerArgs.concat(_args): outerArgs; if(this instanceof F){ // 构造函数 return new that(...args); } // 函数挂入_context内 let fnKey = Symbol(); _context[fnKey] = that; // 函数调用 let result = _context[fnKey](...args); // 删除_context内的新函数 delete _context[fnKey]; return result; } }