时间:2021-05-18
前言
在函数调用时,arguments和this会被静默的传递给函数,并可以在函数体内引用它们,借以访问函数相关的一些信息。
其中arguments是一个类数组结构,它保存了调用时传递给函数的所有实参;this是函数执行时的上下文对象, 这个对象有些让人感到困惑的行为。 下面分别对他们进行讨论。
1. arguments
1.1 背景
JavaScript 允许函数在调用时传入的实参个数和函数定义时的形参个数不一致, 比如函数在定义时声明了 n 个参数, 在调用函数时不一定非要传入 n 个参数,例如:
1.2 arguments 与 形参的对应关系
arguments是个类数组结构,它存储了函数在调用时传入的所有实参, 通过访问它的length属性可以得到其中保存的实参的个数,并可以通过arguments[n]按顺序取出传入的每个参数(n=1,2,..,arguments.length-1)。
参数在arguments中保存的顺序和传入的顺序相同, 同时也和形参声明的顺序相同,例如:
当传入的实参多于形参个数时,想要获得多余出的实参,就可以用arguments[n]来获取了, 例如:
1.3 arguments 与 形参的值相互对应
在非严格模式下, 修改arguments中的元素值会修改对应的形参值;同样的,修改形参的值也会修改对应的arguments中保存的值。下面的实验可以说明:
但是,在严格模式下不存在这种情况, 严格模式下的arguments和形参的值之间失去了对应的关系:
注意: arguments 的行为和属性虽然很像数组, 但它并不是数组,只是一种类数组结构:
function fn(){console.log(typeof arguments); // objectconsole.log(arguments instanceof Array); // false}fn();1.4 为什么要了解 arguments
在ES6中, 可以用灵活性更强的解构的方式(...符号)获得函数调用时传入的实参,而且通过这种方式获得的实参是保存在真正的数组中的,例如:
那么在有了上面这种更加灵活的方式以后,为什么还要了解arguments呢? 原因是在维护老代码的时候可能不得不用到它。
2. 函数上下文: this
在函数调用时, 函数体内也可以访问到 this 参数, 它代表了和函数调用相关联的对象,被称为函数上下文。
this的指向受到函数调用方式的影响, 而函数的调用方式可以分成以下4种:
下面分别讨论以上 4 种调用方式下 this 的指向.
2.1 直接调用一个函数时 this 的指向
有些资料说在直接调用一个函数时, 这个函数的 this 指向 window, 这种说法是片面的, 只有在非严格模式下而且是浏览器环境下才成立, 更准确的说法是:在非严格模式下, this值会指向全局上下文(例如在浏览器中是window, Node.js环境下是global)。而在严格模式下, this 的值是 undefined。实验代码如下:
严格模式下:
总结: 在直接调用一个函数时, 它的 this 指向分成两种情况: 在非严格模式下指向全局上下文, 在严格模式下指向 undefined.
2.2 被一个对象当做方法调用
当函数被一个对象当成方法调用时, 这个函数的 this 会指向调用它的对象。代码验证如下:
因为这个原因, 对象的属性可以通过this来访问, 如果给 xm 加上一个 name 属性, 则通过 xm.name可以得到这个属性值, 也可以在函数中通过 this.name 得到属性值, 即 this.name 就是 vm.name, 进一步, this===xm。 实验如下:
2.3 被作为构造函数来调用时
2.3.1 不要像使用普通函数一样使用构造函数
构造函数本质上是函数, 只是在被 new 操作符调用时一个函数才被称为构造函数。然而话虽如此, 但是由于写出一个构造函数的目的是用他来创建一个对象, 所以还要有一些约定俗成的东西来限制这个概念, 避免把构造函数当成普通函数来使用。例如, 构造函数虽然能被直接调用, 但是不要这样做,因为这是一个普通函数就可以做到的事情,例如:
2.3.2 使用构造函数创建对象时发生了什么
当使用 new 关键字来调用构造函数的最终结果是产生了一个新对象, 而产生新对象的过程如下:
上面的内容如果需要完全理解, 还需要了解原型相关的内容。这里只需要关注第3、4步就可以了,即:将this绑定到生成到的新对象上,并将这个新对象返回, 进一步下结论为:使用构造函数时, this 指向生成的对象, 实验结果如下:
2.3.3 结论
从上面的内容可以得到如下的结论: 当函数作为构造函数使用时, this 指向返回的新对象
2.4 通过 call() 或者 apply() 调用时
使用函数 call 和 apply 可以在调用一个函数时指定这个函数的 this 的指向, 语法是:
例如定义一个对象如下:
上面定义了一个对象, 对象的 name 属性为'小明'; sayName 属性是个函数, 功能是输出对象的 name 属性的值。根据2.2部分可知 sayName 这个函数的 this 指向 xm 对象, this.name 就是 xm.name。下面定义一个新对象, 并把 xm.sayName 这个函数的 this 指向新定义的对象。
新定义一个对象 xh:
对象 xh 只有 name 属性, 没有 sayName 属性, 如果想让 xh 也使用 sayName 函数来输出自己的名字, 那么就要在调用 sayName 时让它的 this 指向小红, 以达到 this.name 等于 xh.name 的目的。 这个目的就可以通过 call 和 apply 两个函数来实现。 以call 函数为例来实现这个需求, 只需要这样写就可以了:
其中fn为xm.sayName; targetThis为xh, 这是因为targetThis的指向就是xh, 此结论可以由 2.2部分 的内容得到。
2.4.1 call 和 apply 的区别
call 和 apply 的区别仅仅是要传给fn的参数的形式不同:对于apply,传给fn的参数argument是个数组,数组由所有参数组成;对于call,传给fn的参数argument直接是所有参数的排列, 直接一个个写入就可以。
例如要传给函数fn三个参数: 1、2、3. 则对于 call和apply调用的方法分别是:
2.5 箭头函数 和 bind 函数
箭头函数和bind函数对于this的处理与普通函数不同, 要单独拿出来说。
2.5.1 箭头函数
与传统函数不同, 箭头函数本身不包含this, 它的 this 继承自它定义时的作用域链的上一层。而且箭头函数不能作为构造函数,它也没有文章 第1部分 所说的arguments属性。
下面用一个例子引出箭头函数中this的来源:
可以看到, 在定时器内定义的普通匿名函数无法访问到 Person 的 age 属性, 这是因为setTimeout是个全局函数, 它的内部的this指向的是window, 而 window 上没有 age 这个属性, 所以就得到了 undefined。 从下面this === window 为 true 也说明了匿名函数中this指向的是window。
将普通的函数换成箭头函数之后可以看到如下结果:
由上面的代码可以看出箭头函数内的 this 指向实例 p, 即它的 this 指向的是定义时候的作用域链的上一层。
说明: 这个例子仅用来引出箭头函数的this指向的来源, 不要像这样使用构造函数。
2.5.2 bind函数
bind函数的作用是根据一个旧函数而创建一个新函数,语法为newFn = oldFn.bind(thisTarget)。它会将旧函数复制一份作为新函数, 然后将新函数的this永远绑定到thisTarget指向的上下文中, 然后返回这个新函数, 以后每次调用这个新函数时, 无论用什么方法都无法改变这个新函数的 this 指向。例如:
// 创建一个对象有 name 和 sayName 属性let p1 = {name: 'P1',sayName(){ console.log(this.name); // 访问函数指向的 this 的 name 属性}}p1.sayName(); // P1// 创建一个对象 p2, 并把这个对象作为bind函数绑定的thislet p2 = {name: 'P2'}// 将p1的 sayName 函数的 this 绑定到 p2 上, 生成新函数 sayP2Name 并返回let sayP2Name = p1.sayName.bind(p2); // 由于此时 sayP2Name 的内部 this 已经绑定了 p2, // 所以即使是按 文章2.1部分 所说的直接调用 sayP2Name, 它的 this 也是指向 p2 的, 并不是指向全局上下文或者 undefinedsayP2Name(); // P2// 定义新对象, 尝试将 sayP2Name 的 this 指向到 p3 上let p3 = {name: 'P3'}// 尝试使用 call和apply 函数来将 sayP2Name 函数的 this 指向p3,// 但是由于 sayP2Name 函数的this 已经被bind函数永远绑定到p2上了, 所以this.name仍然是p2.namesayP2Name.call(p3); // P2sayP2Name.apply(p3); // P2通过以上内容可知一旦通过 bind 函数绑定了 this, 就再也无法改变 this 的指向了.
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
一.String:存储一个字符串,并且提供处理字符串需要的属性和方法。1.创建String对象:显示和隐式js函数//申明String对象的两种方式:显式和隐式
js中如何实现方法重载?这涉及到三个问题1.同名函数的调用问题2.函数中特殊的参数arguments3.如何利用arguments实现方法重载1、同名函数的调用
在学习Java的过程中,我们会遇到许多的问题。下面我们就来看看什么是隐式参数和显示参数。显式参数,就是平时见到的在方法名括号中间的参数,就是所谓能看得见的参数。
本文实例讲述了JS函数动态传递参数的方法。分享给大家供大家参考,具体如下:js函数体内可以通过arguments对象来接收传递进来的参数,利用这一对象属性可以动
JS函数的参数在function内可以用arguments对象来获取。参数的调用有两种方式:1、期望参数的使用。2、实际传递参数的使用。应用举例:functio