[Effective JavaScript note] 37th: Recognize the implicit binding problem for this variable

Source: Internet
Author: User
Tags square root

Csvreader Sample Requirements

CSV (comma-separated value) file format is a simple text representation of tabular data

张三,1982,北京,中国小森,1982,东京,日本吉姆,1958,纽约,美国

You now need to write a simple, customizable class that reads CSV data. The separators here are based on commas, but can also handle some other characters as delimiters.

Analysis

The constructor requires an optional array of delimiter characters and constructs a custom regular expression to separate each row into different entries.

function CSVReader(separators){  this.separators=separators||[‘,‘];  this.regexp=new RegExp(this.separators.map(function(sep){      return ‘\\‘+sep[0];  }).join(‘|‘));}

Implementing a simple Read method is a two-step process: The first step is to divide the input string into an array of reception divisions. In the second step, each row of the array is divided into data by unit. The result should be a two-dimensional array.

CSVReader.prototype.read=function(str){  var lines=str.trim().split(/\n/);  return lines.map(function(line){     return line.split(this.regexp);  })};var reader=new CSVReader();reader.read(‘a,b,c\nd,e,f\n‘);//[[‘a,b,c‘],[‘d,e,f‘]]
Problem

Here's a serious and subtle bug. The callback function passed to Line.map references this, which expects to extract the RegExp property of the Csvreader object. However, the map function binds the recipient of its callback function to the lines array, and the lines array does not have a regexp attribute. As a result, this.regexp produces undefined values, causing the call Line.split to fall into disarray. The line cannot be processed to get an array.
The fact that this bug is being caused: the This variable is bound in a different way. Each function has an implicit binding of this variable. The binding value of the This variable is determined when the function is called. For a variable of a lexical scope, you can identify the recipient of its binding by looking for an explicitly named binding name. But this variable is implicitly bound to the nearest enclosing function. Therefore, for the CSVReader.prototype.read function, the binding of this variable is different from the this binding passed to the Lines.map callback function.

The THISAPI parameter in the callback function specifies

Fortunately, the map method of an array can pass in an optional parameter as the this binding of its callback function. Therefore, the simplest way to fix the bug is to pass the external this binding to the callback function through the second parameter of the map.

CSVReader.prototype.read=function(str){  var lines=str.trim().split(/\n/);  return lines.map(function(line){     return line.split(this.regexp);  },this);};var reader=new CSVReader();reader.read(‘a,b,c\nd,e,f\n‘);//[[‘a‘,‘b‘,‘c‘],[‘d‘,‘e‘,‘f‘]]
Lexical scopes

But not all callback-based APIs are considered thoughtful. Provides additional parameters to specify the binding recipient. We need another way to get the this binding to the external function so that the callback function can still refer to it. A straightforward solution is to use a lexical scope variable to store this additional external reference to this binding.

CSVReader.prototype.read=function(str){  var lines=str.trim().split(/\n/);  var self=this;  return lines.map(function(line){     return line.split(self.regexp);  });};var reader=new CSVReader();reader.read(‘a,b,c\nd,e,f\n‘);//[[‘a‘,‘b‘,‘c‘],[‘d‘,‘e‘,‘f‘]]

The variable name of self is usually used to indicate that the sole purpose of the variable is to act as an additional alias for the current scope this binding. (I often use that)

Function Bind method

Another effective method available in ES5 is to use the Bind method of the callback function.

CSVReader.prototype.read=function(str){  var lines=str.trim().split(/\n/);  return lines.map(function(line){     return line.split(this.regexp);  }.bind(this));};var reader=new CSVReader();reader.read(‘a,b,c\nd,e,f\n‘);//[[‘a‘,‘b‘,‘c‘],[‘d‘,‘e‘,‘f‘]]
Tips
    • The scope of the this variable is always determined by its nearest enclosing function

    • Use a local variable (typically named Self,me,that) to use this binding for intrinsic functions that are available

Appendix I: An overview of the array map method

The map () method returns a new array that consists of a return value from each element in the original array that invokes a specified method.

Grammar
array.map(callback[, thisArg])
Parameter callback: The element in the original array returns a new element after the method.
    • The first argument of Currentvalue:callback, the element that is currently passed in the array.
    • The second argument of Index:callback, the index of the element that is currently being passed in the array.
    • The third argument of Array:callback, which calls an array of map methods.
