/**
* @desc call方法改变this指向的同时,需要将函数执行
*/
Function.prototype.myCall = function(context) {
// 首先,myCall必须是Function类型才能调用
if (Object.prototype.toString.call(this).slice(8, -1) !== 'Function') {
console.error('只有函数才能调用call函数');
return;
}
// 缺省的情况下,context为window。当context为简单类型的时候,强转成Object类型
context = Object.create(context) || window;
// step1: 获取参数
const args = Array.from(arguments).slice(1);
// step2: 将 this 作为 context 的fn属性。并且将context.fn执行。这时候该函数内部的this就为context了。
context.fn = this;
const result = context.fn(...args);
// step3: 将fn属性删除
delete context.fn;
// step4: 返回执行结果
return result;
}
/**
* @desc apply 和 call 的功能一样。区别仅在于传参的方式不一样,是以数组的形式。
*/
Function.prototype.myApply = function(context) {
// 首先判断 this 的类型,保证调用 apply 的是函数
if (Object.prototype.toString.call(this).slice(8, -1) !== 'Function') {
console.error('只有函数类型才有apply属性');
return;
}
// step0: context 缺省的情况下使用window、为了避免简单类型,用Object.create 强转一下
context = context ? Object.create(context) : window;
// step1: 获取参数,这里是apply,直接拿第二个参数即可
const args = arguments[1];
// step2: 将this作为context的某个属性,并调用该属性执行
context.fn = this;
const result = context.fn(...args);
// step3: 删除 context 上的fn 方法
delete context.fn;
// step4: 返回执行结果
return result;
}
/**
* @desc 实现一个bind函数,和apply的区别是:他并不会立即执行
* 而且支持函数的柯里化
*/
Function.prototype.myBind = function(context) {
// 首先,判断调用myBind的类型
if (Object.prototype.toString.call(this).slice(8, -1) !== 'Function') {
console.error('只有函数类型有myBind属性');
return;
}
// step1: 获取参数
const args = Array.from(arguments).slice(1);
// step2: 用self保存this,这个this最终要被执行
const self = this;
const returnFn = function() {
// 支持柯里化,bind之后的函数还可以再传参
const extArgs = Array.from(arguments);
// step2: 利用apply方法执行self。
// step3: 兼容bind后的函数,作为构造函数调用时,this 指向实例
// step4: bind支持函数柯里化
self.apply(this instanceof returnFn ? this : context, [...args, ...extArgs]);
}
// 保持原型
// returnFn.prototype = this.prototype; // 这样写的话、在原型上新增方法或者修改属性,绑定函数的原型也会跟着改变
const fn = function() {};
fn.prototype = this.prototype;
returnFn.prototype = new fn();
return returnFn;
}
/**
* @Desc 手动实现一个new
*/
function _new(fn, ...args) {
// step1: 创建一个空对象、并且把对象的原型指向构造函数
const obj = Object.create(fn.prototype);
// step2: 借助apply,用生成的对象为this调用构造函数
const ret = fn.apply(obj, args);
// step3: 判断构造函数的返回值是否是对象类型,如果是,直接返回。如果不是,则返回新生成的对象
return ret instanceof Object ? ret : obj;
}
function sayHello(param1, param2, param3) {
console.log(this.name, param1, param2);
}
const thisObj = {
name: 'zaoren'
}
// sayHello.call(thisObj, 'day', 'day up');
sayHello.myCall(thisObj, 'day', 'day up');
sayHello.myApply(thisObj, ['day', 'day up']);
const wSayHello = sayHello.myBind(thisObj, 'day')('day up');
function Student(age) {
this.name = 'zaoren';
this.age = age;
return this;
}
const student = _new(Student, '18');
console.log('student', student);
console