持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第20天,点击查看活动详情
一、前言
apply和call的手写差不多,因为他们的基本概念差不多,都是改变函数执行时的上下文,或者改变函数调用时的this指向,只是在于他们的传参不同。而bind又与call的基本功能类似,传参方式也一致,但是它不会像call和apply一样直接调用,它只是改变this指向并返回改变this指向以后的函数不会自己调用。
展开说说
- call给函数传参的时候,是作为call的第二个参数以后所有的参数 逗号隔开:
call(thisArg,arg1,arg2.....) - apply给函数传参的时候,函数的参数全部放在数组中,作为apply的第二个参数:
apply(thisArg,[arg1,arg2...])3.bind和call一样:bind(thisArg,arg1,arg2.....)只改变this指向、返回函数、不默认调用
二、手写apply
因为call和apply只是参数的不同,详细拆分讲解可以参考上一篇手写call:手写call(面试篇)
1.apply的基本使用
var obj = {name:'lucky'}
function fn(a,b){
console.log(this,a+b) //obj,3
}
fn.apply(obj,[1,2])
2. 手写apply
var obj = {name:'lucky'}
Function.prototype.myApply = function(context){
var type = typeof context;
//判断改变后的上下文对象是null和undefined的时候 this应该指向window
if (context === null || context === undefined) {
context = window;
}
//如果改变后的上下文对象是基本包装类型,则this指向其包装对象
switch (type) {
case "number":
context = new Number(context);
break;
case "boolean":
context = new Boolean(context);
break;
case "string":
context = new String(context);
break;
}
//获取第二个以后的参数,就是fn的参数
var arg = arguments[1];
//如果arg不存在 则返回空数组
arg = arg ? arg : [];
/***
梳理:
1.fn1.myApply调用,所以这里的this指向的就是fn1,
给context扩展一个方法,这个方法就是fn1:context[key]=this
2.context就是改变之后的上下文对象
3.然后调用context的扩展的这个方法,fn1就会被调用,并且this指向了context
**/
//给context扩展的方法名要是一个独一无二的值,防止覆盖原有方法
var key = Date.now().toString(36);
//context扩展的方法
context[key] = this;
//然后调用context的扩展的这个方法,fn1就会被调用,并且this指向了context
var result = eval("context[key](" + arg.toString() + ")");
//此时改变之后的上下文对象context就会多一个方,使用完成之后要删除掉这个方法
delete context[key];
return result;
}
function fn1(a,b){
console.log(this,a+b)
}
fn1.myApply(obj, [1, 2]);
fn1.myApply(null, [1, 2]);
fn1.myApply(undefined, [1, 2]);
fn1.myApply(1, [1, 2]);
fn1.myApply("str", [1, 2]);
fn1.myApply(true, [1, 2]);
验证结果
三、手写bind
1.bind基本使用
bind与call、apply不同,不是直接调用,而是改变this指向之后并返回改变后的函数(举例绑定事件)
document.onclick = function () {
//bind绑定与var that = this 同理
//var that = this
setTimeout((function () {
//console.log(that)//document
console.log(this) //document
}).bind(this), 100)
}
2.手写bind
思路:改变this指向,并且把函数返回出去
- 简易写法(先写出核心思路)
/**
思路梳理:
1.返回函数:return function{}
2.改变this:在返回函数里拿到当前函数调用call(apply)改变this指向
3.result()调用就是调用内部返回函数
**/
//myBind返回一个被改变this指向的函数
Function.prototype.myBind = function(){
//返回一个函数
return function(){
fn.call(obj,1,2)
}
}
var obj = {name:'lucky'}
function fn(a,b){
console.log(this,a+b) //obj,3
}
var result = fn.myBind(obj,1,2)
result()
- 完整写法
/**
思路梳理:
1.返回函数:return function{}
2.改变this:利用apply(call)改变this指向
3.result()调用就是调用内部返回函数
**/
//myBind返回一个被改变this指向的函数
Function.prototype.myBind = function(context){
//用_this保存当前的this 也就是调用myBind的函数(fn)
var _this = this;
//拿参数
var arg = Array.from(arguments).slice(1);
arg = arg ? arg : [];
//返回一个函数
return function () {
//利用apply改变this
return _this.apply(context, arg);
}
}
var obj = {name:'lucky'}
function fn(a,b){
console.log(this,a+b)
}
var result = fn.myBind(obj,1,2) //obj,3
//因为是内部调用的apply所以也不用考虑绑定对象的传值,会自动包装
var result = fn.myBind(null,1,2) //window,3
result()
好了,以上就是本篇文章的分享,感谢阅读!