Prototype source code analysis of Enumerable (2)

Source: Internet
Author: User

In the previous each method, the $ break and $ continue in the source code are missing. These two variables are predefined and act as break and continue statements in a normal loop. For efficiency, in some operations, you do not need to traverse a set completely (not limited to an array). Therefore, break and continue are necessary.
For a loop, compare the following methods to exit the loop:
Copy codeThe Code is as follows:
Var array_1 = [1, 2, 3];
Var array_2 = ['A', 'B', 'C'];
(Function (){
For (var I = 0, len = array_1.length; I <len; I ++ ){
For (var j = 0, len_j = array_1.length; I <len_j; j ++ ){
If ('c' = array_2 [j]) {
Break;
}
Console. log (array_2 [j]);
}
}
}) (); // A, B, a, B, a, B
(Function (){
For (var I = 0, len = array_1.length; I <len; I ++ ){
Try {
For (var j = 0, len_j = array_1.length; I <len_j; j ++ ){
If ('c' = array_2 [j]) {
Throw new Error ();
}
Console. log (array_2 [j]);
}
} Catch (e ){
Console. log ('exit a loop ');
}
}
}) (); // A, B, 'exit a loop ', a, B, 'exit a loop', a, B, 'exit a loop'
(Function (){
Try {
For (var I = 0, len = array_1.length; I <len; I ++ ){
For (var j = 0, len_j = array_1.length; I <len_j; j ++ ){
If ('c' = array_2 [j]) {
Throw new Error ();
}
Console. log (array_2 [j]);
}
}
} Catch (e ){
Console. log ('exit a loop ');
}
}) (); // A, B, 'exit a loop'

When we place error capture on the corresponding loop layer, We can interrupt the corresponding loop. You can implement the functions of break and break label (goto ). Such an application requires that the interrupt be moved to the external address, which is exactly the same as that of Enumerable.

Back to Enumerable, because the essence of the each (each = function (iterator, context) {}) method is a loop, the first parameter iterator does not contain loops, therefore, directly calling the break statement will report a syntax error, so the Prototype source code uses the second method above.
Copy codeThe Code is as follows:
Enumerable. each = function (iterator, context ){
Var index = 0;
Try {
This. _ each (function (value ){
Iterator. call (context, value, index ++ );
});
} Catch (e ){
If (e! = $ Break ){
Throw e;
}
}
Return this;
};

Once a $ break is thrown during iterator execution, the loop is interrupted. If it is not $ break, an error is thrown and the program is stable. The $ break definition has no special requirements. You can change it as you like, but it doesn't make much sense.

Some methods in Enumerable have been implemented in some modern browsers (see arrays of chrome native methods). Below is a comparison diagram:


When implementing these methods, you can use native methods to improve efficiency. However, the source code does not use the native part, probably because Enumerable needs to be mixed into other objects in addition to the Array part.

As shown in the figure above, we can see that the importance of each and map is actually each, but each processes each item of the Set in sequence, and map is based on each, the processed results are returned. Within Enumerable, map is an alias of the collect method, and the other alias is select, all of which use the name collect internally.

Check: all | any | include

These three methods do not involve processing the original set, and return values are of the boolean type.

All: if all the elements in Enumerable are equivalent to true, true is returned. Otherwise, false is returned.
Copy codeThe Code is as follows:
Function all (iterator, context ){
Var result = true;
This. each (function (value, index ){
Result = result &&!! Iterator. call (context, value, index );
});
Return result;
}

For the all method, neither of the two parameters is required. Therefore, an internal function is provided to replace the iterator without real parameters. The original value is directly returned, called Prototype. K. Prototype. K is defined at the beginning of the database and is a function Prototype. K = function (x) {return x;} that returns the parameter value ;}. In addition, in the all method, as long as the processing result of one item is false, the entire process can be abandoned (break), so the interrupt loop method starting with this article is used. The final form is:
Copy codeThe Code is as follows:
Prototype. K = function (){};
Enumerable. all = function (iterator, context ){
Iterator = iterator | Prototype. K;
Var result = true;
This. each (function (value, index ){
Result = result &&!! Iterator. call (context, value, index );
If (! Result) throw $ break;
});
Return result;
}

The final returned result is a boolean type, which deviates from all. Let's change the result:
Copy codeThe Code is as follows:
Function collect (iterator, context ){
Iterator = iterator | Prototype. K;
Var results = [];
This. each (function (value, index ){
Results. push (iterator. call (context, value, index ));
});
Return results;
}

At this time, the results is an array. We will not interrupt the processing process, save all the results and return them. Well, this is the collect method or the map method.


