本文主要详细记录
Function实例的call()/apply()/bind()方法的使用及自定义实现详细步骤
- 函数的
call方法-文档链接
// 以指定的this调用函数,并通过 从第二个参数开始依次传递参数
function func(food,drink){
console.log(this)
console.log(food)
console.log(drink)
}
const obj = {
name:'测试'
}
func.call(obj,'面包','可乐')
// obj
// 面包
// 可乐
- 函数的
apply方法-文档链接
// 以指定的this调用函数,并通过 数组的形式 传递参数
function func(food,drink){
console.log(this)
console.log(food)
console.log(drink)
}
const obj = {
name:'测试'
}
func.apply(obj,['面包','可乐'])
// obj
// 面包
// 可乐
- 函数的
bind方法-文档链接
function func(food, drink) {
console.log(this)
console.log(food)
console.log(drink)
}
const obj = {
name: '测试'
}
const bindFunc = func.bind(obj, '面包')
bindFunc('可乐')
// obj
// 面包
// 可乐
如何确认this的值:
在非严格模式下,总是指向一个对象,在严格模式下可以是任意值,开启严格模式可以使用如下两种方式:
- 在整个脚本顶部开启
- 在函数顶部开启
// 1.为整个脚本开启严格模式
'use strict'
function func() {
// 2.为函数开启严格模式
'use strict'
}
然后就可以根据不同的模式来确认this指向啦,
- 全局执行环境中,指向全局对象(非严格模式、严格模式)
- 函数内部,取决于函数被调用的方式
- 直接调用的this值:
- 非严格模式:全局对象(window)
- 严格模式:undefined
- 对象方法调用的this值:
- 严格模式 和 非严格模式 都指向调用者
- 直接调用的this值:
1.全局执行环境
非严格模式: 不做任何设置,直接写就是非严格模式
console.log(this) // window
严格模式: 代码顶部加上 'use strict' 即可
'use strict' // 为整个脚本开启严格模式
console.log(this) // window
2.函数内部
2.1 直接调用-非严格模式
function func() {
console.log(this) // 全局对象window
}
func()
2.1 直接调用-严格模式
function func() {
'use strict'
console.log(this) // undefined
}
func()
2.2 对象方法调用
const food = {
name: '猪脚饭',
eat() {
console.log('吧唧吧唧')
console.log(this)
}
}
// 对象方法调用 严格模式 和 非严格模式 都指向调用者 food
food.eat() // this 指向调用者 food
总结
如何确认this指向:
- 全局执行环境中,指向全局对象(非严格模式、严格模式)
- 如何开启严格模式:
// 为整个脚本开启严格模式
'use strict'
function func() {
// 为函数开启严格模式
'use strict'
}
- 函数内部,取决于函数被调用的方式
- 直接调用的this值: 1. 非严格模式:全局对象(window) 2. 严格模式:undefined
- 对象方法调用时的this值为调用者
如何改变this指向
主要有2类改变函数内部this指向的方法:
- 调用函数并传入具体的
**this**:call:- 参数1:
this - 参数2-n:传递给函数的参数
- 参数1:
apply-数组作为参数- 参数1:
this - 参数2:以数组的形式,传递给函数的参数
- 参数1:
- 创建时绑定
**this**的函数:- bind:返回一个绑定了
this的新函数 - 箭头函数:最近的this是谁,就是谁
- bind:返回一个绑定了
调用函数并传入具体的this:
function funcA(p1, p2) {
console.log('funcA-调用')
console.log(this)
console.log('p1:', p1)
console.log('p2:', p2)
}
const obj = {
name: 'test'
}
// call参数
// 参数1 this值
// 参数2-参数n 挨个传入函数的参数
funcA.call(obj, 1, 2) // obj 1,2
// apply参数
// 参数1 this值
// 参数2 以数组的形式传入函数的参数
funcA.apply(obj, [3, 4]) // obj 3,4
创建绑定this的函数:
function funcB(p1, p2) {
console.log('funcB-调用')
console.log(this)
console.log('p1:', p1)
console.log('p2:', p2)
}
const person = {
name: 'test'
}
// bind参数
// 参数1 this值
// 参数2-参数n 绑定的参数
const bindFuncB = funcB.bind(person, 123)
bindFuncB(666) // person 123 666
const student = {
name: 'xuesheng',
sayHi: function () {
console.log(this)
// 箭头会从自己作用域链的上一层继承this
const inner = () => {
console.log('inner-调用了')
console.log(this)
}
inner()
}
}
student.sayHi()
总结
如何改变this指向,有2类改变this指向的方法,分别是:
- 调用函数时并传入具体的
thiscall:从第二个参数开始挨个传递参数apply:在第二个参数以数组的形式传递参数
- 创建函数时绑定
this?bind:返回一个绑定了this以及参数(可选)的新函数- 箭头函数:创建时会绑定上一级作用域中的
this
自定义call方法
实现myCall方法,实际用法和call方法一致,核心步骤有4步
const person = {
name: 'Test'
}
function func(numA, numB) {
console.log(this)
console.log(numA, numB)
return numA + numB
}
// 参数1:指定的this值
// 参数2-参数n:原函数参数
const res = func.myCall(person, 2, 8)
console.log('返回值为:', res)
- 如何定义
myCall?- 要保证所有的函数都能调用这个方法,所以绑定在原型 prototype 上
- 如何让函数内部的
this为某个对象?- 设置this
- 调用原函数,原函数中的this,就是传入的值
- 如何让
myCall接收参数2-参数n? - 使用Symbol调优
myCall?
// 1. 如何定义`myCall`,
Function.prototype.myCall = function () {
// 逻辑略
}
// 2 设置this并调用原函数
Function.prototype.myCall = function (thisArg) {
// 判断调用对象是否为函数
if(typeof this !== 'function') {
throw new TypeError('Error')
}
// 判断thisArg 是否存在,不存在传入window
thisArg = thisArg || window
//1. this 是调用myCall的 函数 (原函数.mycall)
//2. func.myCall 在mycall 里this 就是func,这里是赋值的过程
thisArg.fn= this
// 通过对象.的方式调用原函数,thisArg.fn(),原函数里的this,就是指向thisArg,在这里就是指向person对象
const res = thisArg.fn()
// 绑定成功后,移除添加的自定义属性
delete thisArg.fn
}
// 3 接收剩余参数并返回结果
Function.prototype.myCall = function (thisArg, ...args) {
thisArg.fn = this
// 调用并获取结果
const res = thisArg.fn(...args)
// 移除添加的自定义属性
delete thisArg.fn
// 返回调用结果
return res
}
// 4 使用`Symbol`调优`myCall`
Function.prototype.myCall = function (thisArg, ...args) {
// 判断调用对象是否为函数
if(typeof this !== 'function') {
throw new TypeError('Error')
}
// 判断thisArg 是否存在,不存在传入window
thisArg = thisArg || window
// 使用Symbol生成唯一标记,避免和原属性冲突
// 此时key为动态的属性名,不能使用.key 来写
const key = Symbol()
thisArg[key] = this
const res = thisArg[key](...args)
// 移除添加的自定义属性
delete thisArg[key]
// 返回调用结果
return res
}
// --------测试代码--------
const person = {
name: 'test'
}
function func(numA, numB) {
console.log(this)
console.log(numA, numB)
return numA + numB
}
// 参数1:指定的this值
// 参数2-参数n:原函数参数
const res = func.myCall(person, 2, 8)
console.log('返回值为:', res)
总结:
自定义call方法的步骤为
- 在
function的原型上添加myCall方法,保证所有函数都可以调用 - 方法内部,通过动态为对象添加方法的形式来指定
this指向 - 调用完毕之后通过
delete关键字删除上一步动态增加的方法 - 方法的名字通过Symbol进行设置,避免和默认名重复
- 使用剩余参数的形式传递参数2-参数n(函数参数)
完整版
Function.prototype.myCall = function (thisArg, ...args) {
// 判断调用对象是否为函数
if(typeof this !== 'function') {
throw new TypeError('Error')
}
// 判断thisArg 是否存在,不存在传入window
thisArg = thisArg || window
const fn = Symbol()
thisArg[fn] = this
const res = thisArg[fn](...args)
delete thisArg[fn]
return res
}
自定义apply方法
实现myApply方法,实际用法和apply方法一致,核心步骤依旧4步
const person = {
name: 'test’
}
function func(numA, numB) {
console.log(this)
console.log(numA, numB)
return numA + numB
}
const res = func.myApply(person, [2, 8])
console.log('返回值为:', res)
- 如何定义
myApply?- 定义在原型上
- 如何让函数内部的
this为某个对象?- 动态给对象添加方法,通过
对象.方法()调用即可 - 使用
Symbol来生成方法名
- 动态给对象添加方法,通过
- 如何让
myApply接收参数?- 定义参数2即可
- 传递给原函数时需要使用
...展开
// 1. 如何定义`myApply`
Function.prototype.myApply = function () {
// 逻辑略
}
// 2 如何让函数内部的`this`为某个对象
Function.prototype.myApply = function (thisArg) {
// 判断调用对象是否为函数
if(typeof this !== 'function') {
throw new TypeError('Error')
}
// 判断thisArg 是否存在,不存在传入window
thisArg = thisArg || window
// 为他添加一个自定义属性,让函数成为他的该属性
// 使用Symbol生成唯一标记,避免和原属性冲突
const fn = Symbol()
thisArg[fn] = this
const res = thisArg[fn](...args)
// 移除添加的自定义属性
delete thisArg[fn]
// 返回调用结果
return res
}
// 3 如何让`myApply`接收参数
Function.prototype.myApply = function (thisArg, args) {
// 判断调用对象是否为函数
if(typeof this !== 'function') {
throw new TypeError('Error')
}
// 判断thisArg 是否存在,不存在传入window
thisArg = thisArg || window
const fn = Symbol()
thisArg[fn] = this
// 调用并获取结果
// 用... 将args展开传入
const res = thisArg[fn](...args)
delete thisArg['fn']
// 返回调用结果
return res
}
// 测试代码
const person = {
name: 'test’
}
function func(numA, numB) {
console.log(this)
console.log(numA, numB)
return numA + numB
}
const res = func.myApply(person, [2, 8])
console.log('返回值为:', res)
总结
自定义apply方法
- 在
function的原型上添加myApply方法,保证所有函数都可以调用 - 方法内部,通过动态为对象添加方法的形式来指定
this指向 - 调用完毕之后通过
delete关键字删除上一步动态增加的方法 - 方法的名字通过Symbol进行设置,避免和默认名重复
- 直接使用数组传递函数的参数,内部调用时结合
...运算符展开数组
完整版
Function.prototype.myApply = function (thisArg, args) {
// 判断调用对象是否为函数
if(typeof this !== 'function') {
throw new TypeError('Error')
}
// 判断thisArg 是否存在,不存在传入window
thisArg = thisArg || window
const fn = Symbol()
thisArg[fn] = this
const res = thisArg[fn](...args)
delete thisArg[fn]
return res
}
自定义bind方法
实现myBind方法,实际用法和bind方法一致,核心步骤为2步
const person = {
name: 'test'
}
function func(numA, numB, numC, numD) {
console.log(this)
console.log(numA, numB, numC, numD)
return numA + numB + numC + numD
}
const bindFunc = func.myBind(person, 1, 2)
const res = bindFunc(3, 4)
console.log('返回值:', res)
- 如何返回一个绑定了
this的函数? - 如何实现绑定的参数,及传入的参数合并?
// 1 如何返回一个绑定了`this`的函数
Function.prototype.myBind = function (thisArg) {
// 判断调用对象是否为函数
if(typeof this !== 'function') {
throw new TypeError('Error')
}
// myBind函数调用时,this就是函数本身
return () => {
// 通过call方法将传入的 thisArg 作为this进行调用
this.call(thisArg)
}
}
// 2 如何实现绑定的参数,及传入的参数合并
// ...args 接收绑定参数
Function.prototype.myBind = function (thisArg, ...args) {
// 判断调用对象是否为函数
if(typeof this !== 'function') {
throw new TypeError('Error')
}
// ...args2 接收调用时的参数
return (...args2) => {
// thisArg 需要指定的this
// args 调用myBind时传入的参数
// args2 调用新函数时传入的参数
return this.call(thisArg, ...args, ...args2)
}
}
// 测试代码
const person = {
name: 'test'
}
function func(numA, numB, numC, numD) {
console.log(this)
console.log(numA, numB, numC, numD)
return numA + numB + numC + numD
}
const bindFunc = func.myBind(person, 1, 2)
const res = bindFunc(3, 4)
console.log('返回值:', res)
总结
自定义bind方法
function原型上添加myBind函数,参数1为绑定的this,参数2-参数2为绑定的参数- 内部返回一个新箭头函数,目的是绑定作用域中的this
- 返回的函数内部,通过
call进行this和参数绑定(这里使用apply方法也是一样的,注意参数是数组) - 通过
call的参数2和参数3指定绑定的参数,和调用时传递的参数
完整版
Function.prototype.myBind = function (thisArg, ...args) {
// 判断调用对象是否为函数
if(typeof this !== 'function') {
throw new TypeError('Error')
}
return (...args2) => {
return this.call(thisArg, ...args, ...args2)
}
}