/**
* @desc实现一个myCall
*/
Function.prototype.myCall = function(context) {
if (Object.prototype.toString.call(this).slice(8, -1) !== 'Function') {
throw new Error('调用类型必须是Function类型!');
}
// step1: 兼容一下 context为空或者为简单类型的情况
context = context ? Object.create(context) : window;
// step2: 获取参数
const args = Array.prototype.slice.call(arguments, 1);
// step3: 将this作为context的某个属性并调用
context.fn = this;
const result = context.fn(...args);
// step4: 删除冗余的字段
delete context.fn;
return result;
}
/**
* @desc实现一个myApply
*/
Function.prototype.myApply = function (context) {
if (Object.prototype.toString.call(this).slice(8, -1) !== 'Function') {
throw new Error('调用apply的必须是函数类型')
}
// step1: context 缺省的情况下使用window、为了避免简单类型,用Object.create 强转一下
context = context ? Object.create(context) : window;
// step2: 获取参数
const args = arguments[1];
// step3: 将this作为context的某个属性,并调用该属性执行
context.fn = this;
const result = context.fn(...args);
// step4: 返回结果前删除冗余的字段
delete context.fn;
return result;
}
/**
* @desc 实现一个myBind
*/
Function.prototype.myBind = function (context) {
if (Object.prototype.toString.call(this).slice(8, -1) !== 'Function') {
throw new Error('调用的对象类型必须是 Function 类型');
}
// this指向调用bind的那个函数
const self = this;
// 兼容context不存在,或者为简单类型
context ? Object.create(context) : window;
// 拿到除去this之外的参数;
const args = Array.prototype.slice.call(arguments, 1);
const returnFn = function () {
const argsAdd = Array.prototype.slice.call(arguments);
return self.apply(this instanceof returnFn ? this : context, [...args, ...argsAdd])
};
// 处理一下使用new Bind的情况
const fn = function () { };
fn.prototype = this.prototype;
returnFn.prototype = new fn();
return returnFn;
}
/**
* @desc 实现一个new操作符
* 1. 需要把原型链链到构造函数上
* 2. 需要把构造函数执行一遍,并且执行结果返回
* 3. 返回结果要注意一下,如果构造函数的执行结果是一个对象,那么就返回执行结果,否则返回新生成的对象
*/
function _new(fn) {
// step1: 创建一个空对象、并且把对象的原型指向构造函数
const obj = Object.create(fn.prototype);
// step2: 借助apply,用生成的对象为this调用构造函数
const res = fn.apply(obj, Array.prototype.slice.call(arguments, 1));
// step3: 判断构造函数的返回值是否是对象类型,如果是,直接返回。如果不是,则返回新生成的对象
return res instanceof Object ? res : obj;
}
/**
* @desc 实现一个curry柯里化函数
*/
const sub_curry = (fn, ...args) => (...newArgs) => fn(...args, ...newArgs)
const curry = (fn, length = fn.length) => (...args) => {
// 参数还不够多,调用 sub_curry把之前的参数暂存一下,并且返回一个函数
if (args.length < length) {
return curry(sub_curry(fn, ...args), length - args.length)
} else {
return fn(...args)
}
}
// =============================== 测试代码 ============================
function sayHello(param1, param2, param3) {
console.log(this.name, param1, param2);
}
const thisObj = {
name: 'zaoren'
}
console.log('call test:------------------------------------------');
sayHello.call(thisObj, 'day', 'day up');
// sayHello.myCall(thisObj, 'day', 'day up');
console.log('apply test:------------------------------------------');
sayHello.myApply(thisObj, ['day', 'day up']);
console.log('bind test:------------------------------------------');
const testBind1 = sayHello.myBind(thisObj, 'day')('day up');
console.log('bind 构建函数 new test:--------------------------------');
const bindCurry = sayHello.myBind(thisObj, 'day');
// 使用构造函数的形式来调用bind后的函数时, undefined day day up是对的。因为改变了this指向
const testBind2 = new bindCurry('day up');
function Student(name) {
this.name = `${name}, day day up`;
}
console.log('new test:------------------------------------------');
const student = _new(Student, 'zaoren');
console.log('student', student.name);
console.log('curry test:------------------------------------------');
var fn = curry(function(a, b, c) {
return `${a} ${b} ${c}`;
});
console.log( fn("zaoren", "day", "dayup")); // ["a", "b", "c"]
console.log( fn("zaoren", "day")("dayup")); // ["a", "b", "c"]
console.log( fn("zaoren")("day")("dayup")); // ["a", "b", "c"]
console.log( fn("zaoren")("day", "dayup")); // ["a", "b", "c"]
console