-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathunderscore.own.js
2432 lines (2080 loc) · 96.6 KB
/
underscore.own.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// Underscore.js 1.8.3
// http://underscorejs.org
// (c) 2009-2017 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
// Underscore may be freely distributed under the MIT license.
(function() {
//一上来就是一个大IIFE,防止污染全局变量,典型闭包
// Baseline setup
// --------------
// Establish the root object, `window` (`self`) in the browser, `global`
// on the server, or `this` in some virtual machines. We use `self`
// instead of `window` for `WebWorker` support.
// 优先级 && > ||
// self 指当前窗口
// this 指当前对象
// 浏览器 self
// 服务端 global
// 虚拟机 this
// 都没有新建空对象{}
var root = typeof self == 'object' && self.self === self && self ||
typeof global == 'object' && global.global === global && global ||
this || {};
// Save the previous value of the `_` variable.
// 防止命名冲突,提前存储一下,为了noConflict中使用
var previousUnderscore = root._;
// Save bytes in the minified (but not gzipped) version:
// 节省字节在压缩版本下
var ArrayProto = Array.prototype,
ObjProto = Object.prototype;
var SymbolProto = typeof Symbol !== 'undefined' ? Symbol.prototype : null;
// Create quick reference variables for speed access to core prototypes.
// 创建快速指向,提高访问速度
var push = ArrayProto.push,
slice = ArrayProto.slice,
toString = ObjProto.toString,
hasOwnProperty = ObjProto.hasOwnProperty;
// All **ECMAScript 5** native function implementations that we hope to use
// are declared here.
// 如果有原生的实现,就用原生的
var nativeIsArray = Array.isArray,
nativeKeys = Object.keys,
nativeCreate = Object.create;
// Naked function reference for surrogate-prototype-swapping.
// 代孕,对不起,我想歪了
var Ctor = function() {};
// Create a safe reference to the Underscore object for use below.
// 这一块为了不管外部时候执行new操作,都返回一个new操作之后的实例对象
var _ = function(obj) {
if (obj instanceof _) return obj;
if (!(this instanceof _)) return new _(obj);
// 为了支持面向对象(即: new)操作模式,将对this._wrapped进行操作
// 会将this._wrapped赋到大多数方法的第一个参数
this._wrapped = obj;
};
// Export the Underscore object for **Node.js**, with
// backwards-compatibility for their old module API. If we're in
// the browser, add `_` as a global object.
// (`nodeType` is checked to ensure that `module`
// and `exports` are not HTML elements.)
// 就导出啦
if (typeof exports != 'undefined' && !exports.nodeType) {
if (typeof module != 'undefined' && !module.nodeType && module.exports) {
exports = module.exports = _;
}
exports._ = _;
} else {
root._ = _;
}
// Current version.
// emmmmmm...
_.VERSION = '1.8.3';
// Internal function that returns an efficient (for current engines) version
// of the passed-in callback, to be repeatedly applied in other Underscore
// functions.
// 字面意思优化回调函数
var optimizeCb = function(func, context, argCount) {
if (context === void 0) return func;
// switch 中比较是 ===, 所以这里的case并不会触发,仅仅是忽略了,如果是 == 那么 null 等于 undefined
// call 比 apply 效率高哦
switch (argCount) {
case 1:
return function(value) {
return func.call(context, value);
};
// The 2-parameter case has been omitted only because no current consumers
// made use of it.
case null:
case 3:
return function(value, index, collection) {
return func.call(context, value, index, collection);
};
case 4:
return function(accumulator, value, index, collection) {
return func.call(context, accumulator, value, index, collection);
};
}
return function() {
return func.apply(context, arguments);
};
};
// 内置的iteratee,用来判断 _.iteratee 是否被重写,闭包,外部获取不到,所以相当于const了
var builtinIteratee;
// An internal function to generate callbacks that can be applied to each
// element in a collection, returning the desired result — either `identity`,
// an arbitrary callback, a property matcher, or a property accessor.
// 无敌万能处理生成回调大法,反正就是不管value传什么,都尽量返回一个合理的回调
// 可以参考这里的解释: #https://juejin.im/entry/59b4a3295188257e7e1153ef
var cb = function(value, context, argCount) {
// 如果 _.iteratee 不等于 builtinIteratee,说明 _.iteratee 被重写,调用被重写后的_.iteratee
if (_.iteratee !== builtinIteratee) return _.iteratee(value, context);
// 如果value为空,那么_.identity,直接返回传入的参数
if (value == null) return _.identity;
// 如果value是函数,那么返回被优化过的函数/迭代器
if (_.isFunction(value)) return optimizeCb(value, context, argCount);
// 如果是对象 | 函数 并且 不是数组,那就返回一个函数,传入一个对象,返回的函数判断传入的对象是否包含attrs的键值对
// 这里传入函数也会被_.isObject判定为对象,不过会被之前的_.isFunction拦截
if (_.isObject(value) && !_.isArray(value)) return _.matcher(value);
// 返回函数,参数为对象,返回 -> 对象[value]
return _.property(value);
};
// External wrapper for our callback generator. Users may customize
// `_.iteratee` if they want additional predicate/iteratee shorthand styles.
// This abstraction hides the internal-only argCount argument.
// 将 _ 的iteratee 存在内置变量上,以供之后判断是否被用户重写
_.iteratee = builtinIteratee = function(value, context) {
return cb(value, context, Infinity);
};
// Similar to ES6's rest param (http://ariya.ofilabs.com/2013/03/es6-and-rest-parameter.html)
// This accumulates the arguments passed into an array, after a given index.
// 将func的第startIndex函数之后的所有参数归为rest参数重新传入
var restArgs = function(func, startIndex) {
// 如果startIndex为空,则替换为func的长度 - 1,因为数组从0开始,不为空则数字化
startIndex = startIndex == null ? func.length - 1 : +startIndex;
return function() {
// 返回函数传进来的参数个数 - 使用者设定的startIndex 并和0比较取最大值
var length = Math.max(arguments.length - startIndex, 0),
rest = Array(length),
index = 0;
for (; index < length; index++) {
// 塞满rest数组,rest内的为startIndex之后的所有参数
rest[index] = arguments[index + startIndex];
}
// 如果startIndex小于3,那么使用call,还是因为call比apply快
switch (startIndex) {
case 0:
return func.call(this, rest);
case 1:
return func.call(this, arguments[0], rest);
case 2:
return func.call(this, arguments[0], arguments[1], rest);
}
// 大于2的时候就处理所有的参数,下面的+1是为了给rest留个位置
var args = Array(startIndex + 1);
for (index = 0; index < startIndex; index++) {
// 除了startIndex之外的,之前的位置按部就班
args[index] = arguments[index];
}
// 放置rest
args[startIndex] = rest;
// 调用apply,将所有参数传入
return func.apply(this, args);
};
};
// An internal function for creating a new object that inherits from another.
// 创建一个继承自prototype的对象
var baseCreate = function(prototype) {
// 如果prototype不是对象,返回一个空对象
if (!_.isObject(prototype)) return {};
// 如果原生支持Object.create,则调用
if (nativeCreate) return nativeCreate(prototype);
// 利用'代孕'构造函数,制作一个继承自prototype的对象
Ctor.prototype = prototype;
var result = new Ctor;
// 手动释放内存,cause闭包引用
Ctor.prototype = null;
return result;
};
// 继承自,这个方法别写了,我自己写着玩的...
_.extendfrom = baseCreate
// 闭包,存储了key,返回一个函数,接受一个obj,不为空则返回obj[key]
// '浅获取'
var shallowProperty = function(key) {
return function(obj) {
return obj == null ? void 0 : obj[key];
};
};
// 当path为数组,且长度为1的时候,返回obj对应的path[0]的值
var deepGet = function(obj, path) {
var length = path.length;
for (var i = 0; i < length; i++) {
// 直接在一开始判断不就得了....
if (obj == null) return void 0;
// 只有path是长度为1的数组的时候才行,不能再最上面判断长度么?大于1就直接返回 void 0
obj = obj[path[i]];
}
// 这里也在一开始直接判断了不好么....
return length ? obj : void 0;
};
// Helper for collection methods to determine whether a collection
// should be iterated as an array or as an object.
// Related: http://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength
// Avoids a very nasty iOS 8 JIT bug on ARM-64. #2094
// Math.pow(2, 53) - 1 是 JavaScript 中能精确表示的最大数字
var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
// 返回一个获取参数(likeArrayList)的.length的函数
var getLength = shallowProperty('length');
// 判断是否是类数组集合,根据length判断
var isArrayLike = function(collection) {
var length = getLength(collection);
return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX;
};
// -------------------------------------------------------------------------------------
// -----------------------------------分界线---------------------------------------------
// -------------------------------------------------------------------------------------
// Collection Functions
// 集合方法部分
// --------------------
// The cornerstone, an `each` implementation, aka `forEach`.
// Handles raw objects in addition to array-likes. Treats all
// sparse array-likes as if they were dense.
// 就相当于forEach
_.each = _.forEach = function(obj, iteratee, context) {
// 当context未定义的时候,直接返回iteratee,否则返回绑定到context的函数
iteratee = optimizeCb(iteratee, context);
var i, length;
// 如果是数组,就正常for循环,并将数组的每一组传入
// iteratee(el, i, list)参数,这就是为啥forEach这种可以获取当前元素,当前索引,被迭代的循环的原因
if (isArrayLike(obj)) {
for (i = 0, length = obj.length; i < length; i++) {
iteratee(obj[i], i, obj);
}
// 如果不是类数组对象,那么获取obj的所有键值,然后和以上同理
} else {
var keys = _.keys(obj);
for (i = 0, length = keys.length; i < length; i++) {
iteratee(obj[keys[i]], keys[i], obj);
}
}
// 这个或许为了链式调用吧
return obj;
};
// Return the results of applying the iteratee to each element.
// 迭代集合中每个元素并调用iteratee,返回一个处理后的新的数组
_.map = _.collect = function(obj, iteratee, context) {
// 优化迭代器
iteratee = cb(iteratee, context);
// 如果不是数组,那么返回key的数组,如果是数组,返回false
var keys = !isArrayLike(obj) && _.keys(obj),
// 获取obj的长度
length = (keys || obj).length,
// 预先定义数组长度,提高性能,以免循环的时候每次都先设置长度,再设置值
results = Array(length);
for (var index = 0; index < length; index++) {
// 数组就是false,不然就是对象
var currentKey = keys ? keys[index] : index;
results[index] = iteratee(obj[currentKey], currentKey, obj);
}
return results;
};
// Create a reducing function iterating left or right.
// 创建一个可左可右的reduce工厂函数
var createReduce = function(dir) {
// Wrap code that reassigns argument variables in a separate function than
// the one that accesses `arguments.length` to avoid a perf hit. (#1991)
var reducer = function(obj, iteratee, memo, initial) {
// 分类 && 根据dir分类
var keys = !isArrayLike(obj) && _.keys(obj),
length = (keys || obj).length,
index = dir > 0 ? 0 : length - 1;
// 如果reduce参数长度大于等于3,那么为false,否则为true
// 进去的话,就说明没有初始值,那么设置初始值为obj的第一个value,然后改变index,再用下面的for循环
if (!initial) {
// 设置memo初始值
memo = obj[keys ? keys[index] : index];
index += dir;
}
for (; index >= 0 && index < length; index += dir) {
var currentKey = keys ? keys[index] : index;
memo = iteratee(memo, obj[currentKey], currentKey, obj);
}
// 返回sum
return memo;
};
return function(obj, iteratee, memo, context) {
// 如果参数长度大于等于3,那么说明传入了初始值
var initial = arguments.length >= 3;
return reducer(obj, optimizeCb(iteratee, context, 4), memo, initial);
};
};
// **Reduce** builds up a single result from a list of values, aka `inject`,
// or `foldl`.
// 正向reduce
_.reduce = _.foldl = _.inject = createReduce(1);
// The right-associative version of reduce, also known as `foldr`.
// 反向reduce
_.reduceRight = _.foldr = createReduce(-1);
// Return the first value which passes a truth test. Aliased as `detect`.
// 寻找/检测,返回第一个通过测试的值
_.find = _.detect = function(obj, predicate, context) {
// 根据类型获取处理OBJ的对应方法
var keyFinder = isArrayLike(obj) ? _.findIndex : _.findKey;
// 测试
var key = keyFinder(obj, predicate, context);
// 有的话就返回
if (key !== void 0 && key !== -1) return obj[key];
};
// Return all the elements that pass a truth test.
// Aliased as `select`.
// 返回所有通过测试的值
_.filter = _.select = function(obj, predicate, context) {
// 结果集合
var results = [];
// 大法
predicate = cb(predicate, context);
// 遍历,通过则吧返回值添加到结果中
_.each(obj, function(value, index, list) {
if (predicate(value, index, list)) results.push(value);
});
return results;
};
// Return all the elements for which a truth test fails.
// 和filter相反
_.reject = function(obj, predicate, context) {
return _.filter(obj, _.negate(cb(predicate)), context);
};
// Determine whether all of the elements match a truth test.
// Aliased as `all`.
// 是不是每一个都通过测试,是则返回true,否则返回false
_.every = _.all = function(obj, predicate, context) {
predicate = cb(predicate, context);
var keys = !isArrayLike(obj) && _.keys(obj),
length = (keys || obj).length;
for (var index = 0; index < length; index++) {
var currentKey = keys ? keys[index] : index;
// 如果断言失败,则取反
if (!predicate(obj[currentKey], currentKey, obj)) return false;
}
return true;
};
// Determine if at least one element in the object matches a truth test.
// Aliased as `any`.
// 如果有一个断言通过,则OK
_.some = _.any = function(obj, predicate, context) {
predicate = cb(predicate, context);
var keys = !isArrayLike(obj) && _.keys(obj),
length = (keys || obj).length;
for (var index = 0; index < length; index++) {
var currentKey = keys ? keys[index] : index;
// 有一个断言通过,返回true
if (predicate(obj[currentKey], currentKey, obj)) return true;
}
return false;
};
// Determine if the array or object contains a given item (using `===`).
// Aliased as `includes` and `include`.
// 判断数组或者对象是否包含给定的item,使用'==='判断
_.contains = _.includes = _.include = function(obj, item, fromIndex, guard) {
if (!isArrayLike(obj)) obj = _.values(obj);
if (typeof fromIndex != 'number' || guard) fromIndex = 0;
return _.indexOf(obj, item, fromIndex) >= 0;
};
// Invoke a method (with arguments) on every item in a collection.
// 调用collection中的方法
// 利用restArgs工具方法,获取包括第三个参数的剩余所有参数
_.invoke = restArgs(function(obj, path, args) {
var contextPath, func;
// 如果是函数
if (_.isFunction(path)) {
func = path;
// 如果是数组
} else if (_.isArray(path)) {
// 获取除最后一个的所有,返回一个数组
contextPath = path.slice(0, -1);
// 单独获取最后一个
path = path[path.length - 1];
}
// map 可以循环任何类数组对象,也就是集合
return _.map(obj, function(context) {
var method = func;
if (!method) {
if (contextPath && contextPath.length) {
// deepGet 只能获取长度为1的数组内容
context = deepGet(context, contextPath);
}
if (context == null) return void 0;
method = context[path];
}
return method == null ? method : method.apply(context, args);
});
});
// Convenience version of a common use case of `map`: fetching a property.
// 获取obj上的key属性
_.pluck = function(obj, key) {
return _.map(obj, _.property(key));
};
// Convenience version of a common use case of `filter`: selecting only objects
// containing specific `key:value` pairs.
// 筛选出数组中包含attrs的键值对
_.where = function(obj, attrs) {
return _.filter(obj, _.matcher(attrs));
};
// Convenience version of a common use case of `find`: getting the first object
// containing specific `key:value` pairs.
// 找到第一个符合的
_.findWhere = function(obj, attrs) {
return _.find(obj, _.matcher(attrs));
};
// Return the maximum element (or element-based computation).
// 获取类数组对象中的最大值
_.max = function(obj, iteratee, context) {
// 创建一个最小的result: -Infinity
var result = -Infinity,
lastComputed = -Infinity,
value, computed;
// 如果没有迭代器,并且obj内不包含数组,也就是包含数字的时候,直接用比result大的值覆盖result
if (iteratee == null || (typeof iteratee == 'number' && typeof obj[0] != 'object') && obj != null) {
obj = isArrayLike(obj) ? obj : _.values(obj);
for (var i = 0, length = obj.length; i < length; i++) {
value = obj[i];
if (value != null && value > result) {
result = value;
}
}
} else {
iteratee = cb(iteratee, context);
_.each(obj, function(v, index, list) {
computed = iteratee(v, index, list);
// && 优先级高于 ||,但是这里的 || 后面的判断是不是有点多余了
if (computed > lastComputed || computed === -Infinity && result === -Infinity) {
result = v;
lastComputed = computed;
}
});
}
return result;
};
// Return the minimum element (or element-based computation).
// 获取类数组对象中的最小值,和_.max相反的逻辑
_.min = function(obj, iteratee, context) {
var result = Infinity,
lastComputed = Infinity,
value, computed;
if (iteratee == null || (typeof iteratee == 'number' && typeof obj[0] != 'object') && obj != null) {
obj = isArrayLike(obj) ? obj : _.values(obj);
for (var i = 0, length = obj.length; i < length; i++) {
value = obj[i];
if (value != null && value < result) {
result = value;
}
}
} else {
iteratee = cb(iteratee, context);
_.each(obj, function(v, index, list) {
computed = iteratee(v, index, list);
if (computed < lastComputed || computed === Infinity && result === Infinity) {
result = v;
lastComputed = computed;
}
});
}
return result;
};
// Shuffle a collection.
//
_.shuffle = function(obj) {
return _.sample(obj, Infinity);
};
// Sample **n** random values from a collection using the modern version of the
// [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle).
// If **n** is not specified, returns a single random element.
// The internal `guard` argument allows it to work with `map`.
// 抽取N/1个样本
_.sample = function(obj, n, guard) {
// 如果没有N或者守护成功,则返回一个
if (n == null || guard) {
if (!isArrayLike(obj)) obj = _.values(obj);
return obj[_.random(obj.length - 1)];
}
var sample = isArrayLike(obj) ? _.clone(obj) : _.values(obj);
var length = getLength(sample);
n = Math.max(Math.min(n, length), 0);
var last = length - 1;
// 随机排序
for (var index = 0; index < n; index++) {
var rand = _.random(index, last);
var temp = sample[index];
sample[index] = sample[rand];
sample[rand] = temp;
}
// 返回前N个
return sample.slice(0, n);
};
// Sort the object's values by a criterion produced by an iteratee.
// 根据iteratee排序
_.sortBy = function(obj, iteratee, context) {
var index = 0;
iteratee = cb(iteratee, context);
// 先用map重置obj的结构,然后用数组的 sort 排序,再用_.pluck获取value的属性值
// 返回该结果
return _.pluck(_.map(obj, function(value, key, list) {
return {
value: value,
index: index++,
criteria: iteratee(value, key, list)
};
}).sort(function(left, right) {
var a = left.criteria;
var b = right.criteria;
if (a !== b) {
if (a > b || a === void 0) return 1;
if (a < b || b === void 0) return -1;
}
return left.index - right.index;
}), 'value');
};
// An internal function used for aggregate "group by" operations.
var group = function(behavior, partition) {
return function(obj, iteratee, context) {
// 初始化result,这里面的数组用于_.partition_使用,result[0]和 result[1]
var result = partition ? [
[],
[]
] : {};
// 处理回调函数
iteratee = cb(iteratee, context);
// 循环
_.each(obj, function(value, index) {
// 判断当前key符合不符合用户给的断言
var key = iteratee(value, index, obj);
// 该行为用于把result分类赋值
behavior(result, value, key);
});
return result;
};
};
// Groups the object's values by a criterion. Pass either a string attribute
// to group by, or a function that returns the criterion.
// 分组
_.groupBy = group(function(result, value, key) {
// 如果result.key有,那么直接push,否则初始化[value]
if (_.has(result, key)) result[key].push(value);
else result[key] = [value];
});
// Indexes the object's values by a criterion, similar to `groupBy`, but for
// when you know that your index values will be unique.
// 当你知道key唯一的时候可以用indexBy,仅仅是behavior函数不同
_.indexBy = group(function(result, value, key) {
// 直接赋值
result[key] = value;
});
// Counts instances of an object that group by a certain criterion. Pass
// either a string attribute to count by, or a function that returns the
// criterion.
// 获取不同分类的数量的计数
_.countBy = group(function(result, value, key) {
if (_.has(result, key)) result[key]++;
else result[key] = 1;
});
// [^\ud800-\udfff] 是普通的 BMP 字符,取反
// [\ud800-\udbff][\udc00-\udfff] 是成对的代理项对
// [\ud800-\udfff] 是未成对的代理项字
var reStrSymbol = /[^\ud800-\udfff]|[\ud800-\udbff][\udc00-\udfff]|[\ud800-\udfff]/g;
// Safely create a real, live array from anything iterable.
// 变成数组
_.toArray = function(obj) {
if (!obj) return [];
if (_.isArray(obj)) return slice.call(obj);
if (_.isString(obj)) {
// Keep surrogate pair characters together
return obj.match(reStrSymbol);
}
if (isArrayLike(obj)) return _.map(obj, _.identity);
return _.values(obj);
};
// Return the number of elements in an object.
// 获取obj的长度
_.size = function(obj) {
if (obj == null) return 0;
return isArrayLike(obj) ? obj.length : _.keys(obj).length;
};
// Split a collection into two arrays: one whose elements all satisfy the given
// predicate, and one whose elements all do not satisfy the predicate.
// 拆成对立的两个数组
_.partition = group(function(result, value, pass) {
result[pass ? 0 : 1].push(value);
}, true);
// -------------------------------------------------------------------------------------
// -----------------------------------分界线---------------------------------------------
// -------------------------------------------------------------------------------------
// Array Functions
// 数组方法部分
// ---------------
// Get the first element of an array. Passing **n** will return the first N
// values in the array. Aliased as `head` and `take`. The **guard** check
// allows it to work with `_.map`.
// 返回数组中第一个或者前N个元素,guard,如果你想捍卫这个函数的尊严,那么将guard设为true
_.first = _.head = _.take = function(array, n, guard) {
if (array == null || array.length < 1) return void 0;
if (n == null || guard) return array[0];
return _.initial(array, array.length - n);
};
// Returns everything but the last entry of the array. Especially useful on
// the arguments object. Passing **n** will return all the values in
// the array, excluding the last N.
// 返回除了最后一个或者最后n个元素,如果guard为真,那么就执行该函数的默认功能,相当于保卫尊严吧,233
_.initial = function(array, n, guard) {
return slice.call(array, 0, Math.max(0, array.length - (n == null || guard ? 1 : n)));
};
// Get the last element of an array. Passing **n** will return the last N
// values in the array.
// 返回数组中的最后n(默认为1)个元素
_.last = function(array, n, guard) {
if (array == null || array.length < 1) return void 0;
if (n == null || guard) return array[array.length - 1];
return _.rest(array, Math.max(0, array.length - n));
};
// Returns everything but the first entry of the array. Aliased as `tail` and `drop`.
// Especially useful on the arguments object. Passing an **n** will return
// the rest N values in the array.
// 返回除了第一个或者除了前N个元素,如果guard为真,那么就执行该函数的默认功能,相当于保卫尊严吧,233
_.rest = _.tail = _.drop = function(array, n, guard) {
return slice.call(array, n == null || guard ? 1 : n);
};
// Trim out all falsy values from an array.
// 去除所有false值,false, null, 0, "", undefined 和 NaN 都是false值
_.compact = function(array) {
return _.filter(array, Boolean);
};
// Internal implementation of a recursive `flatten` function.
// 内部扁平化数组方法
// strict 通常配合shallow一起使用,
var flatten = function(input, shallow, strict, output) {
output = output || [];
var idx = output.length;
for (var i = 0, length = getLength(input); i < length; i++) {
var value = input[i];
// 如果是类数组并且是(数组或者arguments)
if (isArrayLike(value) && (_.isArray(value) || _.isArguments(value))) {
// Flatten current level of array or arguments object.
// 如果为浅,那么直接赋值,不然则递归
if (shallow) {
var j = 0,
len = value.length;
while (j < len) output[idx++] = value[j++];
} else {
flatten(value, shallow, strict, output);
// 递归之后output长度改变,所以idx需要重新赋值
idx = output.length;
}
// 允不允许将非数组的值,添加到结果数组中
} else if (!strict) {
// 直接复制
output[idx++] = value;
}
}
return output;
};
// Flatten out an array, either recursively (by default), or just one level.
// 扁平化数组,shallow控制深浅,默认为全部扁平化
_.flatten = function(array, shallow) {
return flatten(array, shallow, false);
};
// Return a version of the array that does not contain the specified value(s).
// 返回一个不包含某些值的数组版本,就是比_.difference多了一步参数的处理
_.without = restArgs(function(array, otherArrays) {
return _.difference(array, otherArrays);
});
// Produce a duplicate-free version of the array. If the array has already
// been sorted, you have the option of using a faster algorithm.
// Aliased as `unique`.
// 数组去重
_.uniq = _.unique = function(array, isSorted, iteratee, context) {
// 如果不是布尔值,那么重构参数
if (!_.isBoolean(isSorted)) {
context = iteratee;
iteratee = isSorted;
isSorted = false;
}
// 处理迭代器
if (iteratee != null) iteratee = cb(iteratee, context);
// 结果数组
var result = [];
// 存储器?
var seen = [];
for (var i = 0, length = getLength(array); i < length; i++) {
var value = array[i],
computed = iteratee ? iteratee(value, i, array) : value;
// 如果已经被排序,如果是有序数组,那么直接用后一个和前一个判断,这样更快
if (isSorted) {
// !== 优先级大于 ||
// 如果第一个元素 || seen 不等于 computed
if (!i || seen !== computed) result.push(value);
seen = computed;
// 如果有迭代器
} else if (iteratee) {
// 判断seen中有没有结果,没有才push
if (!_.contains(seen, computed)) {
// 再更新
seen.push(computed);
// push到结果数组
result.push(value);
}
// 直接判断有没有
} else if (!_.contains(result, value)) {
result.push(value);
}
}
return result;
};
// Produce an array that contains the union: each distinct element from all of
// the passed-in arrays.
// 先处理接收到的参数,杂糅到一起,然后扁平化,然后去重
_.union = restArgs(function(arrays) {
// 仅仅一层
return _.uniq(flatten(arrays, true, true));
});
// Produce an array that contains every item shared between all the
// passed-in arrays.
// 取并集
_.intersection = function(array) {
// 结果数组
var result = [];
// 当前函数的所有参数的长度
var argsLength = arguments.length;
// for循环
for (var i = 0, length = getLength(array); i < length; i++) {
var item = array[i];
// 如果结果包含item,进入下一个循环
if (_.contains(result, item)) continue;
// 循环其他参数
var j;
for (j = 1; j < argsLength; j++) {
// 如果其他参数不包含item,那么退出该循环,导致j !== argsLength
if (!_.contains(arguments[j], item)) break;
}
// 判断时候可以push到结果数组
if (j === argsLength) result.push(item);
}
return result;
};
// Take the difference between one array and a number of other arrays.
// Only the elements present in just the first array will remain.
// 取数组中的不同,只保留第一个数组的值 => 取非
// rest化参数
_.difference = restArgs(function(array, rest) {
// 扁平化数组
rest = flatten(rest, true, true);
// 过滤
return _.filter(array, function(value) {
// 不包含的才是我想要的
return !_.contains(rest, value);
});
});
// Complement of _.zip. Unzip accepts an array of arrays and groups
// each array's elements on shared indices.
// 将二维数组的值分离,返回二维数组,index上的位置为原数组每一个子数组上index的合集
_.unzip = function(array) {
// 获取长度最长的二维数组的长度
var length = array && _.max(array, getLength).length || 0;
// 初始化结果数组,加快速度
var result = Array(length);
// 循环,并获取每个数组的index对应的,注意:_.pluck中调用了map
for (var index = 0; index < length; index++) {
result[index] = _.pluck(array, index);
}
return result;
};
// Zip together multiple lists into a single array -- elements that share
// an index go together.
// 将每个数组的相同位置的值合并到一起
_.zip = restArgs(_.unzip);
// Converts lists into objects. Pass either a single array of `[key, value]`
// pairs, or two parallel arrays of the same length -- one of keys, and one of
// the corresponding values. Passing by pairs is the reverse of _.pairs.
// 将数组转为对象
_.object = function(list, values) {
var result = {};
for (var i = 0, length = getLength(list); i < length; i++) {
// list[i]为key,values[i]为value
if (values) {
result[list[i]] = values[i];
// 只传入二维数组的时候,[0]为key,[1]为value
} else {
result[list[i][0]] = list[i][1];
}
}
return result;
};
// Generator function to create the findIndex and findLastIndex functions.
var createPredicateIndexFinder = function(dir) {
return function(array, predicate, context) {
// 生成断言函数
predicate = cb(predicate, context);
// 获取array的长度
var length = getLength(array);
// 根据方向设置起始点
var index = dir > 0 ? 0 : length - 1;
// 这里的for循环的条件index >= 0 && index < length,具有两面性
for (; index >= 0 && index < length; index += dir) {
// 通过断言函数,返回当前索引
if (predicate(array[index], index, array)) return index;
}
// 否则就找不到,就返回-1
return -1;
};
};
// Returns the first index on an array-like that passes a predicate test.
// 找到第一个通过测试的值得索引,正向开始找和反向开始找,返回的索引都是正向开始算的
_.findIndex = createPredicateIndexFinder(1);
_.findLastIndex = createPredicateIndexFinder(-1);
// Use a comparator function to figure out the smallest index at which
// an object should be inserted so as to maintain order. Uses binary search.
// 找obj在 array的位置
// 该方法不允许有NaN,因为NaN无法和任何数字做比较
//
// 前提:数组为有序
// 优点:查找参数少,查找速度快
// 缺点:要求待查表为有序表
// 思路:先找重点位置,与aim比,等于则直接返回,大于取前半部分,小于取后半部分
// 循环以上操作,下面的二分是一个变种
_.sortedIndex = function(array, obj, iteratee, context) {
// 生成迭代器,仅有一个value参数,这里iteratee为空,所以返回identity,直接返回value
iteratee = cb(iteratee, context, 1);
// 返回obj的值
var value = iteratee(obj);
var low = 0,
high = getLength(array);
// 变种的二分算法的核心逻辑
// 不是找相等
while (low < high) {
var mid = Math.floor((low + high) / 2);
if (iteratee(array[mid]) < value) low = mid + 1;
else high = mid;
}
return low;
};
// Generator function to create the indexOf and lastIndexOf functions.
// 生成一个创造indexOf/lastIndexOf的函数
var createIndexFinder = function(dir, predicateFind, sortedIndex) {
// idx会被赋上找到的item的索引,并作为结果返回
return function(array, item, idx) {
var i = 0,
length = getLength(array); // 获取 array 的长度
// 如果idx是数字,那么进入该if
// idx为从哪里开始找
if (typeof idx == 'number') {
// 根据查找方向走
if (dir > 0) {
// 如果idx大于等于0,那么i = idx
// 否则取idx + length 和 i 中较大的一个
// i位起始位置,也就是从哪里开始找
i = idx >= 0 ? idx : Math.max(idx + length, i);
} else {
// 重新设置length
// 如果idx大于等于0,那么取idx + 1和length中较小的一个值
// 否则取idx + length + 1
// 因为idx是数组的下标,所以数组的长度为当前下标 + 1,所以需要 + 1
length = idx >= 0 ? Math.min(idx + 1, length) : idx + length + 1;
}
// 如果 idx 为Number以外的类型的时候