Call
使用一个指定的this值,和单独给出的一个或多个参数来调用一个函数,达成可以改变当前函数的this指向,并且让当前函数执行
function fun() {
console.log(this.name, arguments)
}
let obj = { name: 'clying' }
fun.call(obj, 'deng', 'car')
实现
给函数原型添加mycall方法,创建一个上下文对象context,如果传入的对象不存在时,将指向全局window。通过给context添加fn属性,context的fn引用调用该方法的函数fun,并执行fun。执行完成之后删除该属性fn。
设置fn主要是为了实现函数执行这一步
Function.prototype.mycall = function (context, ...args) {
context = (context == null || context == undefined) ? window : new Object(context)
context.fn = this
let r = context.fn(...args)
delete context.fn
return r
}
Apply
pply方法接收的是一个包含多个参数的数组。
function fun() {
console.log(this.name, arguments);
}
let obj = {
name: 'clying'
}
fun.apply(obj, [22, 1])
实现
实现方法与call类似,不过在接收参数时,可以使用一个args作为传入的第二个参数。直接判断如果未传入第二个参数,直接执行函数;否则执行函数。
Function.prototype.myapply = function (context, args) {
context = (context == null || context == undefined) ? window : new Object(context)
context.fn = this
if(!args) return context.fn()
let r = context.fn(...args)
delete context.fn
return r
}
Bind
创建一个新的函数,不自动执行
这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。
function fun() {
console.log(this.name, arguments);
}
let obj = {
name: 'clying'
}
let b = fun.bind(obj,2)
b(3)
// clying Arguments(2) [2, 3]
实现
使用apply实现:
Function.prototype.myBind = function () {
// console.log(context)
// 取出第一个参数
let outContext = arguments[0];
// 取出剩余参数,Array.from() 第一个用途:将类数组对象转换成数组。
let outArgs = Array.from(arguments).slice(1);
let outThis = this; // 存外部this
return function cb() {
// 获取内部参数
let inArgs = Array.from(arguments);
return outThis.apply(outContext, outArgs.concat(inArgs));
};
};
var obj = { name: 1, age: 2 };
var name = "Leo",
age = 18;
function Fn(height, Gender) {
console.log(
"name:",
this.name,
"age:",
this.age,
"height:",
height,
"Gender:",
Gender
);
}
Fn(); // name: Leo age: 18 height: undefined Gender: undefined
var fn1 = Fn.myBind(obj, "80cm");
fn1(); // name: 1 age: 2 height: 80cm Gender: undefined
fn1("男"); // 1 age: 2 height: 80cm Gender: 男
let f1 = Fn.myBind(obj, "80cm")
new f1("60cm");
console.log("--------------bind----------------");
Fn(); // name: Leo age: 18 height: undefined Gender: undefined
var fn1 = Fn.bind(obj, "80cm");
fn1(); // name: 1 age: 2 height: 80cm Gender: undefined
fn1("男"); // 1 age: 2 height: 80cm Gender: 男
let f2 = Fn.bind(obj, "88cm")
new f2("69cm");
但是bind在使用new运算符构造绑定函数的时候,会忽略传入的thisArg。
上面这种实现可以看出,此时new后,还是把thisArg传到了构造函数里
同时为了保持构造函数的原型继承问题,需要把继承prototype
所以这里需要进行判断,是否进行了new
判断是否是new绑定的方式有两种
1、new.target
2、instanceof
Function.prototype.myBind = function () {
// console.log(context)
// 取出第一个参数
let outContext = arguments[0];
// 取出剩余参数,Array.from() 第一个用途:将类数组对象转换成数组。
let outArgs = Array.from(arguments).slice(1);
let outThis = this; // 存外部this
let cb = function () {
// 获取内部参数
const isNew = typeof new.target !== 'undefined' // 判断函数是否被new过
let inArgs = Array.from(arguments);
return outThis.apply(isNew ? this : outContext, outArgs.concat(inArgs));
}
cb.prototype = outThis.prototype// 继承构造函数原型
return cb
};