Enumerable is the cornerstone of the Prototype framework, and Enumerable is not used alone. In Prototype, other objects mix the methods in Enumerable, so that Enumerable methods can be applied to these objects. Such objects include: Array, Hash, ObjectRange , and some objects related to DOM and AJAX.
Enumerable provides a large set of useful methods for enumerations, that is, objects that act as collections of values. It is a cornerstone of Prototype.
Enumerable is what we like to call a module : a consistent set of methods intended not for independent use, but for mixin: incorporation into other objects that “fit” with it.
Quite a few objects, in Prototype, mix Enumerable in already. The most visible cases are Array and Hash, but you'll find it in less obvious spots as well, such as in ObjectRange and various DOM- or AJAX-related objects.
The above sentence probably means that Enumerable is Prototype The cornerstone of the framework, and Enumerable is not used alone. Other objects in Prototype mix the methods in Enumerable, so that Enumerable methods can be applied to these objects. Such objects include: Array, Hash, ObjectRange, and some with DOM. , AJAX related objects.
I personally understand that Enumerable is equivalent to the concept of abstract class in C. Other classes can inherit from this class and implement the abstract method "_each" in Enumerable, and can override other methods, but Enumerable itself cannot be instantiated. , only its subclasses can be instantiated.
Let’s take a look at how to write your own class through Enumerable:
Copy the code The code is as follows:
var YourObject = Class.create ();
Object.extend(YourObject.prototype, Enumerable); Object.extend(YourObject.prototype, {
initialize: function() {
// with whatever constructor arguments you need
/ / Your construction code
},
_each: function(iterator) {
// Your iteration code, invoking iterator at every turn
},
// Your other methods here, including Enumerable overrides
});
It can be seen that the most important thing is to implement the _each method. The initialize method is equivalent to the constructor. If no external parameters are required, it is completely fine. Omit. Below I wrote a class that generates a random number array. It is very simple, but has many imperfections. It is only used for demonstration:
Copy the code The code is as follows:
//Create RandomArray class
var RandomArray = Class.create();
//mixin Enumerable
Object.extend(RandomArray.prototype, Enumerable);
/ / Implement _each and required methods
Object.extend(RandomArray.prototype, {
initialize: function(min,max,count) {
this.min=min;
this.max= max;
this.count=count;
this._numbers=[];
this._createRandomArray();
},
_each: function(iterator) {
var index =this.count; 🎜> _createRandomArray:function(){
var index=0;
while(index
}
} ,
include:function(number){
return this._numbers.indexOf(number)!=-1;
}
});
var obj = new RandomArray( 4,19,5);
//alert(obj.size());
alert(obj.entries());
Look at the source code of Enumerable , and then learn each method in detail:
Copy the code
The code is as follows:
var $break = { };
var Enumerable = (function() {
//Traverse each data
function each(iterator, context) {
var index = 0;
try {
this._each(function(value) {
iterator.call(context, value, index );
});
} catch (e) {
if (e != $break) throw e;
}
return this;
}
//Divide the data into N groups, each group has number number, the last group may be less than number
function eachSlice(number, iterator, context) {
var index = -number, slices = [], array = this.toArray();
if ( number < 1) return array;
while ((index = number) < array.length)
slices.push(array.slice(index, index number));
return slices.collect( iterator, context);
}
//Test whether all data satisfies a certain condition
function all(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;
}
//Check whether any data satisfies a certain condition
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;
}
//Can perform any operation on all data and return the result array
function collect (iterator, context) {
iterator = iterator || Prototype.K;
var results = [];
this.each(function(value, index) {
results.push(iterator. call(context, value, index));
});
return results;
}
//Find the first data that meets a certain condition and return it, which is equivalent to Alias of find method
function detect(iterator, context) {
var result;
this.each(function(value, index) {
if (iterator.call(context, value, index) ) {
result = value;
throw $break;
}
});
return result;
}
//Find all items that satisfy a certain condition data and return the results
function findAll(iterator, context) {
var results = [];
this.each(function(value, index) {
if (iterator.call(context , value, index))
results.push(value);
});
return results;
}
//Filter all data according to filter conditions and find those that satisfy filter Conditional data and return results
//filter is a string or regular expression
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))
results.push(iterator.call(context, value, index));
});
return results;
}
//Check whether certain data is included
function include(object) {
if (Object.isFunction(this.indexOf))
if (this.indexOf(object) != - 1) return true;
var found = false;
this.each(function(value) {
if (value == object) {
found = true;
throw $break;
}
});
return found;
}
//Similar to the eachSlice method, if the number of elements in the last group is less than number, use the fillWith parameter Fill
function inGroupsOf(number, fillWith) {
fillWith = Object.isUndefined(fillWith) ? null : fillWith;
return this.eachSlice(number, function(slice) {
while(slice. length < number) slice.push(fillWith);
return slice;
});
}
//Continuously perform an operation on all data to achieve accumulation or accumulation Multiplication and other operations
function inject(memo, iterator, context) {
this.each(function(value, index) {
memo = iterator.call(context, memo, value, index);
});
return memo;
}
//Execute a method on all data
function invoke(method) {
var args = $A(arguments) .slice(1);
return this.map(function(value) {
return value[method].apply(value, args);
});
}
//Find the maximum value in the data
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;
}
//Find the minimum value in the data
function min(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;
}
//Divide all the data into two, the first group is the data that meets a certain condition, and the second group For data that does not meet the conditions
function partition(iterator, context) {
iterator = iterator || Prototype.K;
var trues = [], falses = [];
this.each(function(value, index) {
(iterator.call(context, value, index) ?
trues : falses).push(value);
});
return [ trues, falses];
}
//Get the property values of all data and return the results
function pluck(property) {
var results = [];
this .each(function(value) {
results.push(value[property]);
});
return results;
}
//Find a result that does not satisfy a certain Conditional data
function reject(iterator, context) {
var results = [];
this.each(function(value, index) {
if (!iterator.call(context, value , index))
results.push(value);
});
return results;
}
//Sort all data according to a certain condition
function sortBy(iterator, context) {
return this.map(function(value, index) {
return {
value: value,
criteria: iterator.call(context, value, index)
};
}).sort(function(left, right) {
var a = left.criteria, b = right.criteria;
return a < b ? -1 : a > ; b ? 1 : 0;
}).pluck('value');
}
//Return the array representation of data
function toArray() {
return this.map();
}
//Basically put two sets of data together to perform certain operations
function zip() {
var iterator = Prototype.K, args = $A(arguments);
if (Object.isFunction(args.last()))
iterator = args.pop();
var collections = [this].concat(args ).map($A);
return this.map(function(value, index) {
return iterator(collections.pluck(index));
});
}
function size() {
return this.toArray().length;
}
//Return the string representation representing the Enumerable object
function inspect() {
return '#
}
return {
each: each,
eachSlice: eachSlice,
all: all,
every: all,
any: any,
some: any,
collect: collect,
map: collect,
detect: detect,
findAll: findAll,
select: findAll,
filter: findAll,
grep: grep,
include: include,
member: include,
inGroupsOf: inGroupsOf,
inject: inject,
invoke: invoke,
max: max,
min: min,
partition: partition,
pluck: pluck,
reject: reject,
sortBy: sortBy,
toArray: toArray,
entries: toArray,
zip: zip,
size: size,
inspect: inspect,
find: detect
};
})();
Let’s learn the methods provided by Enumerable:
all
any
collect
detect
each
eachSlice
entries
find
findAll
grep
inGroupsOf
include
inject
invoke
map
max
member
min
partition
pluck
reject
select
size
sortBy
toArray
zip
all method:
Determines whether all the elements are boolean-equivalent to true, either directly or through computation by the provided iterator.
Basically call the each method to check whether each data meets the iterator condition, and if one of them does not meet the iterator condition, $break is thrown Exception, then this exception will be caught in each method. Pay attention to the usage of '!!' here, which can convert certain objects into corresponding bool values:
!!{} true
!![] true
!! 'False
!!' String 'true
!! 0 false
Let's take a look at the example:
Copy the code The code is as follows:
[].all()
// -> true (empty arrays have no elements that could be false-equivalent)
$R(1, 5).all()
// -> true (all values in [1..5] are true-equivalent)
[0, 1, 2]. all()
// -> false (with only one loop cycle: 0 is false-equivalent)
[9, 10, 15].all(function(n) { return n > = 10; })
// -> false (the iterator will return false on 9)
$H({ name: 'John', age: 29, oops: false }).all (function(pair) { return pair.value; })
// -> false (the oops/false pair yields a value of false)
any Method:
is similar to the all method, so I won’t go into details. Take a look at the example
Copy the code The code is as follows:
[].any()
// -> false (empty arrays have no elements that could be true-equivalent)
$R(0, 2).any( )
// -> true (on the second loop cycle, 1 is true-equivalent)
[2, 4, 6, 8, 10].any(function(n) { return 0 == n % 3; })
// -> true (the iterator will return true on 6: the array does have 1 multiple of 3)
$H({ opt1: null, opt2 : false, opt3: '', opt4: 'pfew!' }).any(function(pair) { return pair.value; })
// -> true (thanks to the opt4/'pfew!' pair, whose value is true-equivalent)
collect/map (collect method alias) method:
Returns the results of applying the iterator to each element. Aliased as map.
This is a sort of Swiss-Army knife for sequences. You can turn the original values into virtually anything!
This method is called "Swiss Army Knife" "This method can basically perform any operation on data. The map method is an alias of this method. The implementation of this method is very simple. results.push(iterator.call(context, value, index)); This sentence is the key, It is to make an iterator call for each data and store the result in the array to be returned. Take a look at the example:
Copy the code The code is as follows:
['Hitch', "Hiker's", 'Guide', 'To', 'The', 'Galaxy'].collect(function(s) { return s.charAt(0).toUpperCase(); }) .join('')
// -> 'HHGTTG'
$R(1,5).collect(function(n) { return n * n; })
// -> [1, 4, 9, 16, 25]
Note the last few lines of the help document:
First, the method-calling scenario : you want to invoke the same method on all elements, possibly with arguments, and use the result values. This can be achieved easily with invoke.
Second, the property-fetching scenario: you want to fetch the same property on all elements, and use those. This is a breeze with pluck.
detect method:
Find the first data that meets a certain condition and return it. Take a look at the example. Actually This method is an alias of find, so calling detect and find are the same:
Copy code The code is as follows:
// An optimal exact prime detection method, slightly compacted.
function isPrime(n) {
if (2 > n) return false;
if (0 == n % 2) return (2 == n);
for (var index = 3; n / index > index; index = 2)
if (0 == n % index) return false;
return true;
}
// isPrime
$R(10,15).find(isPrime) // -> 11
[ 'hello', 'world', 'this', 'is', 'nice']. find(function(s) { return s.length <= 3; })
// -> 'is'
each method:
When calling this method, it actually performs an iterator operation on each data. The first parameter passed into the iterator function is the data, and the second parameter is Index, $continue and $break exceptions can be thrown during the traversal process. Note:
The usage of <span style="font-family:新宋体">$continue<code><span style="font-family:新宋体">$continue</span>
is deprecated. This feature will not be available in releases after Prototype 1.5 in favor of speed.
Look at the example:
Copy code The code is as follows:
['one', 'two', 'three'].each(function(s) { alert (s); });
[ 'hello', 'world'].each(function(s, index) { alert(index ': ' s); });
// alerts -> '0: hello' then '1: world'
// This could be done better with an accumulator using inject, but humor me
// here...
var result = [];
$R(1,10).each(function(n) {
if (0 == n % 2) throw $continue;
if (n > 6) throw $ break;
result.push(n);
});
// result -> [1, 3, 5]
eachSlice method:
Many simple methods below are given directly as examples. I won’t explain them anymore. There is nothing difficult about the algorithm. It’s basically the same. You can understand it by looking at it:
Copy the code The code is as follows:
var students = [ { name: 'Sunny', age: 20 }, { name: 'Audrey', age: 21 }, { name: 'Matt', age: 20 }, { name: ' Élodie', age: 26 }, { name: 'Will', age: 21 }, { name: 'David', age: 23 }, { name: 'Julien', age: 22 }, { name: 'Thomas' , age: 21 }, { name: 'Serpil', age: 22 } ];
students.eachSlice(4, function(toon) { return toon.pluck('name'); })
// -> [ ['Sunny', 'Audrey', 'Matt', 'Élodie'],
// ['Will', 'David', 'Julien', 'Thomas'],
// ['Serpil'] ]
//The first method below is provided by the Array object
students.eachSlice(2).first()
// -> [{ name : 'Sunny', age: 20 }, { name: 'Audrey', age: 21 }]
entries method is the toArray method, and the map method is called in the toArray method, map The method is equivalent to the collect method:
Copy code The code is as follows:
$R(1, 5).toArray() // - > [1, 2, 3, 4, 5]
find/findAll (alias of select method) method:
Copy code The code is as follows:
//find method
[ 'hello', 'world', 'this', 'is', 'nice'].find(function(s) { return s.length <= 3; })
// -> 'is'
//findAll method
$R(1, 10).findAll(function(n) { return 0 == n % 2; })
// -> [2, 4, 6, 8, 10] [ 'hello', 'world', 'this', 'is', 'nice'].findAll (function(s) { return s.length >= 5; })
// -> ['hello', 'world']
grep method:
What needs to be noted about this method is that the parameter filter must be unified into a regular expression in the function, and then the match method of the regular expression is called to make a judgment
Copy code The code is as follows:
// Get all strings with a repeated letter somewhere
['hello', 'world', 'this', 'is', 'cool'].grep (/(.)1/)
// -> ['hello', 'cool']
// Get all numbers ending with 0 or 5
$R(1,30).grep (/[05]$/)
// -> [5, 10, 15, 20, 25, 30]
// Those, minus 1
$R(1,30).grep (/[05]$/, function(n) { return n - 1; })
// -> [4, 9, 14, 19, 24, 29]
// Get all strings with a repeated letter somewhere
['hello', 'world', 'this', 'is', 'cool'].grep(/(.)1/)
// -> ['hello' , 'cool']
// Get all numbers ending with 0 or 5
$R(1,30).grep(/[05]$/)
// -> [5, 10 , 15, 20, 25, 30]
// Those, minus 1
$R(1,30).grep(/[05]$/, function(n) { return n - 1; })
// -> [4, 9, 14, 19, 24, 29]
inGroupsOf method:
Copy code The code is as follows:
var students = [ { name: 'Sunny', age: 20 }, { name: 'Audrey', age: 21 }, { name: 'Matt', age: 20 }, { name: 'Élodie', age: 26 }, { name: 'Will', age: 21 }, { name: 'David', age: 23 }, { name: 'Julien', age: 22 }, { name: 'Thomas', age: 21 }, { name: 'Serpil', age: 22 } ];
//The pluck method is to obtain a certain attribute of the object, here the name attribute is obtained
students.pluck('name').inGroupsOf(4)
// -> [ ['Sunny', 'Audrey', 'Matt', 'Élodie'],
// ['Will ', 'David', 'Julien', 'Thomas'],
// ['Serpil', null, null, null] ]
include/member(include method alias) method, here we first check whether there is an indexOf method on the object, and if so, call this method directly:
Copy code The code is as follows:
$R(1,15).include(10)
// -> true
['hello', 'world'].include('HELLO')
/ / -> false
[1, 2, '3', '4', '5'].include(3)
// -> true (== ignores actual type)
inject method:
Copy code The code is as follows:
$R(1,10).inject(0, function(acc, n) { return acc n; })
// -> 55 (sum of 1 to 10)
$R(2,5).inject(1, function(acc, n) { return acc * n; })
// -> 120 (factorial 5)
['hello', 'world' , 'this', 'is', 'nice'].inject([], function(array, value, index) {
if (0 == index % 2) array.push(value);
return array;
})
// -> ['hello', 'this', 'nice']
invoke method:
Copy code The code is as follows:
['hello', 'world', 'cool!'].invoke('toUpperCase')
// ['HELLO', 'WORLD', 'COOL!']
['hello', 'world', 'cool!'].invoke('substring', 0, 3)
// [ 'hel', 'wor', 'coo']
max/min method:
Copy code The code is as follows:
$R(1,10).max() // -> 10
['hello', 'world', 'gizmo'].max()
// -> 'world'
function Person(name, age) { this.name = name; this.age = age; }
var john = new Person('John', 20);
var mark = new Person('Mark', 35);
var daisy = new Person('Daisy', 22);
[john, mark, daisy].max(function(person) { return person.age ; })
// -> 35
partition method:
Copy code The code is as follows:
['hello', null, 42, false, true, , 17].partition()
// -> [['hello', 42, true, 17], [null, false, undefined]]
$R(1, 10).partition(function(n) { return 0 == n % 2; })
// -> [[2, 4, 6, 8, 10], [1, 3, 5, 7, 9]]
['hello', null, 42, false, true, , 17].partition()
// -> [['hello', 42, true, 17], [null, false, undefined]]
$R(1, 10).partition(function(n) { return 0 == n % 2; })
// -> [[2, 4, 6, 8, 10], [1, 3, 5, 7, 9]]
pluck method:
Copy code The code is as follows:
['hello', 'world', 'this', 'is', 'nice'].pluck( 'length')
// -> [5, 5, 4, 3, 4]
reject method:
Copy code The code is as follows:
$R(1, 10).reject(function(n) { return 0 == n % 2; })
// -> [1, 3, 5, 7, 9]
[ 'hello', 'world', 'this', 'is', 'nice'].reject(function(s) { return s.length >= 5; })
// -> ['this', 'is', 'nice']
$R(1, 10).reject(function(n) { return 0 == n % 2; })
// -> [1, 3, 5, 7, 9]
[ 'hello', 'world', 'this', 'is', 'nice']. reject(function(s) { return s.length >= 5; })
// -> ['this', 'is', 'nice']
size method is omitted.
sortBy method:
This method first returns an object array through the map method, then calls the sort method of the array, and finally takes out the value attribute in the object
Copy the code The code is as follows:
['hello', 'world', 'this', 'is', 'nice'].sortBy(function(s) { return s.length ; })
// -> 'is', 'this', 'nice', 'hello', 'world']
['hello', 'world', 'this', 'is' , 'cool'].sortBy(function(s) {
var md = s.match(/[aeiouy]/g);
return null == md ? 0 : md.length;
} )
// -> [ 'world', 'this', 'is', 'hello', 'cool'] (sorted by vowel count)
zip method :
Copy code The code is as follows:
var firstNames = ['Justin', 'Mislav', 'Tobie', 'Christophe'];
var lastNames = ['Palmer', 'Marohnić', 'Langel', 'Porteneuve'] ;
firstNames.zip(lastNames)
// -> [['Justin', 'Palmer'], ['Mislav', 'Marohnić'], ['Tobie', 'Langel'], [ 'Christophe', 'Porteneuve']]
//From this example we can see that parameter a is an array, representing the corresponding items in the two arrays
firstNames.zip(lastNames, function(a) { return a.join(' '); })
// -> ['Justin Palmer', 'Mislav Marohnić', 'Tobie Langel', 'Christophe Porteneuve']
//Through this example we You can see that multiple arrays can be passed in
var cities = ['Memphis', 'Zagreb', 'Montreal', 'Paris'];
firstNames.zip(lastNames, cities, function(p) { return p[0] ' ' p[1] ', ' p[2]; })
// -> ['Justin Palmer, Memphis', 'Mislav Marohnić, Zagreb', 'Tobie Langel, Montreal ', 'Christophe Porteneuve, Paris']
firstNames.zip($R(1, 100), function(a) { return a.reverse().join('. '); })
// -> ['1. Justin', '2. Mislav', '3. Tobie', '4. Christophe']