JavaScript:call和apply的ES6和ES5实现
JavaScript:call和apply的ES6和ES5实现
首先来看一下call和apply的定义和作用
**
call()
**方法使用一个指定的this
值和单独给出的一个或多个参数来调用一个函数。
apply()
方法调用一个具有给定this
值的函数,以及以一个数组(或类数组对象)的形式提供的参数。
总的来说,call和apply的区别就是参数的不同。
call的实现
举个例子:
var foo = {
value: 1
};
function bar() {
console.log(this.value);
}
bar.call(foo); // 1
需要注意的两点是:
call 改变了 this 的指向,指向到 foo
bar 函数执行了
call实现的第一步:
当调用 call 的时候,可以把 foo 对象改造成如下:
var foo = {
value: 1,
bar: function() {
console.log(this.value)
}
};
foo.bar(); // 1
但是这样会给foo对象多加了一个属性,不过可以使用之后可以删除。
所以这个可以拆分成三步:
将函数设为对象的属性
执行该函数
删除该函数
// 第一步
foo.fn = bar
// 第二步
foo.fn()
// 第三步
delete foo.fn
所以call的v1可以这样写:
// callES6v1
Function.prototype.myCall = function(context) {
// 首先要获取调用call的函数,用this可以获取
context.fn = this;
context.fn();
delete context.fn;
}
接下来就是要注意的几个问题:
1.this 参数可以传 null,当为 null 的时候,视为指向 window
2.函数是可以有返回值的!
3.call可以传入多个参数
4.给对象添加的属性名不能冲突
5.调用call的必须是函数
这些都是小问题:
// callES6v2
Function.prototype.myCall = function(context, ...args) {
if(typeof this !== 'function') {
throw new TypeError('error');
}
// this 参数可以传 null,当为 null 的时候,视为指向 window
context = context || window;
// 给对象添加的属性名不能冲突
const fn = Symbol();
context[fn] = this;
// call可以传入多个参数
let result = context[fn](...args);
delete context[fn];
// 函数是可以有返回值的
return result;
}
以上就是call的es6实现,下面手写es5实现。es5实现逻辑和es6一样,前面都分析好了,就是语法的问题而已。
需要理解的语法:
eval()
函数会将传入的字符串当做 JavaScript 代码进行执行。可以理解为HTML5中的script标签。
// callES5
// 模拟symbol的函数
function mySymbol(obj) {
var unique_proper = 'hcb' + Math.random();
if(obj.hasOwnProperty(unique_proper)) {
unique_proper += Math.random();
} else {
return unique_proper;
}
}
Function.prototype.myCallES5 = function (context) {
if(typeof this !== 'function') {
throw new TypeError('error');
}
context = context || window;
fn = mySymbol(context);
context[fn] = this;
// 如果只有一个参数的话,那么直接返回对象执行绑定函数的结果。
if(arguments.length < 2) {
return context[fn]();
}
var args = [];
for(var i = 1; i < arguments.length; i++) {
args.push(arguments[i]);
}
var fnStr = 'context[fn](';
for(var i = 0; i < args.length; i++) {
fnStr += i == args.length - 1 ? args[i] : args[i] + ',';
}
fnStr += ')';
var result = eval(fnStr);
delete context[fn];
return result;
}
同理,apply的es6实现和es5实现如下:
Function.prototype.myApplyES6v2 = function (context, args) {
if(typeof this !== 'function') {
throw new TypeError('error');
}
context = context || window;
fn = Symbol();
context[fn] = this;
let result = context[fn](...args);
delete context.fn;
// 这里为什么要return呢,因为可能绑定的函数会返回一个对象
return result;
}
// es5的写法
function mySymbol(obj) {
var unique_proper = 'hcb' + Math.random();
if(obj.hasOwnProperty(unique_proper)) {
unique_proper += Math.random();
} else {
return unique_proper;
}
}
Function.prototype.myApply = function (context) {
if(typeof this !== 'function') {
throw new TypeError('error');
}
context = context || window;
var args = arguments[1];
var fn = mySymbol(context);
context[fn] = this;
if(arguments.length < 2) {
return context[fn]();
}
var fnStr = 'context[fn](';
for(var i = 0; i < args.length; i++) {
fnStr += i == args.length - 1 ? args[i] : args[i] + ',';
}
fnStr += ')';
var result = eval(fnStr);
delete context[fn];
return result;
}
------------- 本文结束 感谢阅读 -------------