Any: if one or more Enumerable elements are equivalent to true, true is returned. Otherwise, false is returned. The principle is similar to all. If all is false, the task is terminated, if any is true, the job is terminated.
Copy codeThe Code is as follows:
Function any (iterator, context ){
Iterator = iterator | Prototype. K;
Var result = false;
This. each (function (value, index ){
If (result = !! Iterator. call (context, value, index ))
Throw $ break;
});
Return result;
}

Include: to determine whether a specified object exists in Enumerable, this method is further optimized based on the = Operator, that is, the indexOf method is called. For arrays, if indexOf returns-1, you cannot know that the corresponding element does not exist. If the set does not have the indexOf method, you can only find the matching result. Search and no algorithms here, just traverse one by one. If it is easy to rewrite, but there are not many applications at ordinary times, it is estimated that we did not spend the effort to optimize this. Therefore, if the result is true, the efficiency is higher than when the result is false, depending on luck.
Copy codeThe Code is as follows:
Function include (object ){
If (Object. isFunction (this. indexOf) // this decision function should be familiar
If (this. indexOf (object )! =-1) return true; // call directly if indexOf exists.

Var found = false;
This. each (function (value) {// efficiency problem here
If (value = object ){
Found = true;
Throw $ break;
}
});
Return found;
}


The following is a set of data filtering methods: return a single element: max | min | detect returns an array: grep | findAll | reject | partition where max and min are not limited to numerical comparison, the character can be compared. Max (iterator, context) can still contain two parameters. You can use iterator to compare values. The advantage is that it does not have to be limited to a specific data type, for example, the object array obtains the maximum value according to certain rules:
Copy codeThe Code is as follows:
Console. dir ([{value: 3}, {value: 1}, {value: 2}]. max (function (item ){
Return item. value;
}); // 3

Therefore, the implementation method of source code can be imagined. The implementation method of direct comparison can be as follows:
Copy codeThe Code is as follows:
Function max (){
Var result;
This. each (function (value ){
If (result = null | value> = result) // result = null is the first comparison.
Result = value;
});
Return result;
}

After expansion, the value needs to be further changed to value = (the returned value after iterator processing ):
Copy codeThe Code is as follows:
Function max (iterator, context ){
Iterator = iterator | Prototype. K;
Var result;
This. each (function (value, index ){
Value = iterator. call (context, value, index );
If (result = null | value> = result)
Result = value;
});
Return result;
}

Min works the same way. The principle and closeness of detect and any. If any is true, true is returned. If detect is true, the value that meets the true condition is returned. The source code will not be pasted. Grep is very familiar. A unix/linux tool is also familiar with the function of returning all elements that match the specified regular expression. Only unix/linux can process strings. The range is extended here, but the basic form remains unchanged. If each item in the set is a string, the implementation is like this:
Copy codeThe Code is as follows:
Enumerable. grep = function (filter ){
If (typeof filter = 'string '){
Filter = new RegExp (filter );
}
Var results = [];
This. each (function (value, index ){
If (value. match (filter )){
Results. push (value );
}
})
Return results;
};

However, the set to be processed now may all be strings. To achieve wider application, you must first consider the call form. Taking a look at the above implementation, pay attention to the following sentence:
If (value. match (filter ))
Here, value is a String, and match is a String method. To extend the supported types, either add the match method to each value or convert the form. Apparently, the first type of sound was too loud, and the author switched his mind:
If (filter. match (value ))
In this way, no matter what the value is, as long as the filter has the corresponding match method, the above RegExp object does not have the match method, so in the source code, the author extends the RegExp object:
RegExp. prototype. match = RegExp. prototype. test;
Note that the match above is essentially different from the match of String. In this way, if the value is an object, our filter only needs to provide the matching method for the object to be detected. So there is:
Copy codeThe Code is as follows:
Function grep (filter, iterator, context ){
Iterator = iterator | Prototype. K;
Var results = [];

If (Object. isString (filter ))
Filter = new RegExp (RegExp. escape (filter ));

This. each (function (value, index ){
If (filter. match (value) // native filter does not have the match method.
Results. push (iterator. call (context, value, index ));
});
Return results;
}

Matching results can be processed and then returned. This is the role of the iterator parameter. Unlike the max method, grep uses iterator to process results when performing major operations. max uses iterator to process source data before performing major operations. Because the filter in grep replaces the iterator in max. As for findAll, it is an enhanced version of grep. It is easy to read grep and findAll. Reject is the Gemini version of findAll. Partition is findAll + reject, which combines parent-child versions. For more information, see http://www.cnblogs.com/xesam /]

Related Article

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.