提示💡
将当前函数添加到context上, 调用并传入参数。
call()
ES5 写法
Function.prototype.myCall = function(context) {
// 首先判断调用对象
if(typeof this !== 'function') {
throw new TypeError('error')
}
// 获取参数
var args = [...arguments].slice(1); //第一个是对象,从第二个开始
var result = null
// 判断content(this指针要指向的对象)是否传入, 如果没有设置为window
context = context || window
// 将调用函数设置为对象的方法
context.fn = this
// 调用函数
result = context.fn(...args);
// 删除属性
delete context.fn
return result
}
ES6 写法
提示💡
- 1.判断当前
this
是否为函数,防止Function.prototype.myCall()
直接调用- 2.
context
为可选参数,如果不传的话默认上下文为window
- 3.为
context
创建一个Symbol
(保证不会重名)属性,将当前函数赋值给这个属性- 4.处理参数,传入第一个参数后的其余参数
- 4.调用函数后即删除该
Symbol
属性
Function.prototype.myCall = function(context = window, ...args) {
// 首先判断调用对象
if(typeof this !== 'function') {
throw new TypeError('error')
}
//将调用函数设置为对象的方法
const fn = Symbol(); // 避免重名
context[fn] = this
// 调用函数
const result = context[fn](...args);
// 删除属性
delete context[fn]
return result
}
测试代码
const obj = {};
const f = function () {
return this;
};
f() === window // true
f.myCall(obj) === obj // true
apply()
提示💡
apply
实现类似call
,参数为数组。
ES5 写法
Function.prototype.myApply = function (context) {
// 首先判断调用对象是否为函数
if(typeof this !== 'function') {
throw new TypeError('error')
}
var result = null
// 判断传入的对象是否存在,在浏览器中默认是window, 在node.js中默认是Object
context = context || window
// 把当前调用的函数赋值给传入对象的
// context.fn 可以理解为: context.prototype.
context.fn = this
if (arguments[1]) {
result = context.fn([...arguments[1]]) // 调用赋值的函数
}
delete context.fn
return result
}
ES6写法
Function.prototype.myApply = function (context = window, args) {
// 首先判断调用对象是否为函数
if(typeof this !== 'function') {
throw new TypeError('error')
}
if (this === Function.prototype) return; // 用于防止 Function.prototype.myCall() 直接调用
// 把当前调用的函数赋值给传入对象的
// context.fn 可以理解为: context.prototype.
const fn = Symbol();
context[fn] = this
let result;
if (Array.isArray(args)) {
result = context[fn](...args);
} else {
result = context[fn]();
}
delete context.fn
return result
}
测试代码
function f(x, y){
console.log(x + y);
}
f.myCall(null, 1, 1) // 2
f.myApply(null, [1, 1]) // 2
bind()
简单版本
Function.prototype.myBind = function (obj) {
const fn = this;
return function () {
fn.apply(obj);
};
};
测试代码
var counter = {
count: 0,
inc: function () {
this.count++;
}
};
var func = counter.inc.myBind(counter);
func();
counter.count // 1
进阶版本: 函数柯里化、new
关键字
提示💡
- 修改
this
指向- 动态传递参数(函数柯里化)
// 方式一:只在bind中传递函数参数 fn.bind(obj,1,2)() // 方式二:在bind中传递函数参数,也在返回函数中传递参数 fn.bind(obj,1)(2)
- 兼容
new
关键字
Function.prototype.myBind = function (context) {
// 判断调用对象是否为函数
if (typeof this !== "function") {
throw new TypeError("Error");
}
// 获取参数
const args = [...arguments].slice(1),
fn = this;
return function Fn() {
// 根据调用方式,传入不同绑定值
return fn.apply(this instanceof Fn ? new fn(...arguments) : context, args.concat(...arguments));
}
}
测试代码
function fn(...args) {
console.log(this, args);
}
let obj = {
myname: "张三",
};
const bindFn = fn.myBind(obj, 0); // this 也会变成传入的obj ,bind不是立即执行需要执行一次
bindFn(1, 2); // this指向obj
fn(1, 2); // this指向window
new bindFn(1, 2); // 兼容new关键字