call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数,该方法的语法和做用与 apply() 方法相似,只有一个区别,就是 call() 方法接受的是一个参数列表,而 apply() 方法接受的是一个包含多个参数的数组
在说实现自定义call、apply以前,咱们首先看下一段代码es6
var age = 6; var person = { age: 3, fn: function() { console.log(this.age); } } var fn = person.fn; fn(); person.fn();
结果是数组
6 3
为何呢?记住一句话:this的最终指向的是那个调用它的对象var fn = person.fn
,将person
的fn
方法赋值给fn
,但未调用,而fn()
调用的时候实际等于window.fn()
,因此fn中的this指向window
,因此fn()
的结果是window.age = 6
而person.fn()
是person
这个对象调用的,因此此时fn
函数中this
的指向是person
这个对象。因此this.age = 3
app
咱们本身实现call方法,思路就是基于此函数
Function.prototype.myCall = function(_context) { // 当_context未传值或者为null或者undefined,context指向window var context = _context || window; // 给对象添加一个方法,这个this就是使用call使用的函数 context.fn = this; // 拼接执行eval时候的参数 var args = []; for(var i = 1; i < arguments.length; i++) { args.push('arguments['+ i +']'); } // 字符串拼接数组,数组隐式转换成args.toString()即'context.fn('+ args +')', => 'context.fn(arguments[1])' var ret = eval('context.fn('+ args +')'); delete context.fn; return ret; } Function.prototype.myCallEs6 = function(context = window) { // 给对象添加一个方法,这个this就是使用call使用的函数 context.fn = this; // 获取除了第一个之外参数 const args = [...arguments].slice(1); // 执行方法,此时执行方法的this指向的是context const ret = context.fn(...args); delete context.fn; // 返回执行结果 return ret; } const person = { age: 3 }; function fn(name) { this.name = name; console.log(this); } fn.myCall(person, 'xzf'); fn.call(person, 'xzf');
call和apply做用相同,仅仅只是参数不一样,apply的参数以数组的形式传入,因此咱们只要作一下修改this
Function.prototype.myApply = function(_context) { // 当_context未传值或者为null或者undefined,context指向window var context = _context || window; // 给对象添加一个方法,这个this就是使用call使用的函数 context.fn = this; var ret; var args = arguments[1]; if(args) { // 假设args为['123','abc'],['123','abc'].toString()会转换成'123,abc' // 执行eval的时候,会变成一个数字,一个变量,此时是不存在变量abc的 // 须要经过上面mycall相似的方法进行转换 var argsArr = []; for(var i = 0; i < arguments.length; i++) { argsArr.push('args['+ i +']'); } ret = eval('context.fn('+ argsArr +')'); } else { ret = context.fn(); } delete context.fn; return ret; } // es6 Function.prototype.myApplyEs6 = function(context = window) { // 当_context未传值或者为null或者undefined,context指向window // 给对象添加一个方法,这个this就是使用call使用的函数 context.fn = this; let ret; // 若是有参数,展开数组参数传入 if(arguments[1]) { ret = context.fn(...arguments[1]); } else { ret = context.fn(); } delete context.fn; return ret; }