前端知识体系
GitHub (opens new window)

GuoLiBin6

程序员永不下班
GitHub (opens new window)
  • 介绍
  • 前端基础

    • CSS

    • JavaScript

      • 进程、线程、协程
      • call、apply、bind
        • bind
          • 实现bind
          • 实现bind总结
      • ES6

    • HTML

  • 浏览器基础

  • 软件开发

  • 数据结构

  • 性能优化

  • Node.js

  • 项目实战

  • 收录

  • 搞事啦

  • 前端知识体系
  • 前端基础
  • JavaScript
GuoLiBin6
2024-01-03
目录

call、apply、bind

# bind

先看使用方法

const obj1 = {
	name: 'lili'
}
const obj2 = {
	name: 'xiaoming'
}
window.name = 'window'

function who (text) {
	console.log(`我是${this.name}, ${text}`)
}

who('我是工具') // 我是window,我是工具

const fn1 = who.bind(obj1)
fn1('我是女孩') // 我是lili,我是女孩

const fn2 = who.bind(obj2, '我是男孩')
fn2() // 我是xiaoming,我是男孩
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

由上看出几点:

  • bind()函数执行,不会执行原函数,而是返回一个新函数,新函数执行时才会执行原函数
  • bind()可以改变this的指向,第一次执行时,who为普通函数,this指向window,后两次输出分别指向obj1和obj2
  • bind()接受多个参数,第一个参数为原函数执行过程中this的指向,从第二个参数开始,参数是可选的,可以在bind时传入,也可以在返回的新函数执行时传入

原函数参数及顺序,是bind时第二个参数开始的参数+新函数执行时传入的参数顺序,多传入的参数不生效。利用这个特性,==可以使一个函数拥有预设的初始参数==,看代码:

function list () {
	return Array.prototype.slice.call(arguments)
}

const list1 = list(1, 2, 3) // [1, 2, 3]

const paramToList = list.bind(null, 5)

const lsit2 = paramToList(1, 2) // [5, 1, 2]
const list3 = paramToList() // [5]
1
2
3
4
5
6
7
8
9
10

这样,在bind时传入的参数5,就会作为paramToList函数的预设参数,在调用时总是出现在第一个参数位置。

# 实现bind

既然已经知道了 bind() 函数的作用,我们就可以通过它的作用,慢慢还原它的实现经过

首先,biind() 调用是 fun.bind(obj),直接由一个函数发起调用,且函数上没有这个方法,证明 bind() 存在于函数的原型链上,如下

Function.prototype.bind = function (obj) {
	//
}
1
2
3

其次,bind() 函数执行不会执行原函数,会返回一个新函数,新函数执行时才会执行原函数,即,bind中返回的函数执行时,才会打印“执行”,如下

Function.prototype.bind = function (obj) {
	// 保存原函数 
    // bind作为对象的方法调用,this指向调用方法的对象,即执行 func.bind(obj) 时,this 指向 func
	const self = this
	// 返回新函数
	return function () {
		// 需要在这里执行原函数
		self()
	}
}
1
2
3
4
5
6
7
8
9
10

实际使用一下看看,如下

Function.prototype.bind = function (obj) {
	// 保存原函数 
    // bind作为对象的方法调用,this指向调用方法的对象,即执行 func.bind(obj) 时,this 指向 func
	const self = this
	// 返回新函数
	return function () {
		// 需要在这里执行原函数
		self()
	}
}

const person = {
	legs: 2
}

function showLegs () {
	console.log('执行')
	console.log('legs', this.legs)
}

const func = showLegs.bind(person)
func() // 执行 // legs undefined
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

然后,bind() 返回了一个函数,函数再执行,执行的是原函数,但是原函数中的this并没有被绑定到person,要想将 this 绑定到 person,要在新函数,即 func 执行时做改变 this 指向的操作,this 由指向 func 改为指向 person,如下

Function.prototype.bind = function (obj) {
	// 保存原函数 
	// bind作为对象的方法调用,this指向调用方法的对象,即执行 func.bind(obj) 时,this 指向 func
	const self = this
	// 返回新函数
	return function () {
		// 需要在这里执行原函数,并且改变this指向
		self.apply(obj)
	}
}

const person = {
	legs: 2
}

function showLegs () {
	console.log('legs', this.legs)
}

const func = showLegs.bind(person)
func() 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

到这里,bind() 就成功实现了,但是我们还 bind() 可以接受多个参数,从第二个开始作为参数传入,而且返回的新函数中也可以传入参数,所以我们还需要将实现优化,支持传入参数,如下

// 最终版
Function.prototype.bind = function () {
	// 保存原函数 
	// bind作为对象的方法调用,this指向调用方法的对象,即执行 func.bind(obj) 时,this 指向 func
	const self = this
	// 拆解参数
	const obj = [].shift.call(arguments)
	const args = [].slice.call(arguments)
	// 返回新函数
	return function () {
		// 拆解参数
		const allArgs = [].concat.call(args, [].slice.call(arguments))
		// 需要在这里执行原函数,并且改变this指向
		self.apply(obj, allArgs)
	}
}

const person = {
	legs: 2
}

function showLegs (name, endText) {
	console.log('legs', this.legs)
	console.log(`${name} has ${this.legs} legs, ${endText}`)
}

const func = showLegs.bind(person, 'lili')
func(' right') // lili has 2 legs, right
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

# 实现bind总结

上述过程中,我们由bind的作用推导出bind的实现,根据以上代码查看下面的一些要点,帮助我们更快的实现bind

  • bind作用:返回一个新函数func,新函数func执行时执行被改变了this指向的原函数showLegs
  • bind实现:返回新函数,在新函数中改变原函数指向
#JavaScript
上次更新: 2024-01-12 19:09:34
进程、线程、协程
let const

← 进程、线程、协程 let const→

最近更新
01
Border为none和0的区别
09-13
02
BFC原理
09-11
03
一键换肤
09-10
更多文章>
Theme by Vdoing | Copyright © 2022-2025 GuoLiBin6
冀ICP备2022013865号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式