手写源码并分析逻辑
/** @format
* 函数
* call,apply,bind 用于改变函数中执行时的this
* apply,call 参数不一致 apply 是一个数组,call是一个参数列表
* bind方法和call很相似,第一参数也是this的指向,后面传入的也是一个参数列表(但是这个参数列表可以分多次传入,call则必须一次性传入所有参数),但是它改变this指向后不会立即执行,而是返回一个永久改变this指向的函数。
*/
// 根据如上述求定义编写自定义改变this函数
/**
* call方法的第一个参数也是this的指向,后面传入的是一个参数列表(注意和apply传参的区别)。当一个参数为null或undefined的时候,表示指向window(在浏览器中),和apply一样,call也只是临时改变一次this指向,并立即执行
*/
/**
* ES6写法
* Function.prototype.$call=function(context,...args){}
* @returns any
*/
Function.prototype.$call = function () {
/**
* this 用于引用执行上下文中的对象
* this 谁调用指向谁,动态调整
* 当访问对象的属性成员时
* 如果本身没有,就通过 __proto__ 向上寻找
* __proto__ 指向的是 对象的构造函数原型对象 fun.prototype
* 由于只要是对象类型的数据就拥有 __proto__ 属性
* fun.prototype 不存在的话接着寻找 fun.prototype.__proto__
* 最后也找不到,就返回 null
* */
console.log('我要打印的值:', this);
// 获取参数
const context = arguments[0] || window;
// es5 语法 可以遍历参数
/* const _arguments = [];
for (let index = 1; index < arguments.length; index++) {
_arguments.push(arguments[index]);
} */
// es6语法
const _arguments = [...arguments].slice(1);
context.fn = this;
const result = context.fn(..._arguments);
delete context.fn;
return result;
};
/**
* apply接受两个参数,第一个参数是this的指向,第二个参数是函数接受的参数,以数组的形式传入,且当第一个参数为null、undefined的时候,默认指向window(在浏览器中),使用apply方法改变this指向后原函数会立即执行,且此方法只是临时改变thi指向一次。
*/
Function.prototype.$apply = function () {
console.log('我要打印的值:', this);
// 获取参数
const context = arguments[0] || window;
context.fn = this;
let result = null;
if (!arguments[1] || !Array.isArray(arguments[1]) || arguments[1].length === 0) {
result = context.fn();
} else {
result = context.fn(...arguments[1]);
}
delete context.fn;
return result;
};
/**
* bind方法和call很相似,第一参数也是this的指向,后面传入的也是一个参数列表(但是这个参数列表可以分多次传入,call则必须一次性传入所有参数),但是它改变this指向后不会立即执行,而是返回一个永久改变this指向的函数。
*/
Function.prototype.$bind = function () {
const context = arguments[0] || window;
const _arguments1 = [...arguments].slice(1);
context.fn = this;
const fn = this;
return function () {
const _arguments2 = [...arguments].slice();
/**
* apply 方法允许我们在调用函数时,将一个数组或类数组对象展开为函数的参数列表。这正是我们需要的功能,因为我们想要将预设的参数与新函数调用时传入的参数合并。
另外,使用 apply 方法还能使得代码更加简洁,因为我们不需要手动构建一个参数数组,而是直接使用 apply 方法来将预设的参数与新函数调用时传入的参数合并。这样可以减少代码的复杂度和出错的可能性。
*/
return fn.$apply(context, [].concat[(_arguments1, _arguments2)]);
// 直接调用
// return context.fn(...([].concat(_arguments1, _arguments2)));
};
};
/**
* 等同于
* var a= new Function();
*/
const obj = {
name: 'tongbinzuo',
};
function a() {
console.log('我要打印的值:', this.name);
}
const b = a.$bind(obj, 1, 2, 3, 4);
b(5, 6, 7, 8);