一. 概览
函数call,apply,bind方法在工作中经常用到,所以对其底层实现逻辑的探究很有必要,本文就从0开始手写封装call,apply,bind方法。
二. call,apply
原生call,apply
var name = 'sandy'
function person(a,b,c) {
console.log(this.name)
console.log(a,b,c,'a,b,c')
return {
a,b,c
}
}
const egg = {name: 'jack'}
let result = person.call(egg,'a','b','c');
let result = person.apply(egg,['a','b','c'])
封装思路
- 改变this指向,实则是将调用的函数包含在新的this的属性中;
- 当传参为null时,默认是全局对象;
- apply和call差别不大,主要差别是apply传的是数组
封装代码(call)
Function.prototype.newMycall = function(obj) {
// this 为要调用的函数
var obj = obj || window
obj.p = this;
const arr = [].slice.call(arguments,1)
let result = obj.p(...arr);
delete obj.p;
return result
}
let result = person.newMycall(null,'a','b','c')
封装代码(apply)
Function.prototype.newMyapply = function(obj) {
// this 为要调用的函数
var obj = obj || window
obj.p = this;
const arr = arguments[1] || []
let result = obj.p(...arr);
delete obj.p;
return result
}
let result = person.newMyapply(egg,['a','b','c'])
三. bind
原生用法
function person(a,b,c) {
console.log(this.name)
console.log(a,b,c,'a,b,c')
return {
a,b,c
}
}
person.prototype.age = 18
const egg = {name: 'jack'}
const aa = person.bind(egg,'a','b')
// 构造函数用法
const sl = new aa('c')
1. 此时this会恢复默认的,指向全局;
2. s1会继承aa函数实例上的方法和属性
bind方法的封装较apply和call要复杂些,现在一步一步去实现
1. 柯里化传参实现
通过原生bind我们可以看到,bind的使用是传了两次参的,但是最后调用是在一个函数里面调用的,也就是说两次传参聚合在一个函数里面使用,也就是咱们所说的柯里化传参
实现代码
Function.prototype.newBind = function(obj) {
// this 为要调用的函数
var obj = obj || window;
const that = this;
const arr1 = [].slice.call(arguments,1)
return function() {
const arr2 = [].slice.all(arguments)
const arr = [...arr1,...arr2]
that.apply(obj,arr)
}
}
这样就初步实现了简单的bind封装,但是不能满足使用构造函数实例的场景,所以虚进一步封装
2. 最终实现
封装思路:
- bind()调用之后应该返回一个构造函数,且要继承将要调用的函数的原型上的方法和属性;
- 当实例用法时,要调用函数中的this指向全局;
- instanceof方法用于区分是实例用法,还是简单的函数用法
- 用实例继承的原因是为了避免原型上的相互影响
Function.prototype.newBind = function(obj) {
// this 为要调用的函数
var obj = obj || window;
const that = this;
const arr1 = [].slice.call(arguments,1)
const o = function() {}
const newFun = function () {
const arr2 = [].slice.call(arguments,0)
const arr = [...arr1,...arr2];
let data;
if(this instanceof newFun) {
that.apply(this,arr);
} else {
const data = that.apply(obj,arr);
return data
}
}
o.prototype = that.prototype
newFun.prototype = new o()
return newFun
}
问题
为什么不直接用newFun.prototype = new that()?
new person()的用法是将person作为构造函数去使用, 但是最后调用的person方法中有return ,会改变返回的实例值
(1) 当return一个基本类型时,return不会发生作用,构造函数依旧返回一个实例对象;
(2) 当return一个引用类型时,return会覆盖创建实例,构造函数返回自己手写的引用类型值;
(3) 当只使用return时,return只会终止代码执行。依旧返回一个创建的实例
版权声明:本文为m0_45093055原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。