window.name = 'window'
var obj = {
name: 'obj'
}
function getName(p1, p2) {
console.log(p1, p2)
console.log(this.name)
}
getName('str1', 'str2')
getName.call(obj, 'str1', 'str2')
// 函数运行结果能够思考一下。
复制代码
怎么记住call呢,其实就是getName这个方法执行了,不是window去执行,是call括号内的参数去执行。数组
能够联想到一个很典型的使用场景 [].prototype.shift.call(arguments),起初很不理解这种写法,后来一想,其实就是 arguments 不是数组,它没有shift方法能够直接来用,就把数组的shift方法拿来用。app
1 明确是谁调用call,答案,是函数。函数
2 call接收的参数是什么?第一个参数是要改变的this指针,也就是上面说到了,是谁去执行这个函数。若无指定,默认为windowthis
3 call接收的第二个,第三个,等等,参数,是用来作什么的?答,就是做为调用call的那个函数所需的参数。spa
function myCall(context) {
// 1
if (typeof this !== 'function'){
throw new TypeError('error')
}
// 2
context = context || window
// 3
context.fn = this
// 4
const args = [...arguments].slice(1)
// 5
const result = context.fn(...args)
// 6
delete context.fn
return result
}
Function.prototype.myCall = myCall
getName.myCall(obj, 'str1', 'str2')
复制代码
apply使用与call大致一致,只是接受参数的方法不一样。call能够接收多个参数。apply接收的第一个参数是this,第二个参数是 所需参数所组成的数组。prototype
window.name = 'window'
var obj = {
name: 'obj'
}
function getName(p1, p2) {
console.log(p1, p2)
console.log(this.name)
}
getName('str1', 'str2')
getName.apply(obj, ['str1', 'str2'])
复制代码
Function.prototype.myApply = function(context) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
context = context || window
context.fn = this
var result
if (arguments[1]) {
result = context.fn(...arguments[1])
} else {
result = context.fn()
}
delete context.fn
return result
}
复制代码
和上述的call实现基本相似,就参数处理有些不一样,再也不赘述。指针
为何有call apply后还要有个bind ? 当咱们须要绑定一个点击事件的时候,就改变回调函数的this,怎么破?由于 call apply都是当即执行了,因此bind登场。看一下下面这个例子吧。code
var obj = {
name: 'obj'
}
document.addEventListener('click',myClick.bind(obj,'p1','p2'),false);
function myClick(p1,p2){
console.log(this.name, p1, p2)
}
复制代码
MDN的解释是:bind()方法会建立一个新函数,称为绑定函数,当调用这个绑定函数时,绑定函数会以建立它时传入 bind()方法的第一个参数做为 this,传入 bind() 方法的第二个以及之后的参数加上绑定函数运行时自己的参数按照顺序做为原函数的参数来调用原函数。cdn
注意:bind方法的返回值是函数对象
bind()最简单的用法是建立一个函数,使这个函数不论怎么调用都有一样的this值。
由于bind返回值是函数,那么函数除了直接运行以外,还能够做为构造函数放在new 操做符以后,因此bind的实现就要把这种状况考虑进去。
任何函数均可以做为构造函数,放在 new 以后使用,那么new的过程是怎么样的呢?大致分为如下几步,具体不深究。
上面所说的那个空对象就是构造函数内部的this,而且 对于 new 的状况来讲,不会被任何方式改变 this
window.name = 'window'
var obj = {
name: 'obj'
}
function Fun(p1,p2){
console.log(this)
console.log(this.__proto__ === Fun.prototype)
console.log(this.name)
this.a = p1
this.b = p2
console.log(this)
}
var c = new Fun('str1', 'str2')
console.log(c)
复制代码
运行结果以下:
![]()
再来看一下,直接执行Fun的返回结果,代码不作修改,直接执行 Fun('str1', 'str2')
两次结果的不一样,本文不展开叙述。那么举这个例子是想说明什么呢? 答 : 函数充当构造函数和普通函数,运行时内部的this指向不一样。 接下来看bind函数的模拟实现
Function.prototype.myBind = function (context) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
const _this = this
const args = [...arguments].slice(1)
// 返回函数
return function F() {
// 1 判断是否用做构造函数
if (this instanceof F) {
return new _this(...args, ...arguments)
}
// 2 用做普通函数
return _this.apply(context, args.concat(...arguments))
}
}
// 仍是用上述举例子
window.name = 'window'
var obj = {
name: 'obj'
}
function Fun(p1, p2){
this.a = p1
this.b = p2
console.log(this.name)
console.log(p1, p2)
}
var f1 = Fun.bind(obj, 'str1')
f1('str2')
复制代码
运行结果以下,可见 改变了fn1函数的this指向
再来看一下 ,去掉 f1('str2') ,换成以下语句的运行结果
// f1('str2')
var b = new f1('str2')
console.log(b)
复制代码
没有逐行的说明,不过对于内部实现,有new的前置介绍和注释,相信开始的call实现代码都看懂了的话,这个bind方法也会一目了然