目录
1. call
模拟实现代码如下:
// 函数用法简单示例 func.call(obj, param1, param2)
Function.prototype.myCall = function(context){
context = context || window; // context 是要调用函数的那个对象,即 obj
context.fn = this; // this 指的是调用 call 方法的那个函数,即 func,这里相当于让 obj 的 fn 属性指向 func
let args = [...arguments].slice(1); // 参数 param1 param2
let re = context.fn(...args); // 在 obj 上调用 func 方法,re 为返回值
delete context.fn; // 删除对象 obj 上的 fn 属性,相当于解除对象 obj.fn 对 func 的引用
return re; // 函数返回
}
2. apply
模拟实现代码如下:
// 函数用法简单示例 func.apply(obj, [param1, param2])
Function.prototype.myApply = function(context){
context = context || window;
context.fn = this;
let re;
if(arguments[1]){
re = context.fn(...arguments[1]);
}else{
re = context.fn();
}
delete context.fn;
return re;
}
前面两个方法实现总结:
它们相当于在某个对象里面添加了一个属性,该属性指向需要执行的函数,然后在调用了函数后从该对象上移除添加的属性。
3. bind
模拟实现代码如下:
// 函数用法简单示例 func1 = func.bind(obj, param1, param2)
Function.prototype.myBind = function (context) {
let _this = this; // 保留对当前 this 的引用,即函数 func,后续会用到
let args = [...arguments].slice(1);
return function F(){ // 会形成闭包
if(this instanceof F){ // 作为构造函数时,this 会指向 F 创建的实例对象
return new _this(...args,...arguments); // args 是开始绑定时传入的参数,arguments 接受新传入的参数
}
// 后续普通调用时
return _this.apply(context, args.concat(...arguments));
}
}
4. new
模拟实现代码如下:
function myNew() {
let Constructor = [].shift.call(arguments); // 这里修改了 arguments(严格模式下也有效),可用添加 'use strict' 进行测试
if(typeof Constructor !== 'function'){
throw new Error('The first arguments of new must be a function');
}
let obj = {};
Object.setPrototypeOf(obj, Constructor.prototype); // 原型链做链接
let res = Constructor.apply(obj, arguments); // 相当于构造函数 Constructor 调用,可能在 obj 上添加一些属性
return res instanceof Object ? res : obj; // 如果返回值是隐式返回,则返回新创建的对象 res,否则返回显示返回的对象 obj(这和Constructor函数是否有返回值有关)
}
5. 测试
上述模拟实现测试:
// 测试代码
var name = 'A';
var obj = {
name:'B'
}
function outputName(param1, param2){
console.log(param1, param2, this.name);
}
function Fn(name, age){
this.name = name;
this.age = age;
}
// 函数调用
outputName.myCall(obj, 1, 2); // 1 2 'B'
outputName.myApply(obj, [1, 2]); // 1 2 'B'
outputName.myBind(obj, 1, 2)(); // 1 2 'B'
console.log(myNew(Fn,'小明', 22)); // Fn {name: '小明', age: 22}