举荐浏览地址

掘金 欢送 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 23fn.call(20, 23) // Number {20} 23 undefinedfn.call()   //Window {0: global, window: …} undefined undefined     | 严格模式下为 undefinedfn.call(null)   //Window {0: global, window: …} undefined undefined       | 严格模式下为 nullfn.call(undefined)  //Window {0: global, window: …} undefined undefined     | 严格模式下为 undefined
fn调用了callfnthis 指向 obj,最初 fn 被执行;this 指向的值都是援用类型,在非严格模式下,不传参数或传递 null/undefinedthis 都指向 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.fnfnthis 指向到 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 函数也能够解决根本类型比方下面的热身1 fn.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() 的区别

通过 applycall 扭转函数的 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)  // 76let 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]   // 76let 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) // 76let 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 扭转了 slicethis 指向 arguments 来遍历输入。

参考

JavaScript深刻之call和apply的模仿实现

JavaScript深刻之bind的模仿实现

MDN bind

完结

感激浏览到这里,如果这篇文章能对你有一点启发或帮忙,欢送 star, 我是林一一,下次见。