Item 20: 使用 call 方法自定义接收者来调用方法
Item 20: Use call to Call Methods with a Custom
Receiver
Ordinarily, the receiver of a function or method (i.e., the value bound
to the special keyword this ) is determined by the syntax of its caller.
In particular, the method call syntax binds the object in which the
method was looked up to this . However, it is sometimes necessary
to call a function with a custom receiver, and the function may not
already be a property of the desired receiver object. It’s possible, of
course, to add the method to the object as a new property:
obj.temporary = f; // what if obj.temporary already existed?
var result = obj.temporary(arg1, arg2, arg3);
delete obj.temporary; // what if obj.temporary already existed?
But this approach is unpleasant and even dangerous. It is often unde-
sirable, and even sometimes impossible, to modify obj . Specifically,
whatever name you choose for the temporary property, you run the
risk of colliding with an existing property of obj . Moreover, some
objects can be frozen or sealed, preventing the addition of any new
properties. And more generally, it’s bad practice to go around arbi-
trarily adding properties to objects, particularly objects you didn’t
create (see Item 42).
Luckily, functions come with a built-in call method for providing a
custom receiver. Invoking a function via its call method:
f.call(obj, arg1, arg2, arg3);
behaves similarly to calling it directly:
f(arg1, arg2, arg3);
except that the first argument provides an explicit receiver object.
The call method comes in handy for calling methods that may have
been removed, modified, or overridden. Item 45 shows a useful exam-
ple, where the hasOwnProperty method can be called on an arbitrary
object, even if the object is a dictionary. In a dictionary object, looking
up hasOwnProperty produces an entry from the dictionary rather than
an inherited method:
dict.hasOwnProperty = 1;
dict.hasOwnProperty("foo"); // error: 1 is not a function
Using the call method of the hasOwnProperty method makes it pos-
sible to call the method on the dictionary even though the method is
not stored anywhere in the object:
var hasOwnProperty = {}.hasOwnProperty;
dict.foo = 1;
delete dict.hasOwnProperty;
hasOwnProperty.call(dict, "foo"); // true
hasOwnProperty.call(dict, "hasOwnProperty"); // false
The call method can also be useful when defining higher-order func-
tions. A common idiom for a higher-order function is to accept an
optional argument to provide as the receiver for calling the function.
For example, an object that represents a table of key-value bindings
might provide a forEach method:
var table = {
entries: [],
addEntry: function(key, value) {
this.entries.push({ key: key, value: value });
},
forEach: function(f, thisArg) {
var entries = this.entries;
for (var i = 0, n = entries.length; i < n; i++) {
var entry = entries[i];
f.call(thisArg, entry.key, entry.value, i);
}
}
};
This allows consumers of the object to use a method as the callback
function f of table.forEach and provide a sensible receiver for the
method. For example, we can conveniently copy the contents of one
table into another:
table1.forEach(table2.addEntry, table2);
This code extracts the addEntry method from table2 (it could have
even extracted the method from Table.prototype or table1 ), and the
forEach method repeatedly calls addEntry with table2 as the receiver.
Notice that even though addEntry only expects two arguments,
forEach calls it with three: a key, value, and index. The extra index
argument is harmless since addEntry simply ignores it.
Things to Remember
✦ Use the call method to call a function with a custom receiver.
✦ Use the call method for calling methods that may not exist on a given object.
✦ Use the call method for defining higher-order functions that allow
clients to provide a receiver for the callback.
来源:Effective Javascript编写高质量JavaScript代码的68个有效方法