到底为什么写func.apply(this, arguments)

2,349 阅读5分钟

一些不重要的废话

接上回日记,在我坚持不懈的努力创作下,终于将我的创作者中心中的部分数据变为了正数。

img01.png

喜大普奔

7.gif

一些重要的废话

不过上次好像有点紧张了,不好意思,忘记了自我介绍。

9.gif

大家好,我是IT大军前端军营的摸鲸校尉

正文开始

之前逛掘金的时候看到了大帅老猿大佬的一篇文章。放上部分原文代码:

function throttle(func, delay) {
    var last = 0;
    return function () {
        var now = Date.now();
        if (now >= delay + last) {
            func.apply(this, arguments);
            last = now;
        } else {
            console.log("距离上次调用的时间差不满足要求哦");
        }
    }
}
function resize(e) {
    console.log("窗口大小改变了");
}
window.addEventListener('resize', throttle(resize, 300));

有一代码是这样的 func.apply(this, arguments);。说实话,没有怎么看懂。于是我在我的高达1024kb储存量的大脑搜寻一下相关知识记忆。apply好像是可以修改this的指向,arguments好像是一个类数组的东西......呵呵呵呵呵,好像帮助并不大。

11.png

既然如此,那我们一步步来。

关于this

我依稀记得有个大佬给我说过:

this只有在运行时才会明确其指向的对象

话不多说,try one try (试一试)。

var name = 'a', age = 0, type = 'A'
function fn () {
    console.log( this.name,'年龄是 ', this.age, '血型是 ', this.type );
}
fn() //  expected output: a 年龄是  0 血型是  A

这里是在全局作用域下调用的fn,所以this就会指向全局作用域对象window。

var name = 'a', age = 0, type = 'A'
function fn () {
    console.log( this.name,'年龄是 ', this.age, '血型是 ', this.type );
}
fn()
//  expected output: a 年龄是  0 血型是  A
var x = {
    name: 'XMAN',
    age: 24,
    type: this.type,
    fn: fn
}
var y = {
    name: 'YMAN',
    age: 25,
    type: 'B',
    fn: fn
}
  
x.fn() //  expected output: XMAN 年龄是  24 血型是  A
y.fn() //  expected output: YMAN 年龄是  25 血型是  B

这里其实都是调用的同一个方法,之所以console出来的东西不一样,就是因为this所指向的对象不一样。执行x.fn()时,调用环境是在x中,所以this指向对象x;同样的执行y.fn()时,this指向对象y。 当然this还有很多其他情况,大家可以自己研究。其实是我怕整多了讲不好会误导祖国的花朵

8.gif

关于apply、bind、call

先看MDN的描述

apply() 方法调用一个具有给定this值的函数,以及以一个数组(或类数组对象)的形式提供的参数。

bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。

call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。

这仨乍一看好像差不多,好像都能修改this的指向对象,但是区别肯定是有的。 try one try (试一试):

  var name = 'a', age = 0, type = 'A'
  function fn () {
    console.log( this.name,'年龄是 ', this.age, '血型是 ', this.type );
  }
  var y = {
    name: 'YMAN',
    age: 25,
    type: 'B',
    fn: fn
  }
  fn() // expected output: a 年龄是  0 血型是  A
  fn.call(y) // expected output:  YMAN 年龄是  25 血型是  B
  fn.apply(y) // expected output:  YMAN 年龄是  25 血型是  B
  fn.bind(y)() // expected output:  YMAN 年龄是  25 血型是  B

可以看到bind方法多了一个( ),这个是因为bind方法会创建一个新的函数,所以要调用一下。

try again:

  var name = 'a', age = 0, type = 'A'
  var x = {
    name: 'XMAN',
    age: 24,
    type: this.type
  }
  function todo (para1, para2) {
    console.log(this.name,'年龄是:', this.age, '血型是:', this.type, '爱好:', para1, para2)
  }
  todo('篮球', '足球')            //  a 年龄是: 0 血型是: A 爱好: 篮球 足球
  todo.call(x, '篮球', '足球')    //  XMAN 年龄是: 24 血型是: A 爱好: 篮球 足球
  todo.call(x, ['篮球', '足球'])  //  XMAN 年龄是: 24 血型是: A 爱好: ["篮球", "足球"] undefined
  todo.apply(x, '篮球', '足球')   //  报错,apply不能这样的,其第二个参数得是一个数组或者类数组对象
  todo.apply(x, ['篮球', '足球']) //  XMAN 年龄是: 24 血型是: A 爱好: 篮球 足球
  todo.bind(x, '篮球', '足球')()  //  XMAN 年龄是: 24 血型是: A 爱好: 篮球 足球

call( )方法的作用和 apply( ) 方法类似,区别就是call( )方法接受的是参数列表,而apply( )方法接受的是一个参数数组。

关于arguments

同样先看MDN怎么说:

arguments 是一个对应于传递给函数的参数的类数组对象。

还是举个栗子吧:

  function testArguments() {
    console.log(arguments);
    // expected output: Arguments(3) ["a", "b", "c", callee: ƒ, Symbol(Symbol.iterator): ƒ]
    
    console.log('arguments.length :>> ', arguments.length);
    // expected output: arguments.length :>>  3
    
    console.log(arguments[0])
    // expected output: a
    
    arguments.push('d')
    // expected output: arguments.push is not a function
  }
  testArguments('a','b','c')

从上面可以看出,arguments就是传给函数的参数组成的类数组,并且有length,也可以通过索引去获取对应的数据,但是他不拥有像push这样的方法。其实是可以想到的,毕竟是别人Array的,怎么能给arguments玩呢。

总结

通过我们的不懈努力,终于找到了func.apply(this, arguments);这行代码的相关知识。所以,答案也呼之欲出了......

12.jpg

ちょっと待ってください (等一下),好像还出不来,容我再想想......

6.jpg

还是说说我自己的一些看法吧:他通过apply修改了this的指向,这里的this应该是指向window;当然我觉得更重要的是arguments,由于func是传入的,也就是说不是固定的函数,那么其参数也可能不是一定的,而arguments可以将个数不确定的参数传给func。这样一来,不过是什么函数,需要什么参数,就不需要关心了。なるほど,哦~,丞相,我悟了,但还没有完全悟......

最后

第二次写文章,要是有啥没写正确的请大佬们指正。 当然我也不一定会去改。