Thisarg: The object to which this is directed when executing the callback function. Describe

The map method invokes the callback function sequentially for each element in the original array.
Callback the return value after each execution is combined to form a new array.
The callback function is only called on the indexed index, and those indexes that have never been assigned a value or deleted by using Delete are not called.
The callback function is automatically passed in three parameters: an array element, an element index, and the original array itself.

If the Thisarg parameter has a value, this will point to the object on the Thisarg parameter each time the callback function is called. If the Thisarg parameter is omitted, or if the assignment is null or undefined, this points to the global object.

Note: Map does not modify the original array that invokes it (it can, of course, change the original array when callback executes).
When working with arrays using the map method,
The range of array elements is determined before the first invocation of the callback method.
In the course of the map method execution:
The newly added elements in the original array will not be accessed by callback;
If existing elements are changed or deleted, their values passed to callback are the values of the time the map method traverses them, and the deleted elements will not be accessed.

Example: Convert a word in an array to a corresponding plural form.

The following code converts all the words in an array to the corresponding plural form.

function fuzzyPlural(single) {  var result = single.replace(/o/g, ‘e‘);    if( single === ‘kangaroo‘){    result += ‘se‘;  }  return result; }var words = ["foot", "goose", "moose", "kangaroo"];console.log(words.map(fuzzyPlural));// ["feet", "geese", "meese", "kangareese"]
Example: Finding the square root of each element in an array

The following code creates a new array with a value of the square root of the corresponding number in the original array.

var numbers = [1, 4, 9];var roots = numbers.map(Math.sqrt);/* roots的值为[1, 2, 3], numbers的值仍为[1, 4, 9] */
Example: Using the Map method on a string

The following example shows an array of ASCII codes that correspond to each character in a string using the Map method on a string:

var map = Array.prototype.mapvar a = map.call("Hello World", function(x) { return x.charCodeAt(0); })// a的值为[72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]
Examples of Use tips

Typically, the callback function in the map method only needs to accept one parameter, which is the array element itself that is being traversed. But that doesn't mean that map only gives callback a parameter. This thought inertia may make us make a mistake that is easy to make.

// 下面的语句返回什么呢:["1", "2", "3"].map(parseInt);// 你可能觉的会是[1, 2, 3]// 但实际的结果是 [1, NaN, NaN]// 通常使用parseInt时,只需要传递一个参数.但实际上,parseInt可以有两个参数.第二个参数是进制数.可以通过语句"alert(parseInt.length)===2"来验证.// map方法在调用callback函数时,会给它传递三个参数:当前正在遍历的元素, 元素索引, 原数组本身.// 第三个参数parseInt会忽视, 但第二个参数不会,也就是说,parseInt把传过来的索引值当成进制数来使用.从而返回了NaN./*//应该使用如下的用户函数returnIntfunction returnInt(element){  return parseInt(element,10);}["1", "2", "3"].map(returnInt);// 返回[1,2,3]*/
Compatibility with legacy environments
Reference: http://es5.github.com/#x15.4.4.19if (!    Array.prototype.map) {Array.prototype.map = function (callback, thisarg) {var T, A, K;    if (this = null) {throw new TypeError ("This is a null or not defined"); }//1.    Assign o to the array that calls the map method.    var O = Object (this);    2. Assign Len to the length of the array O.    var len = o.length >>> 0;    3. If callback is not a function, the TypeError exception is thrown. if (Object.prototype.toString.call (callback)! = "[Object Function]") {throw new TypeError (callback + "is not a Func    tion "); }//4.    If the parameter thisarg has a value, the T is assigned the value Thisarg; otherwise t is undefined.    if (thisarg) {T = Thisarg; }//5.    Creates a new array A with a length of the original array o len A = new Array (len); 6.    The k is assigned a value of 0 k = 0; 7.    When K < Len executes the loop.      while (K < Len) {var kvalue, mappedvalue;        Traverse O,k to the original array index if (k in O) {//kvalue is the value corresponding to index K.        Kvalue = o[K];        The execution callback,this points to T, with three parameters. Kvalue: value, K: Index, O: the original array.        Mappedvalue = Callback.call (T, Kvalue, K, O);        The return value is added to the new array A. a[K] = Mappedvalue;    }//K self-increment 1 k++; }//8.  Returns a new array a return a;      }; }

[Effective JavaScript note] 37th: Recognize the implicit binding problem with this variable

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.