举荐浏览地址
掘金 欢送 Start
思维导图
大家好,我是林一一。上面的这一篇是对于 JS 中 call,apply,bind 原理和模仿实现和场景的面试题文章,一起开始浏览吧。🧐
call,apply,bind 都能够扭转 this 的指向
对于 this 指向问题能够看看这篇 面试 | 你不得不懂的 JS this 指向
一、call 格局 [function].call([this], [param]…),一句话概括:call()
将函数的 this
指定到 call()
的第一个参数值和残余参数指定的状况下调用某个函数或办法。
原理:
[function]
.call([this]),执行call()
会将函数[function]
中的this
绑定到第一个参数。而函数call()
中的this
是来源于[function]
的,[function]
是在call()
函数外部执行的,是call()
通过操控this
来执行函数[function]
,同时给[function]
传递残余的参数。
思考
1. 热身题 1
function fn(a, b) {console.log(this, a, b)
}
var obj = {name: '林一一'}
fn.call(obj, 20, 23) // {name: "林一一"} 20 23
fn.call(20, 23) // Number {20} 23 undefined
fn.call() //Window {0: global, window: …} undefined undefined | 严格模式下为 undefined
fn.call(null) //Window {0: global, window: …} undefined undefined | 严格模式下为 null
fn.call(undefined) //Window {0: global, window: …} undefined undefined | 严格模式下为 undefined
fn
调用了call
,fn
的this
指向obj
,最初fn
被执行;this
指向的值都是援用类型,在非严格模式下,不传参数或传递null/undefined
,this
都指向window
。传递的是原始值,原始值会被包装。严格模式下,call
的一个参数是谁就指向谁
2. 热身题 2
var obj1 = {
a: 10,
fn: function(x) {console.log(this.a + x)
}
}
var obj2 = {
a : 20,
fn: function(x) {console.log(this.a - x)
}
}
obj1.fn.call(obj2, 20) //40
略微变量一下,原理不变
obj1.fn
中fn
的this
指向到obj2
,最初还是执行obj1.fn
中的函数。
二、apply 和 call 基本一致
两者惟一不同的是:apply 的除了一个 this 指向的参数外,第二个参数是数组[arg1, arg2...],call 的第二参数是列表(arg1, arg2...)
var name = '二二'
var obj = {
name: '林一一',
fn: function() {return `${this.name + [...arguments]}`
}
}
obj.fn.apply(window, [12, 23, 34, 56]) // "二二 12,23,34,56"
apply 第二个参数接管的是数组
面试题
1. 模仿实现内置的 call(),apply()办法。
- call 的模仿实现
模仿实现
call
须要明确call
的原理,1.this
的指向扭转,call
函数中执行调用的函数。
上面代码参考来自讶羽
大佬的
Function.prototype.myCall = function (context, ...args){
context = context || window
// 这里的 this 是指向 fn 的,通过 this 就能够获取 fn,context 是咱们的 obj,能够间接给 obj 增加一个函数属性
context.fn = this
delete context.fn(...args)
return
}
var name = '二二'
var obj = {name: '林一一',}
function fn() {console.log(this.name, ...arguments)
}
fn.myCall(null)
fn.myCall(obj, 12, 23, 45, 567)
下面的模仿
call
其实并没有思考根本类型的状况,原生的 call 函数也能够解决根本类型比方下面的热身 1fn.call(20, 23)
输入并不会报错。然而这里的myCall
会间接报错,提供一个更加全面模仿call
有趣味的能够看看 彻底搞懂闭包,柯里化,手写代码,金九银十不再丢分!
- apply 的模仿实现
Function.prototype.myApply = function (context, args){
context = context || window
context.fn = this
delete context.fn(args)
return
}
相似下面的模仿
call
写法
2. call 和 apply 区别
call
办法的语法和作用与apply
办法相似,只有一个区别,就是 call() 办法承受的是一个参数列表,而 apply() 办法承受的是一个蕴含多个参数的数组。
var name = '二二'
var obj = {name: '林一一'}
function fn(){console.log(this.name, ...arguments)
}
fn.apply(obj, [12, 34, 45, 56]) //fn(12, 23, 45, 56) 林一一 12 34 45 56
要留神的是,残余的数组参数会以单个参数的模式传递给函数,
fn.apply(obj, [12, 34, 45, 56]) ==> fn(12, 23, 45, 56)
。
三、bind
援用 MDN 的话:
bind()
办法会创立一个新函数。当这个新函数被调用时,bind()
的第一个参数将作为它运行时的this
,之后的一序列参数将会在传递的实参前传入作为它的参数。
- 返回一个新函数,这个新函数执行时的
this
才指定到bind
的第一个参数 bind
的残余参数,传递给新的函数- 返回后的新函数是自我调用的
小思考
1. 下面说的这个新函数是啥?
其实这个新函数就是调用
bind
的函数,bind
调用后会将调用bind
的函数拷贝一份返回。
一个小栗子
var name = '二二'
var obj = {name: '林一一'}
function fn(){return `${this.name} ` + [...arguments]
}
let f = fn.bind(obj, 12, 23, 45, 67, 90)
f() // "林一一 12,23,45,67,90"
下面的新函数就是
f()
,f()
就是bind
拷贝函数fn
后返回的。
2. bind 是怎么实现拷贝 fn 的?
简略地说:通过
this
的获取,再return
回这个this
获取的函数,参考call
。
面试题
1. bind() 和 call()、apply() 的区别
通过
apply
和call
扭转函数的this
指向,他们两个函数的第一个参数都是一样的示意要
扭转指向的那个对象,第二个参数,apply
是数组,而call
则是arg1,arg2...
这种模式。通
过bind
扭转this
作用域会返回一个新的函数,这个函数不会马上执行
2. 模仿实现内置的 bind() 办法。
上面的代码来自 JavaScript 深刻之 bind 的模仿实现
Function.prototype.bind2 = function (context) {if (typeof this !== "function") {throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
}
var self = this;
var args = Array.prototype.slice.call(arguments, 1);
var fNOP = function () {};
var fBound = function () {var bindArgs = Array.prototype.slice.call(arguments);
return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs));
}
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
}
模仿实现 api,最重要的是思维过程,这是 copy 不了的。
思考题
1. 求数组中的最大值和最小值
-
应用 Math 的 max/min 求最大最小值
let arr = [12, 45, 65, 3, 23, 11, 76, 8, 9, 56, 70] let max = Math.max(...arr) // 76 let min = Math.min(...arr) // 3
-
应用数组 sort 办法求最大最小值
let arr = [12, 45, 65, 3, 23, 11, 76, 8, 9, 56, 70] let list = arr.sort(function(a, b) {return b - a}) let max = list[0] // 76 let min = list[list.length - 1] // 3
-
应用 apply 求数组最大值最小值
let arr = [12, 45, 65, 3, 23, 11, 76, 8, 9, 56, 70] let max = Math.max.apply(null, arr) // 76 let min = Math.max.apply(null, arr) // 3
2. 如何判断一个数组
Object.prototype.toString.call()
,instanceof
。特地要留神 typeof 不能够判断数组类型
let arr = []
Object.prototype.toString.call(arr)
3.Object.prototype.toString.call() 为什么能够用来判断类型
因为
Object.prototype.toString() 办法会返回对象的类型字符串
,输入"[object Object]"
其中第二个Object
是传入参数的构造函数。所以应用call
就能够指定任意的值和联合toString
将组成的构造函数类型返回来判断类型。同样情理换成apply/bind
同样也能够判断
Object.prototype.toString.call('str') // "[object String]"
Object.prototype.toString.call(123) // "[object Number]"
Object.prototype.toString.call({}) // "[object Object]"
Object.prototype.toString.call([]) // "[object Array]"
Object.prototype.toString.apply({}) // "[object Object]"
Object.prototype.toString.apply([]) // "[object Array]"
var f = Object.prototype.toString.bind({})
f() // "[object Object]"
var fn = Object.prototype.toString.bind([])
fn() // "[object Array]"
4. 应用 call() 实现将类数组转化成数组
应用
call(),[].slice / Array.prototype.slice()
let array = [12, 23, 45, 65, 32]
function fn(array){var args = [].slice.call(arguments)
return args[0]
}
fn(array) // [12, 23, 45, 65, 32]
下面利用
call
扭转了slice
的this
指向arguments
来遍历输入。
参考
JavaScript 深刻之 call 和 apply 的模仿实现
JavaScript 深刻之 bind 的模仿实现
MDN bind
完结
感激浏览到这里,如果这篇文章能对你有一点启发或帮忙,欢送 star, 我是林一一,下次见。