call和apply方法主要完成两项工作
- 改变函数中this指向
- 执行函数,并返回函数的执行结果
call方法模拟,传入参数是散列的形式
Function.prototype.call = function (context) {//context是指this新指向的函数对象
// 如果传入的函数对象为null,让this指向window对象
var context = context || window
// 修改this指向 : 将调用call的函数做为context的属性,再执行函数
context.fn = this;
//传入参数方法1:arguments获取不定参数列表
var args = [];
for (var i = 1, len = arguments.length; i < len; i++) {
args.push('arguments[' + i + ']');
}
var result = eval('context.fn(' + args + ')');//eval函数会默认识别字符串里的数组元素,逐个传递参数
//传入参数方法2:
// let arr = [...arguments]
// arr.shift()
// var result = context.fn(...arr)
//函数执行完后删除函数属性
delete context.fn;
return result
}
// 测试一下
var value = 2;
var obj = {
value: 1
}
function bar(name, age) {
console.log(this.value);
return {
value: this.value,
name: name,
age: age
}
}
bar.call(null); // 2
console.log(bar.call(obj, 'kevin', 18));
// 1
// Object {
// value: 1,
// name: 'kevin',
// age: 18
// }
apply方法模拟,传入参数是数组的形式
Function.prototype.apply = function (context, arr) {
var context = Object(context) || window;
context.fn = this;
var result;
if (!arr) {
result = context.fn();
}
else {
result = context.fn(...arr)
}
delete context.fn
return result;
}
bind方法模拟
bind()方法会创建一个新函数,当这个新函数被调用时,bind()的第一个参数作为它运行时的this,之后的一序列参数将会在传递的实参传入前作为它的参数
bind函数的特点 :
- 返回一个函数
- 返回的函数在执行时可以传入参数
例子 :
var foo = {
value: 1
};
function bar(name, age) {
console.log(this.value);
console.log(name);
console.log(age);
}
var bindFoo = bar.bind(foo, 'daisy');
bindFoo('18');//1 daisy 18
模拟实现
Function.prototype.bind = function (context) {
var self = this;//调用bind方法的函数
// 获取bind函数从第二个参数到最后一个参数
var args = Array.prototype.slice.call(arguments, 1);
return function () {
// 这个时候的arguments是指bind返回的函数传入的参数
var bindArgs = Array.prototype.slice.call(arguments);
return self.apply(context, args.concat(bindArgs));//函数可能有返回值
}
}
bind()返回值作为构造函数
一个绑定函数也能使用new操作符创建对象:这种行为就像把原函数当成构造器。提供的 this 值被忽略,同时调用时的参数被提供给模拟函数。
bind()方法最终版本模拟实现 :
Function.prototype.bind2 = function (context) {
var self = this;
var args = Array.prototype.slice.call(arguments, 1);
var fNOP = function () {};
var fBound = function () {
var bindArgs = Array.prototype.slice.call(arguments);
//当作为构造函数时,this指向实例对象,此时结果为true,将绑定函数的this指向改实例,可以让实例获得来自绑定函数的值
//当作为普通函数时,this指向window,此时结果为false,将绑定函数的this指向context
return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs));
}
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
}