call() 和 apply() 实际上做了什么来欺骗数组方法来处理类似数组的对象

问题描述 投票:0回答:4

有很多关于 call、apply 和 bind 之间差异的信息,但我正在努力寻找有关 call 和 apply 方法究竟如何欺骗现有函数(通常接受数组)以接受类似数组的对象的信息。

据我了解,对此类方法的完整引用是关于在哪里找到该方法的说明。 然后,调用/应用方法将“this”参数更改为指向类似数组的对象。

我找了很多JS源码都没有结果。

在 Math.max.apply(Math,arguements) 中,如果我们可以重新调整期望数组的函数来处理类似数组的对象,部分原因是由于更新了“this”参数,那么它有什么意义只提供数学作为上下文。

call 和 apply 有什么特别之处,可以欺骗方法来处理类似数组的对象??

<script type="text/javascript">
function multiMax(multi){
    return multi  * Math.max.apply(Math,
        Array.prototype.slice.call(arguments,1));
}
assert(multiMax(3, 1, 2, 3) == 9, "3*3=9 (First arg, by  largest.)");
</script>
javascript javascript-objects
4个回答
2
投票

call
apply
无法欺骗函数。

相反,当您使用它们在类似数组的对象上调用数组方法时,这是有效的,因为数组方法是故意通用的。

[whatever] 函数故意是通用的;它不需要 它的

this
值是一个 Array 对象。因此可以转移 到其他类型的对象用作方法。

对于

Math
函数,由于它们根本不使用
this
值,因此您可以使用任何您想要的值。


2
投票

所有数组都是对象。唯一阻止方法处理类似数组的对象的方法是显式检查该对象是否是数组。这是无法通过可用于类似数组对象的方法来完成的。事实上,您甚至不需要使用

call
bind
来完成此操作;您可以将该方法附加到类似数组的对象并像平常一样调用它(但是我可能不会推荐它)。


1
投票

apply 和 call 对类似数组的元素不执行任何操作,也不会欺骗任何操作。他们只是做(工作),因为他们公开了其成员的索引。

数组方法通过索引访问成员;如果一个成员对于给定的数组方法来说是正确的类型,那么它将像处理它自己的方法一样轻松地对其进行操作。但是,如果该成员不是可以由给定数组方法处理的类型,您自然会收到错误。

在早期版本的 JavaScript 中,您可以将感兴趣的对象作为参数传递给数组对象的属性函数,而无需使用 call 或 apply 方法。

如:

Array.split( collection );


1
投票

我认为其他答案描述得很好,但也许这个说明性代码也可以阐明这个主题。 这是一个关于通用推送及其人造邪恶双胞胎的故事:

// create plain object
var obj = {}

// give it ability to `push` by simple assignment from some poor one-time array instance
obj.PSH = [].push
// poor array will be lost in garbage: we should have used Array.prototype instead, but nevermind

console.log(obj)
// => Object {}
// (Chrome console does not show method, but we can see it in inspection or in for-in loop:)

for(var prop in obj) console.log(prop,':',obj[prop])
// => PSH : push() { [native code] }
// ah, 'native code', how enigmatic.

// let's use it
obj.PSH('zero')
obj.PSH('one')
console.log(obj)
// => Object {0: "zero", 1: "one", length: 2}
// now we see it created numbered properties and `length` property on our object 

// what happens if we alter that `length` and call PSH after?
obj.length = 10
obj.PSH('TEN')
console.log(obj)
// => Object {0: "zero", 1: "one", 10: "TEN", length: 11}
// ah, predictable

// now we know what that native code most probably does, so we can create evil twin
function prankpush (what) {
    var where = this.length || 0
    this[where] = what      // insert that to to the last index
    this.length = where + 2 // but lets make it more interesting
}
// this time we will call it, so we will not taint out obj with another method
prankpush.call(obj,'prank2')
prankpush.call(obj,'prank3')
console.log(obj)
// => Object {0: "zero", 1: "one", 10: "TEN", 11: "prank1", 13: "prank2", length: 15}
// no 12 and length is bigger, what an evil success!

// but we could have as well do one time method assignment, call (like we did with native push in the beginning) …
obj.prankpush = prankpush
obj.prankpush('prank4')
// … and this time cover our tracks so prankpush will not be present in for-in-loop
delete obj.prankpush
console.log(obj)
// => Object {0: "zero", 1: "one", 10: "TEN", 11: "prank2", 13: "prank3", 15: "prank4", 17: "prank4", length: 19}
© www.soinside.com 2019 - 2024. All rights reserved.