根据B站的小野森森老师的视频整理,代码有所修改。
主要有以下难点:
this
指向解决方法是:函数内部的this
指向是通过其调用者决定的,因此可以通过将call
等的第一个参数(假设为ctx
)上增加一个属性(假设为originFn
),保存当前调用函数的地址,通过调用ctx.originFn
来改变this
指向。然后通过将其余参数拼接为字符串,调用eval
方法将字符串转为表达式的方式传参。
源码如下:
1、call
// call Function.prototype.myCall = function (ctx) { ctx = ctx == null ? window : Object(ctx); var args = []; for (var i = 1; i < arguments.length; i++) { args.push("arguments[" + i + "]"); } var originFn = new Date().getTime(); // 保证不重名 ctx.originFn = this; var ret = eval("ctx.fn(" + args + ")"); delete ctx.originFn ; return ret; }
2、apply
// apply Function.prototype.myApply = function (ctx, args) { if (typeof args !== "object" && typeof args !== "function") { throw new TypeError("CreateListFromArrayLike called on non-object"); } args = [].slice.call(args); ctx = ctx == null ? window : Object(ctx); var _args = []; for (var i = 0; i < args.length; i++) { _args.push("args[" + i + "]"); } var originFn = new Date().getTime(); // 保证不重名 ctx.originFn = this; var ret = eval("ctx.fn(" + _args + ")"); delete ctx.originFn ; return ret; }
3、bind
// bind Function.prototype.myBind = function (ctx) { var args = [].slice.call(arguments, 1), _tempFn = function () { }, originThis = this; var newFn = function () { var newArgs = [].slice.call(arguments); // 这里需要判断返回的函数是不是使用new调用的,如果是,则需要保证把这个函数当作普通的构造函数使用,不能改变其内部this为ctx,并且生成的实例需要继承调用bind的函数 return originThis.apply(this instanceof newFn ? this : ctx, args.concat(newArgs)); } _tempFn.prototype = this.prototype; newFn.prototype = new _tempFn(); return newFn; }
[参考链接]
[1]、小野森森老师的视频