前言
bind、call、apply 三个改变指向函数,在JS面试中,经常问道。
本文收录了 四种 手写bind的方案
区别
bind 和 call/apply 有一个很重要的区别,一个函数被 call/apply 的时候,会直接调用,但是 bind 会创建一个新函数 。
当这个新函数被调用时,bind() 的第一个参数将作为它运行时的 this,之后的一序列参数将会在传递的实参前传入作为它的参数。
bind 做了什么
bind() 函数会创建一个 新绑定函数(bound function,BF) 。
绑定函数是一个exotic function object(怪异函数对象,ECMAScript 2015中的术语),它包装了原函数对象。调用绑定函数通常会导致执行包装函数。
绑定函数具有以下内部属性:
- [[BoundTargetFunction]] - 包装的函数对象
- [[BoundThis]] - 在调用包装函数时始终作为this值传递的值。
- [[BoundArguments]] - 列表,在对包装函数做任何调用都会优先用列表元素填充参数列表。
- [[Call]] - 执行与此对象关联的代码。通过函数调用表达式调用。内部方法的参数是一个this值和一个包含通过调用表达式传递给函数的参数的列表。
当调用绑定函数时,它调用[[BoundTargetFunction]]上的内部方法[[Call]],就像这样Call(boundThis, args)。其中,boundThis是[[BoundThis]],args是[[BoundArguments]]加上通过函数调用传入的参数列表。
绑定函数也可以使用new
运算符构造,它会表现为目标函数已经被构建完毕了似的。提供的this值会被忽略,但前置参数仍会提供给模拟函数。
手写函数
最佳手写方案:https://github.com/Raynos/function-bind/blob/master/implementation.js
// 方法1
Function.prototype.my_bind = function(context) {
if(typeof this !== "function"){
throw new TypeError("not a function");
}
let self = this;
let args = [...arguments].slice(1);
function Fn() {};
Fn.prototype = this.prototype;
let bound = function() {
let res = [...args, ...arguments]; //bind传递的参数和函数调用时传递的参数拼接
context = this instanceof Fn ? this : context || this;
return self.apply(context, res);
}
//原型链
bound.prototype = new Fn();
return bound;
}
/**
作者:刘小夕
链接:http://www.imooc.com/article/286880
来源:慕课网
本文原创发布于慕课网 ,转载请注明出处,谢谢合作 **/
//方法2
/*
函数的 bind 方法核心是利用 call,同时考虑了一些其他情况,例如
bind 返回的函数被 new 调用作为构造函数时,绑定的值会失效并且改为 new 指定的对象
定义了绑定后函数的 length 属性和 name 属性(不可枚举属性)
绑定后函数的原型需指向原来的函数
*/
const isComplexDataType = obj => (typeof obj === 'object' || typeof obj === 'function') && obj !== null
const selfBind = function(bindTarget , ...arg1){
if(typeof this !== 'function') throw new TypeError("bind must be called on a function");
let func = this;
let boundFunc = function(...arg2){
le targs = [...arg1,...arg2];
if(new.target){
let res = func.apply(this,args);
if(isComplexDataType(res)) return res;
return this;
} else {
func.apply(bindTarget , args)
}
}
this.prototype && (boundFunc.prototype == Object.create(this.prototype));
let desc = Object.getOwnPropertyDescriptors(func);
Object.defineProperties( boundFunc , {
length : desc.length,
name : Object.assign( desc.name , {
value : `bound${desc.name.value}`
})
});
}
// 方法3
Function.prototype.newBing = function(target){
//target改变返回函数执行的this指向
var self = this;//谁调用,this指向谁,此处将self=调用newBing的函数
var args = [].slice.call(arguments,1);//此处见他人博客 cichuhttps://blog.csdn.net/u011402896/article/details/79546268
var temp = function(){}; //缓冲函数(圣杯模式)
var f = function(){
var _arg = [].slice.call(arguments,0); //此处是函数B执行时的参数
//真正执行的是调用newBing的函数
return self.apply(this instanceof temp ? this : (target || window),args.concat(_arg)); //apply将方法内部的this指向改变为第一个参数,详细见 原型链 call/apply 继承模式 http://note.youdao.com/noteshare?id=7d5b99baf295b3499a49681cfdf72837
// instanceof 是表示this能否在原型链上找到自己的原型temp,实现5的功能
}
temp.prototype = self.prototype;
f.prototype = new temp(); //建立继承关系,圣杯模式 以f为构造函数去创建出的对象,能找到self. 实现5的功能。
return f;
}
/** ---------------------
作者:菜鸡的全栈路
来源:CSDN
原文:https://blog.csdn.net/qq_39403733/article/details/87287706
版权声明:本文为博主原创文章,转载请附上博文链接! **/
如有侵权,请联系我
网友评论