Function.prototype.mycall = function (context, ...argus) {
if (typeof this !== 'function') {
throw new TypeError('not funciton')
}
const fn = this
let result = null
context = context || window
context.fn = fn
result = context.fn(...argus)
delete context.fn
return result
}
Function.prototype.myapply = function (context, ...argus) {
if (typeof this !== 'function') {
throw new TypeError('not funciton')
}
const fn = this
let result = null
context = context || window
argus = argus && argus[0] || []
context.fn = fn
result = context.fn(...argus)
delete context.fn
return result
}复制代码
class Member {
constructor (options) {
const {name, sex, age} = options
this.name = name
this.sex = sex
this.age = age
}
introduce () {
console.log(`I'm ${this.name}, ${this.age}, ${this.sex}`) } } const member1 = new Member({ name: 'gina', sex: 'girl', age: 23 }) const member2 = new Member({ name: 'gun', sex: 'boy', age: 24 }) member2.introduce.mycall(member1) // I'm gina, 23, girl
member2.introduce.myapply(member1) // I'm gina, 23, girl复制代码
Math.max.myapply(null, [1,2,3,4]) // 4
Math.max.mycall(null, 1,2,3,4) // 4复制代码
if (typeof this !== 'function') {
throw new TypeError('not funciton')
}复制代码
Math.max.mycall(null, 1,2,3,4)
的时候,mycall函数内部的this指向了Math.max函数,因此咱们能够经过const fn = this
获取到要执行的函数,而后将该函数绑定到传入的context
对象(context.fn = fn
),而后再把它删除掉delete context.fn
。整体来讲,call和apply的实现仍是比较简单的。git
Function.prototype.mybind = function (context, ...argus) {
if (typeof this !== 'function') {
throw new TypeError('not funciton')
}
const fn = this
const fBound = function (...argus2) {
return fn.apply(this instanceof fBound ? this : context, [...argus, ...argus2])
}
fBound.prototype = Object.create(this.prototype)
return fBound
}复制代码
const foo = {
v: 1
};
function bar() {
return this.v;
}
const bindFoo = bar.mybind(foo);
bindFoo() // 1复制代码
bind
函数返回的是一个可执行函数,因此return
了一个函数。此刻返回的函数,按正常来讲,在执行的时候,this是指向执行处的当前上下文。但该案例中, mybind
须要知足bar在执行中返回值时,this
依然是指向 foo,因此咱们在mybind
返回的函数中须要使用fn.apply
来保持上下文和执行mybind
的时候一致。github
const foo = {
v: 1
};
function bar(name, age) {
console.log(this.v);
console.log(name);
console.log(age);
}
const bindFoo = bar.bind(foo, 'daisy');
bindFoo('18');
// 1
// daisy
// 18复制代码
mybind
须要作到能够接受传参,而且将参数给到bar
函数,后面再执行bindFoo
再传的参数,会接在以前传参的后面。因此mybind
源码中使用了[...argus, ...argus2]
来进行参数整合。bash
const value = 2;
const foo = {
value: 1
};
function bar(name, age) {
this.habit = 'shopping';
console.log(this.value);
console.log(name);
console.log(age);
}
bar.prototype.friend = 'kevin';
const bindFoo = bar.bind(foo, 'daisy');
const obj = new bindFoo('18');
// undefined
// daisy
// 18
console.log(obj.habit);
console.log(obj.friend);
// shopping
// kevin复制代码
在执行const obj = new bindFoo('18')
这一 new
操做的时候,此刻this
应该指向当前对象obj
。因此mybind
在fn.apply
的第一个参数,作了这样的判断this instanceof fBound ? this : context
。app
在const obj = new bindFoo('18')
内部执行到this instanceof fBound ? this : context
时,此刻this
指向obj
,fBound
其实也就是bindFoo
,this instanceof fBound
判断了obj
是否是继承自bindFoo
,也就是进行了构建函数new
操做。函数
function bar() {}
bar.prototype.value = 2
const bindFoo = bar.mybind(null);
bindFoo.prototype.value = 1;
console.log(bar.prototype.value) // 2复制代码
mybind
执行后返回的函数fBound
修改prototype
的时候,不该该影响到fn.prototype
,二者应该是独立的。因此源码使用了fBound.prototype = Object.create(this.prototype)
, 而不是fBound.prototype = this.prototype
。ui
总得来讲,bind
的实现考虑的点仍是比较多的。this