Suppose we have an array of strings [' some ', ' strings ']
and we need to clear the whitespace before and after the strings, the first thought is.
|
|
For further optimization, consider removing the wrapped function from the map and using String.prototype.trim directly. however, the problem arises.
The second argument of map
The reason for the error is that Array.prototype.map has a second argument that is easily overlooked.
where thisArg specifies the value of this to be bound when the callback is executed, and we print this this out as follows
JS’s methods and prototype
We know that trim, map, and other methods are defined in built-in objects such as String, Array, and so on. The implementation of these methods is integrated into the JS runtime environment. Here is a reference to a polyfill of String.prototype.trim.
When we call " str ".trim()
, the string literal is converted to a String object and the .
operator binds this object to the this value of String.prototype.trim() and executes this function. As you can see, the string that trim operates on is the value of this, not the argument passed in to the function. Without explicit binding, it appears that this in functions defined on the built-in object prototype will have a default initial value. For example.
The second parameter in Array.prototype.map is just another way to bind this, usually we don’t pass this parameter, and this in the map callback function is undefined. this in String.prototype.trim is tied to undefined, resulting in a TypeError, which is equivalent to the following case.
Function.prototype.call
There is one solution.
Function.prototype.call is another way to modify the this bound to a function call.
We pass Function.prototype.call in the first parameter of the map, where this is bound to the second parameter passed String.prototype.trim, similar to the following code.
Summary
The above solution is fantastic, and the actual development should avoid similar enigmatic code. The recommended method for this specific scenario is [' some ', ' strings '].map(s => s.trim()
.
However, learning about this issue helps to deepen your understanding of JS prototype and language features such as functions.
We generally refer to functions that “belong” to an object as methods. JS implements such a mechanism semantically through the prototype mechanism. In practice, however, a method defined in an object is not fundamentally different from a normal function, except that the object happens to hold a reference to that function. Even if a normal function can manipulate this, there is no direct connection between the value bound to this and the object it contains.
JS’s this, prototype, and other mechanisms should be designed to implement the object-oriented paradigm in dynamic languages. Functions (methods) implement object-oriented “artifacts” by manipulating this instead of the function’s arguments, and by calling methods like s.trim()
. A recent look at Python’s classes shows some similarities. The functions in a class are related to each other only by self, very similar to JS’s this, except that Python’s “method” lists self explicitly as an argument to a function, whereas JS’s this is implicit and can be tampered with in some special way. JS’s this is implicit and can be tampered with in some special way, making it somewhat elusive.
map is the classic set of functional styles. Ideally, a map function should be a pure function, e.g. it is more elegant to call jQuery’s trim function.
|
|
However, String.prototype.trim in the JS standard library operates on this and is not a pure function, which leads to the problem described in this article.
Legacy issues
Testing in Firefox 61 it seems that the second argument of map is invalid.