重源码!bind的源码实现
bind 用法/特点
1 函数A调用bind方法时,需要传递的参数O, x, y, z, ···;
2 返回新的函数B;
3 函数B在执行的时候,具体的功能实际上还是使用的A,只不过this指向变成了O || window ;
4 函数B在执行的时候,传递的参数会拼接到x, y, z,···后面,一并传给A执行 ;
5 new B() 构造函数依旧是A, O不起任何作用 ;
- call apply 直接执行;bind是返回一个函数,这个函数等待某个状态的触发才去执行
底层用call和apply来实现一下bind方法
1 这里 在Function原型上赋值了一个方法,实现了改变this指向功能;
Function.prototype.newBind = function (target) {
// target 改变返回函数执行的this指向
var self = this;
var f = function(){
// 真正执行的其实是 self self = this
return self.apply(target||window);
}
return f;
}
2 接下来实现了在绑定方法里this绑定对象以外再传参的功能
Function.prototype.newBind = function (target) {
// target 改变返回函数执行的this指向
var self = this;
var args = [].slice.call(arguments,1);
var f = function(){
// 真正执行的其实是 self self = this
return self.apply(target||window,args);
}
return f;
}
3 再加入调用该newBind方法时,的传参功能
Function.prototype.newBind = function (target) {
// target 改变返回函数执行的this指向
var self = this;
var args = [].slice.call(arguments,1);
var f = function(){
var _arg = [].slice.call(arguments,0);
// 真正执行的其实是 self self = this
return self.apply(target||window,args.concat(_arg));
}
return f;
}
4 如果A调用newBind方法,在此基础上的new B()构造函数依旧是A,实现这个功能
Function.prototype.newBind = function (target) {
// target 改变返回函数执行的this指向
var self = this;
var args = [].slice.call(arguments,1);
var temp = function(){};
var f = function(){
var _arg = [].slice.call(arguments,0);
// 真正执行的其实是 self self = this
return self.apply(this instanceof temp ? this : ( target||window),args.concat(_arg));
}
temp.prototype = self.prototype;
f.prototype =new temp();
return f;
}
call的源码实现
call用法、特点
call() 方法在使用一个指定的 this 值和若干个指定的参数值的前提下调用某个函数或方法。
发生的过程类似下面的情形
const o = {
a : 1,
b : 2,
add : function(c, d){
return this.a + this.b + c + d;
}
}
1 给O对象添加一个add属性,这个时候this就指向了O;
2 O.add(5,7)得到的结果和add.call(O,5,7)相同;
3 call方法是立即调用,调用后方法不保存,所以仿写需要在方法执行后删除对象
上的这个方法。
- 将函数设为对象的属性: o.fn = bar;
- 执行该(函数)方法: o.fn() ;
- 删除该方法: delete o.fn
Function.prototype.es3Call = function (context) {
var context = context || window;
context.fn = this;
var args = [];
for(var i=1,len = arguments.length;i<len;i++){
args.push('arguments['+ i +']');
}
var result = eval('context.fn('+ args + ')');
delete context.fn;
return result;
}
ES6 call的实现
Function.prototype.es6Call = function (context) {
var context = context || window;
context.fn = this;
var args = [];
for(var i = 1, len = arguments.length; i<len;i++){
args.push('arguments['+ i +']');
}
const result = context.fn(...args);
delete context.fn;
return result;
}
apply 源码实现
apply和call区别在于apply第二个参数是Array,而call是将参数一个一个传入的。
Function.prototype.es3Apply = function (context,arr) {
var context = context || window;
context.fn = this;
var result;
if(!arr){
result = context.fn();
}else{
var args = [];
for(var i = 0,len = arr.length;i<len; i++){
args.push('arr['+ i +']');
}
result = eval('context.fn('+ args +')');
}
delete context.fn;
return result;
}
基于es6的实现
Function.prototype.es6Apply = function (context,arr) {
var context = context || window;
context.fn = this;
var result;
if (!arr) {
result = context.fn();
}else{
if(!(arr instanceof Array)) throw new Error('params must be array');
result = context.fn(...arr);
}
delete context.fn;
return result;
}