js中 call、apply、bind 源码解析
2020-03-18 本文已影响0人
alokka
写在前面
call、apply、bind其实用法都差不多 都是改变 this 指向,只是后面参数的传递有些许不同
- call 的参数是直接放进去的,第二第三第 n 个参数全都用逗号分隔,直接放到后面 obj.myFun.call(db,'成都', ...,'string' )
- apply 的所有参数都必须放在一个数组里面传进去 obj.myFun.apply(db,['成都', ..., 'string' ])
- bind 除了返回是函数以外,它 的参数和 call 一样
1. call 源码解析 es3 es6
function add(c, d) {
return this.a + this.b + c + d;
}
const obj = { a: 1, b: 2 };
// ES3 call 实现
Function.prototype.es3call = function (context) {
var content = context || window;
content.fn = this;
var args = [];
// arguments是传入的参数(实参)的类数组对象 函数自带
for (var i = 1, len = arguments.length ; i < len; i++) {
args.push('arguments[' + i + ']');
}
var result = eval('content.fn('+args+')');
delete content.fn;
return result;
}
console.error(add.es3call(obj, 3, 4)); // 10
// 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;
}
console.log(add.es6call(obj, 3, 4)); // 10
apply、bind的 es6 写法大家可以自己尝试写一下
2. apply 源码解析 es3 es6
function add(c, d) {
return this.a + this.b + c + d;
}
const obj = { a: 1, b: 2 };
// ES3 apply 实现
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
}
console.log(add.apply(obj, [1, 2])); // 6
// ES6 apply 实现
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;
}
console.error(add.es6apply(obj, [1, 2])); // 6
3. bind 源码解析 es3 es6
function add(c, d) {
return this.a + this.b + c + d;
}
const obj = { a: 1, b: 2 };
// ES3 apply 实现
Function.prototype.es3bind= function (target) {
target = target || window;//如果没有传入,就为window
var self = this;//谁调用myBind,this就指向谁
var args = [].slice.call(arguments, 1);//args:[arguments[1],arguments[2]....]
var temp = function () { };
var fn = function () {
var fnArgs = [].slice.call(arguments, 0);
//this 如果new fn() this 指向构造出来的对象,否则为window ;this instanceof fn看this的原型链上有没有fn的原形
return self.apply(this instanceof fn ? this : target, args.concat(fnArgs));
}
temp.prototype = this.prototype;
fn.prototype = new temp(); //形成继续关系 fn.prototype.__proto__ == this.prototype true
return fn;
}
console.log(add.es3bind(obj, 3, 4)()); // 10
// ES6 apply 实现
Function.prototype.es6Bind = function(context, ...rest) {
var self = this;//谁调用myBind,this就指向谁
return function fn(...args) {
return this instanceof fn ? new self(...rest, ...args) : self.apply(context, rest.concat(args))
}
}
console.log(add.es6Bind(obj, 3, 4)()); // 10