bind/apply/call
一.联系与区别
相同点
三个方法都是Function的原型方法,都可以改变this的指向,第一个参数都是this需要指向的对象,如果第一个参数为null或undefined时,指向window或者Global。
不同点
- call和apply用法基本一致,call'的参数是依次传参,是一一映射的。apply除了一个绑定的对象外,剩下的参数都放在一个数组里,作为第二个参数传回去。call和apply都是对函数直接调用的。
- bind的传参与call相同,但是bind方法返回的还是一个函数。
function fn(age) {
console.log(`我是${this.name},今年${age}岁,喜欢吃${this.fruit}`)
}
let obj1 = {
name:"张三",
fruit:"苹果"
}
let obj2 = {
name:"李四",
fruit:"西瓜"
}
fn()
fn.call(obj1,18)
fn.apply(obj2,[19])
fn.bind(obj2,20)()
///////
我是undefined,今年undefined岁,喜欢吃undefined
我是张三,今年18岁,喜欢吃苹果
我是李四,今年19岁,喜欢吃西瓜
我是李四,今年20岁,喜欢吃西瓜
二. 如何实现一个bind?
Function.prototype.bind(),
分析:
- bind是接收n个参数,但是第一个值为绑定的对象。
- bind的返回是返回一个函数 先按照思路写一些伪代码:
function _bind(params){
处理原本的this
获取剩下的参数 ...args
返回一个function(){
改变一个this的指向,并可以直接调用
}
}
function _bind(context){
const fn = this;
const args = Array.prototype.slice.call(arguments,1)
return function (...innerArgs) {
return fn.apply(context,[...args,...innerArgs])
}
}
测试使用一下:
Function.prototype._bind = function(context){
const fn = this;
const args = Array.prototype.slice.call(arguments,1)
return function (...innerArgs) {
const all = [...args,...innerArgs]
return fn.apply(context,all)
}
}
function show() {
this.bar = "bar"
return this.name
}
var fnShow = show._bind({name:"name2"})
console.log(fnShow())
打印结果:name2
但是如果使用new关键字,new中this的指向会高于bind,bind的返回的函数如果作为构造函数,搭配new关键字出现的话,这种绑定就需要被忽略,绑定到实例上。
Function.prototype._bind = function(context){
const fn = this;
const args = Array.prototype.slice.call(arguments,1)
//创建一个F函数
const F = function () {}
//将this的原型对象指向F的原型对象,这里保存,之后可以用 this instanceof F 来判断是不是构造函数
F.prototype = this.prototype
let bound = function () {
let innerArgs = Array.prototype.slice.call(arguments)
const finalArgs = [...args,...innerArgs]
// 在这里进行new 的判断
return fn.apply(this instanceof F ? this : context || this , finalArgs)
}
bound.prototype = new F()
return bound
}
function fn(name) {
this.bar = "bar"
this.name = name
}
function show() {
console.log(this.name)
}
var fnShow =show._bind(new fn('baz'))
console.log(fnShow())
构造函数中
this指向new创建的实例。(this instanceof F)所以可以通过在函数内判断this是否为当前函数的实例进而判断当前函数是否作为构造函数。
三、如何实现一个call?
分析一下call的使用方法
function show(bar,baz,foo) {
console.log(this.name || "",bar,baz,foo)
}
let obj = {
name :"name"
}
show('bar','baz','foo')
show.call(null,'bar','baz','foo')
show.call(obj,'bar','baz','foo')
- 用函数绑定call之后,对函数直接调用
- 没有绑定对象,立即执行 分析:
- 参数依次展开 context ,参数1,参数2,参数3....
- if(对象){ 修改this指向,执行 }else{ 直接执行 }
Function.prototype._call = function (context,...args) {
//this 为 Function 给context绑上要执行的方法,用显示调用的方式进行模拟
context.fn = this
if(context){
const result = context.fn(...args)
delete context.fn
return result
}else{
return this(...args)
}
}