模拟实现 call、apply 和 bind
call
介绍
一句话介绍 call:
call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。
举个例子:
// demo1
var student = {
name: 'xiaoming'
}
function people () {
console.log(this.name)
}
people.call(student) // xiaoming
// demo2
var student2 = {
name: 'xiaoming2'
}
function people2 (age, job) {
console.log(this.name)
console.log(age)
console.log(job)
}
people2.call(student2, 18, 'student')
// xiaoming2
// 18
// student
// demo3
var student3 = 'xiaoming3'
function people3 () {
console.log(this.student3)
}
people3.call(null) // xiaoming3
分析:
- 在 demo1 中,people 函数执行时,
call()
方法改变了this
的指向,指向到了 student。 - 在 demo2 中,people2 函数执行时,多传入了 2 个参数。
- 如果没有传递第一个参数,那么
this
值指向全局对象。见文档
实现
Function.prototype.myCall = function ($this) {
var context = $this || window // 判断是否传递第一个参数
var args = []
context.fn = this // 这里 this 指向调用 myCall() 的对象
// 遍历除第一位后面的参数
for (var i = 1, len = arguments.length; i < len; i++) {
args.push(arguments[i])
}
var res = context.fn(...args) // 返回 call 绑定函数的值
delete context.fn // fn 的对象属性,在这里是辅助作用,最后得删掉
return res
}
var v = 1
var foo = {
v: 1
}
function bar (name, age) {
console.log(this.v)
return {
name: name,
age: age,
value: this.v
}
}
bar.myCall(null) // 1
console.log(bar.myCall(foo, 'kevin', 18))
// 1
// Object {
// name: 'kevin',
// age: 18,
// value: 1,
// }
apply
介绍
一句话介绍 apply:
apply() 方法调用一个具有给定 this 值的函数,以及以一个数组(或类数组对象)的形式提供的参数。
因此除了传递参数的形式不同之外,其他实现和 call 类似。
实现
Function.prototype.myApply = function ($this, arr) {
var context = $this || window
var res
context.fn = this
if (!arr) {
res = context.fn()
} else {
res = context.fn(...arr)
}
delete context.fn
return res
}
var v = 1
var foo = {
v: 1
}
function bar (name, age) {
console.log(this.v)
return {
name: name,
age: age,
value: this.v
}
}
bar.myApply(null) // 1
console.log(bar.myApply(foo, ['kevin', 18]))
// 1
// Object {
// name: 'kevin',
// age: 18,
// value: 1,
// }
bind
介绍
一句话介绍 bind:
bind()
方法创建一个新的函数,在bind()
被调用时,这个新函数的 this 被指定为bind()
的第一个参数,而其余参数将作为新函数的参数,供调用时使用。
举个例子:
// demo1
var foo = {
v: 1
}
function bar (name, age) {
console.log(this.v)
console.log(name)
console.log(age)
}
var bindFoo = bar.bind(foo, 'daisy')
bindFoo('20')
// 1
// daisy
// 20
分析:
- 返回一个函数。
- 可以传入参数,在使用 bind 绑定时传入一个参数(name),在后面执行返回函数时再传入另一个参数(age)。
- 调用
bind()
方法是一个函数。 - 作为构造函数的绑定函数。见文档
实现
Function.prototype.myBind = function (context) {
if (typeof this !== 'function') {
throw new Error('调用 bind 的不是函数')
}
var $this = this
var args = Array.prototype.slice.call(arguments, 1)
var fnBind = function () {
var fnBindArgs = Array.prototype.slice.call(arguments)
var _this = null
if (this instanceof fnBind) {
_this = this
} else {
_this = context
}
return $this.apply(_this, args.concat(fnBindArgs))
}
fnBind.prototype = Object.create(this.prototype)
return fnBind
}
var value = 2
var fooBind = {
value: 1
}
function barBind (name, age) {
this.habit = 'shopping'
console.log('barBind', this.value)
console.log('barBind', name)
console.log('barBind', age)
}
barBind.prototype.friend = 'xiaohong'
var bindFoo = barBind.myBind(fooBind, 'xiaoming')
var obj = new bindFoo('20')
// barBind undefined
// barBind xiaoming
// barBind 20
console.log(obj.habit)
console.log(obj.friend)
// shopping
// xiaohong