-
Notifications
You must be signed in to change notification settings - Fork 0
/
2014.08.24.Underscore-1.6.0.js
executable file
·2162 lines (2007 loc) · 103 KB
/
2014.08.24.Underscore-1.6.0.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.6.0
// http://underscorejs.org
// (c) 2009-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
// Underscore may be freely distributed under the MIT license.
/**
* 两个空格的代码缩进 = =
*
* @mark
* @problem
* @solve ==> problem solve / mark
*
* @1.01 - 1.38: 2014.07.04
* @2.01 - 2.14: 2014.07.05
* @3.01 - 3.39: 2014.07.07
* @4.01 - 4.07: 2014.07.08
* @5.01 - 5.11: 2014.07.09
* @6.01 - 6.31: 2014.07.10
* @7.01 - 7.09: 2014.07.11
*
* @8.01 - 8.26: 2014.07.16
* @9.01 - 9.17: 2014.07.17
*
* @10.01 - 10.08: 2014.07.18
*
* 擦 硬是中间浪费一个月时间 一年也才12个月呢 靠
*
* @11.01 - 11.15: 2014.08.13
* @12.01 - 12.10: 2014.08.14
*
* 周一 没什么事 总算有些时间看点东西 只剩下模板部分了 好开森
* @13.01 - 13.33: 2014.08.18
*
* @14.01 - 14.10: 2014.08.19
* @15.01 - 15.07: 2014.08.24
*
* 于此,undersore.js 1000来行的代码阅读完毕。
* 涨了不少见识,ありがとう.
*
* Commented By Xaber
*
*/
(function() {
// Baseline setup
// --------------
// Establish the root object, `window` in the browser, or `exports` on the server.
// @1.01 暂未接触 node 场景 以window为例
var root = this;
// Save the previous value of the `_` variable.
// @1.02 防止冲突
var previousUnderscore = root._;
// Establish the object that gets returned to break out of a loop iteration.
// @1.03 渣翻译:建立一个对象 用来获得返回值来跳出一个循环
var breaker = {};
// Save bytes in the minified (but not gzipped) version:
// @1.04 保存引用 为避免访问这些方法时,一次又一次的访问相应构造函数的原型上的方法
var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
// Create quick reference variables for speed access to core prototypes.
var
push = ArrayProto.push,
slice = ArrayProto.slice,
concat = ArrayProto.concat,
toString = ObjProto.toString,
hasOwnProperty = ObjProto.hasOwnProperty;
// All **ECMAScript 5** native function implementations that we hope to use
// are declared here.
// @1.05 ES 5的一些快捷方法
var
nativeForEach = ArrayProto.forEach,
nativeMap = ArrayProto.map,
nativeReduce = ArrayProto.reduce,
nativeReduceRight = ArrayProto.reduceRight,
nativeFilter = ArrayProto.filter,
nativeEvery = ArrayProto.every,
nativeSome = ArrayProto.some,
nativeIndexOf = ArrayProto.indexOf,
nativeLastIndexOf = ArrayProto.lastIndexOf,
nativeIsArray = Array.isArray,
nativeKeys = Object.keys,
nativeBind = FuncProto.bind;
// Create a safe reference to the Underscore object for use below.
// @1.06 闭包中的_
var _ = function(obj) {
// @1.07 如果obj 是 _ 的实例 直接返回
if (obj instanceof _) return obj;
// @1.08 安全模式 直接在内部使用return new 的形式
// 防止了 遗忘new 或者 以函数形式调用的情况 估计只是想偷懒罢了
if (!(this instanceof _)) return new _(obj);
// @1.09 返回的实例 包含获得一个_wrapped属性 指向引用的obj对象
this._wrapped = obj;
};
// Export the Underscore object for **Node.js**, with
// backwards-compatibility for the old `require()` API. If we're in
// the browser, add `_` as a global object via a string identifier,
// for Closure Compiler "advanced" mode.
// @1.10 node 的场景
if (typeof exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) {
exports = module.exports = _;
}
exports._ = _;
} else {
// @1.11 window的场景 此时原来的window._已被覆盖
root._ = _;
}
// Current version.
_.VERSION = '1.6.0';
// Collection Functions
// --------------------
// The cornerstone, an `each` implementation, aka `forEach`.
// Handles objects with the built-in `forEach`, arrays, and raw objects.
// Delegates to **ECMAScript 5**'s native `forEach` if available.
// @1.12 变量保存快捷方式 而不是多次获取对象属性
var each = _.each = _.forEach = function(obj, iterator, context) {
// @1.13 obj 为 undefined 或 null 的场景
if (obj == null) return obj;
// @1.14 如果ES 5 forEach存在 并且 obj.forEach === nativeForEach 即表明obj 是一个数组
if (nativeForEach && obj.forEach === nativeForEach) {
obj.forEach(iterator, context);
// @1.15 underscore中很多情况都用了这个 + 号
// 一般情况下 都是 默认类型转换成 boolean
// @mark 暂不理解这种强制类型转换成 number / NaN 的场景
// 字符串是可以进入这个逻辑的 另外也发现个访问字符串相应位置的简便方法
// '0123456'[0] ==> 0
} else if (obj.length === +obj.length) {
// @1.16 length 保存obj.length 为避免在forEach过程中 动态改变 obj内部元素的数量 而导致无限循环 或其他一些情况 如删除了某个等等
// 另外,也避免了在for循环中 每一次 i < obj.length 判断访问一次obj的length属性
for (var i = 0, length = obj.length; i < length; i++) {
// @1.17 在函数的循环上打主意 循环中 函数内部 一个返回值 如果 === breaker 跳出循环
// iterator 函数接受三个参数 value key obj
// 而这部分内容,Array.prototype.forEach内部是没有这样的功能的
// 也就是说 高版本浏览器反而没有这样的特权 但是因为是浏览器内部实现 估计性能快些 这样倒是平均了 呵
// @problem: 但是breaker 是一个对象 这就有点奇怪了 因为无论 iterator return出什么 它永远不会全等于闭包中的一个对象
// 如果是 一个 true 或者 !!breaker 或者 将全等换成== 或者去掉 全等的判定 都可以break 所以这里感觉有点问题
//
// @solve: 只要这部分breaker 只要是作为闭包的存在 break 也仅仅用于 underscore中的相应方法(如 _.some / _.every)调用 each时候在内部break的情况下
// 传递的参数iterator 内部return 出breaker 即 跳出了循环 2014.07.07 10:00
// 但是 为照顾 _.some / _.every 普通的循环 每次都执行了这个无用的判断
if (iterator.call(context, obj[i], i, obj) === breaker) return;
}
} else {
// @ 1.22 获得keyLists _.keys()内部调用 _.has 剔除了原型上的属性方法
// @ mark 如果是到这步 即each 一个object 循环执行两次.. _.keys中循环了一次 外面又一个for循环..
var keys = _.keys(obj);
for (var i = 0, length = keys.length; i < length; i++) {
// @1.23 这部分 object each的情况 只要iterator return true 即跳出循环
if (iterator.call(context, obj[keys[i]], keys[i], obj) === breaker) return;
}
}
// @1.24 _.each 原来还有返回值呢
return obj;
};
// Return the results of applying the iterator to each element.
// Delegates to **ECMAScript 5**'s native `map` if available.
_.map = _.collect = function(obj, iterator, context) {
var results = [];
if (obj == null) return results;
// @1.25 Array.prototype.map obj.map === nativeMap 即表明是一个数组
// map 即 映射 最后会返回一个新数组 而对应的数组元素则是 传入的函数 调用得到的返回值
if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
// @1.26 使用外部函数each list即obj
// 这里有个有意思的地方 因为 each 传入的函数并不是iterator 而是function(value, index, list) {results.push(iterator.call(context, value, index, list)); }
// 它是没有返回值的 所以也就不会中途被return 出来
// 而通过循环、push results的每一项也就获得了相应的函数调用返回值
each(obj, function(value, index, list) {
results.push(iterator.call(context, value, index, list));
});
return results;
};
var reduceError = 'Reduce of empty array with no initial value';
// **Reduce** builds up a single result from a list of values, aka `inject`,
// or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
// @ 2.08 注意 reduce中 Array.prototype.reduce 也是一样 如果value 是object等 传递的都是引用
_.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
var initial = arguments.length > 2;
if (obj == null) obj = [];
// @ 1.27 Array.prototype.reduce
if (nativeReduce && obj.reduce === nativeReduce) {
// @ 2.01 如果传递 context / thisobj 重写iterator为绑定了context的函数
// iterator 参数 previous, current, index, arr
// 在没有初始值memo的情况下 index 从 1 开始 函数执行 arr.length - 1 次
// 初始值 memo 存在的情况下 index 从 0 开始 函数执行 arr.length 次
if (context) iterator = _.bind(iterator, context);
// @ 2.02 memo 则是作为初始值的存在
return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
}
// @ 2.03 可接受 obj对象 的情况 因为用了each
each(obj, function(value, index, list) {
// @ 2.04 initial 代表是否拥有 memo 参数 这里表示不存在的情况下 把初始值设置为循环中 数组 / obj 第一个value
if (!initial) {
memo = value;
// @ 2.05 并标记已获得初始值 这是初始值不存在情况下的第一次循环 那么下一次 index 也就从1开始了
initial = true;
} else {
// @ 2.06 之后的循环都进入这个else逻辑 每一次循环 memo 值 更新为 iterator 函数中 return 出的新值
// 而如果初始值 memo 存在的情况下 第一次循环即进入这个 else 逻辑 index 从 0 开始
memo = iterator.call(context, memo, value, index, list);
}
});
// @ 2.07 如果连上面的循环都没进入(循环中改变了initial = true) 表明 obj参数 错误
// 其实 string 和 object情况都可
if (!initial) throw new TypeError(reduceError);
return memo;
};
// The right-associative version of reduce, also known as `foldr`.
// Delegates to **ECMAScript 5**'s native `reduceRight` if available.
// @ 2.09 和_.reduce基本类似
_.reduceRight = _.foldr = function(obj, iterator, memo, context) {
var initial = arguments.length > 2;
if (obj == null) obj = [];
if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
if (context) iterator = _.bind(iterator, context);
return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
}
// @ 2.10 arr / string 的情况
var length = obj.length;
// @ 2.11 obj的情况
if (length !== +length) {
var keys = _.keys(obj);
length = keys.length;
}
each(obj, function(value, index, list) {
// @ 2.12 index 从后倒数 前自减
index = keys ? keys[--length] : --length;
// @ 2.13 如果没有初始值memo 第一次循环 也就没有调用initial函数
if (!initial) {
memo = obj[index];
initial = true;
} else {
// @ 2.14 获得的相应的参数 都是从后开始的
memo = iterator.call(context, memo, obj[index], index, list);
}
});
if (!initial) throw new TypeError(reduceError);
return memo;
};
// Return the first value which passes a truth test. Aliased as `detect`.
_.find = _.detect = function(obj, predicate, context) {
var result;
// @ 3.06 内部调用_.any / _.some 闭包调用predicate 函数
// 如果 返回值 == true 指定 result 为 value 并 返回 true
// 而any 内部又根据函数的返回值 == true 返回 闭包中的breaker
// 故:如果是想中间跳出循环而不需要返回值的话请用 _.any 或 _.some
any(obj, function(value, index, list) {
if (predicate.call(context, value, index, list)) {
result = value;
return true;
}
});
return result;
};
// Return all the elements that pass a truth test.
// Delegates to **ECMAScript 5**'s native `filter` if available.
// Aliased as `select`.
// @ 3.07 返回值 为 一个新数组 保存的是符合 func 筛选规则的 value
// 需要注意的是 如果value 是引用值 直接保存的引用 而不是复制
_.filter = _.select = function(obj, predicate, context) {
var results = [];
if (obj == null) return results;
if (nativeFilter && obj.filter === nativeFilter) return obj.filter(predicate, context);
each(obj, function(value, index, list) {
if (predicate.call(context, value, index, list)) results.push(value);
});
return results;
};
// Return all the elements for which a truth test fails.
// @ 3.08 利用 filter 返回的数组 正好是 不符合规则的 的 valueList
_.reject = function(obj, predicate, context) {
return _.filter(obj, function(value, index, list) {
return !predicate.call(context, value, index, list);
}, context);
};
// Determine whether all of the elements match a truth test.
// Delegates to **ECMAScript 5**'s native `every` if available.
// Aliased as `all`.
// @ 3.09 正好和 _.some 相反 必须全部符合过滤条件才返回 true
_.every = _.all = function(obj, predicate, context) {
predicate || (predicate = _.identity);
var result = true;
if (obj == null) return result;
if (nativeEvery && obj.every === nativeEvery) return obj.every(predicate, context);
each(obj, function(value, index, list) {
// @ 3.10 循环遍历 只要 predicate函数 一次返回值 != true 跳出循环 返回 !!result
// @ 利用了运算符的优先级 每次循环 result 获得了 predicate 的返回值
if (!(result = result && predicate.call(context, value, index, list))) return breaker;
});
return !!result;
};
// Determine if at least one element in the object matches a truth test.
// Delegates to **ECMAScript 5**'s native `some` if available.
// Aliased as `any`.
// @ 3.02 与Array.prototype.some 类似 但有区别
// nativeSome 在未传递func的情况下 会报错 ==> undefined is not a function
// 但underscore 在为传递func时内部指定了函数,故在未传递情况下 返回 ture
// 接受obj func thisobj作为参数
var any = _.some = _.any = function(obj, predicate, context) {
// @ 3.03 如果函数未传递 指定函数为_.identity 即 function (value) { return value; }
predicate || (predicate = _.identity);
var result = false;
if (obj == null) return result;
if (nativeSome && obj.some === nativeSome) return obj.some(predicate, context);
each(obj, function(value, index, list) {
// @ 3.04 predicate 函数接受 三个参数 value, index, arr / obj / string
// @mark 为何要在判断前多加一个result 因为 只要函数调用的返回值 == true
// 即 return breaker 跳出了循环
if (result || (result = predicate.call(context, value, index, list))) return breaker;
});
// @ 3.05 双重否定转换为 true / false
return !!result;
};
// Determine if the array or object contains a given value (using `===`).
// Aliased as `include`.
_.contains = _.include = function(obj, target) {
if (obj == null) return false;
// @ 3.11 Array.prototype.indexOf
if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
// @ 3.12 调用_.any / _.some 内部使用的都是全等判断 故对引用类型的值是无效的
// 并且传递的函数 直接 使用return value === target; 也就确定了 函数的返回值为 true / false
return any(obj, function(value) {
return value === target;
});
};
// Invoke a method (with arguments) on every item in a collection.
// @ 3.14
// 接受两种情况:
// situation one : method 为函数
// 返回一个绑定了this 分别为 obj相应value 的函数调用完成的返回值的 数组
// situation two : method 为 obj相应value上的方法名(必须是方法名)
// 对应的方法绑定的 this 为对应的value 内部调用 _.map 将调用完成后的返回值 push进数组
// 返回值为 内部_.map 处理后的数组
// 例如: _.invoke([[5, 1, 7], [3, 2, 1]], Array.prototype.sort); / _.invoke([[5, 1, 7], [3, 2, 1]], 'sort');
// 例如: _.invoke( [{ aa : 1, a : function () { return this.aa; }}, { bb : 2, a : function () { return this.bb } }], 'a');
_.invoke = function(obj, method) {
var args = slice.call(arguments, 2);
var isFunc = _.isFunction(method);
return _.map(obj, function(value) {
return (isFunc ? method : value[method]).apply(value, args);
});
};
// Convenience version of a common use case of `map`: fetching a property.
// @ 3.16 通过内部调用_.property(key) 获得 一个获取obj 上 对应key 的value 的函数
// 传递obj 和 相应函数 给_.map 循环遍历执行 返回一个数组 每个数组元素都是 obj 中元素(arr / obj) 相应key 的value
// 实例见: http://underscorejs.org/#pluck
// _.pluck([{name: 'moe', age: 40}, {name: 'larry', age: 50}, {name: 'curly', age: 60}], 'name');
_.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.
_.where = function(obj, attrs) {
// @ 3.18 利用_.matches(attrs) 返回一个提供obj参数的函数 传递给_.filter
// 那么内部obj 参数位置接受的 其实是_.filter 内部传入的 value
// 那么 value 应该是一个obj / obj / string 类型 而比对的情况 则是value 和 attrs的比对
// 如果value 拥有和 attrs 相同的属性值
// _.filter 内部将符合的value push 进 result 数组 返回
// 再经由这个return 返回出来
return _.filter(obj, _.matches(attrs));
};
// Convenience version of a common use case of `find`: getting the first object
// containing specific `key:value` pairs.
_.findWhere = function(obj, attrs) {
// @ 3.19 类似_.where 但内部循环 使用的是_.find 而_.find 内部使用的是_.some / _.any
// 即 内部只要符合一个条件即跳出循环 并返回相应的value值 (因为 是和attrs 的比对)
// value 是一个引用值
return _.find(obj, _.matches(attrs));
};
// Return the maximum element or (element-based computation).
// Can't optimize arrays of integers longer than 65,535 elements.
// See [WebKit Bug 80797](https://bugs.webkit.org/show_bug.cgi?id=80797)
_.max = function(obj, iterator, context) {
// @ 3.21 在不传递函数参数的情况下 obj 必须是一个arr 且元素必须是简单值 且 typeof Number(value) === 'number' (经过测试)
if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
// @3.22 利用Math对象上的max 方法 apply 调用传递 arr 返回最大的数值
return Math.max.apply(Math, obj);
}
var result = -Infinity, lastComputed = -Infinity;
each(obj, function(value, index, list) {
// @3.23 另一种情况 接受一个函数 来对value 进行操作 比对
// 而作为 大小的 比对 iterator需要返回值 并且最好typeof Number(value) === 'number'
// 否则 大小的比对都是一个false 最后会返回 -Infinity
var computed = iterator ? iterator.call(context, value, index, list) : value;
if (computed > lastComputed) {
result = value;
lastComputed = computed;
}
});
return result;
};
// Return the minimum element (or element-based computation).
// @ 3.24 同 _.max
_.min = function(obj, iterator, context) {
if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
return Math.min.apply(Math, obj);
}
var result = Infinity, lastComputed = Infinity;
each(obj, function(value, index, list) {
var computed = iterator ? iterator.call(context, value, index, list) : value;
if (computed < lastComputed) {
result = value;
lastComputed = computed;
}
});
return result;
};
// Shuffle an array, using the modern version of the
// [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle).
// @ 3.27 用于打乱 obj 的顺序 获得 一个打乱的arr(valueList)
_.shuffle = function(obj) {
var rand;
var index = 0;
var shuffled = [];
each(obj, function(value) {
// @ 3.28 保存 获得的随机数
// 不用key 是为考虑 obj 为 object的情况
rand = _.random(index++);
// @ 3.29 有点意思 每次循环 index += 1
// 当循环到最后 index 自加到 arr.length / string.leng / _.keys(obj).length
// shuffled数组也就获得了对应长度的元素
// 而每一次先将对应位置 设置为已经 / 或 未保存过的随机位置值
// 看了下上面的链接 原来叫做洗牌算法
// 其实这里应该是叫做一种 Knuth-Durstenfeld Shuffle 的升级版 算法的时间复杂度为O(n) 空间复杂度为O(1)
// 每次从未处理的数据中随机取出一个数字,然后把该数字放在数组的尾部,即数组尾部存放的是已经处理过的数字
shuffled[index - 1] = shuffled[rand];
// @ 3.30 随后将随机位置设置为 obj[index - 1]的value
shuffled[rand] = value;
});
return shuffled;
};
// Sample **n** random values from a collection.
// If **n** is not specified, returns a single random element.
// The internal `guard` argument allows it to work with `map`.
_.sample = function(obj, n, guard) {
// @ 3.32 @mark 真不懂得这guard 是啥用的
if (n == null || guard) {
// @ 3.33 obj的情况
if (obj.length !== +obj.length) obj = _.values(obj);
// @ 3.34 返回随机的一个value
return obj[_.random(obj.length - 1)];
}
// @ 3.35 n (数量) 传入的情况下 调用_.shuffle获得随机排列后的数组 再用slice 截取
return _.shuffle(obj).slice(0, Math.max(0, n));
};
// An internal function to generate lookup iterators.
// @ 3.36 闭包中的函数 返回一个函数
var lookupIterator = function(value) {
// @ 3.37 不传递参数情况下 返回 function (value) { return value; }
if (value == null) return _.identity;
// @ 3.38 value 为func 返回func 的引用
if (_.isFunction(value)) return value;
// @ 3.39 否则返回 一个接受 obj参数的 func ==> function (obj) { return obj[value]; }
return _.property(value);
};
// Sort the object's values by a criterion produced by an iterator.
_.sortBy = function(obj, iterator, context) {
// @ 4.01 貌似比较复杂的一个函数 印象中也挺讨厌各种排序的
// 通过 lookupIterator 获得一个对应的函数 iterator 可接受的: undefined / func / key(string)
iterator = lookupIterator(iterator);
// @ 4.02 双重_.map (_.pluck 内部还有一层map调用)
// 第一重 通过_.map返回 一个 a 函数调用完返回的 对象数组
//
// 参数位置的iterator 获得的对应criteria属性值
// undefined value
// func func return value
// key(string) obj[key] return value
// 之后再经过sort方法 再排序加工
return _.pluck(_.map(obj, function(value, index, list) { // @4.03 暂且称此函数为a
return {
value: value,
index: index,
criteria: iterator.call(context, value, index, list)
};
}).sort(function(left, right) {
var a = left.criteria;
var b = right.criteria;
// @ 4.04 先比对criteria 即iterator返回值
if (a !== b) {
// @ 4.05 sort 接受的函数 可接收两个参数 分别是 left 和 right
// 函数的返回值 > 0 表明需要 更换位置
// 含糊的返回值 < 0 表明不需要更换位置
// 通常情况下 一般只传递 function (left, right) { return left - right; }
// 表示 如果 left > right 需要调换位置 即 从小到大
// 如果 a > b 或a === undefined 返回 1 表示 undefined 排最前 然后按照 iterator 返回值 从小到大排序
if (a > b || a === void 0) return 1;
if (a < b || b === void 0) return -1;
}
// @ 4.06 如果a === b 即返回值相等
// 比对 index 从小到大 即出现的顺序排序
return left.index - right.index;
// @ 4.07 第二重 _.map
// _.pluck 接受value 参数 获得一个 function (obj) { return obj['value']}的函数 传递给_.map
// 其实就是再调用一次map 循环 上面 排序完的 对象数组
// 获得 对象数组元素 的 value push 进result 数组再最后返回 排序完的 arr (value list)
}), 'value');
};
// An internal function used for aggregate "group by" operations.
// @ 5.01 一个helper函数 参数位置传递一个函数 返回一个函数
// 函数的调用结果是 返回一个 result 对象
var group = function(behavior) {
return function(obj, iterator, context) {
var result = {};
// @ 5.02 返回的函数 iterator参数位置 传递的参数 再经由闭包中的lookupIterator 处理出一个函数
iterator = lookupIterator(iterator);
each(obj, function(value, index) {
// @ 5.03 循环遍历 将处理后的iterator函数的返回值 作为key
var key = iterator.call(context, value, index, obj);
// @ 5.04 再经由闭包 调用 group 传递的behavior 函数 传递 result(引用传递) key value
behavior(result, key, value);
});
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.
// @ 5.05 调用 group函数 返回一个函数
// 内部 在每一次循环时 调用 这个behavior匿名函数
// 官方例子1 _.groupBy([1.3, 2.1, 2.4], function(num){ return Math.floor(num); });
// 循环调用的结果 获得的key 依次为 1、2、2
// 在调用 behavior 时 result 依次更新为 { 1: [1.3] }、{ 1: [1.3] 2: [2.1]}、{ 1: [1.3] 2: [2.1, 2.4] }
// 官方例子2 _.groupBy(['one', 'two', 'three'], 'length');
// lookupIterator 函数经由 lookupIterator 处理为 function (obj) {return obj['length']; }
// 循环调用的结果 获得的key 依次为 3、3、5
// 其余同理
// underscore 的函数重用率真不是一般的高 闭包的使用 有点登峰造极的感觉
_.groupBy = group(function(result, key, value) {
_.has(result, key) ? result[key].push(value) : 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.
// @ 5.06 这里需要注意的是 要确保 lookupIterator 处理后的 iterator 函数调用返回结果(key)不重复 否则result[key] 会被重写
_.indexBy = group(function(result, key, value) {
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.
// @ 5.07 调用返回的result 的属性为 对应的iterator 的返回值(即key)
// result[key] 则为调用结果 出现的次数
_.countBy = group(function(result, key) {
_.has(result, key) ? result[key]++ : result[key] = 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.
_.sortedIndex = function(array, obj, iterator, context) {
// @ 5.08 获得 正确的 iterator 函数 func / function (obj) {return obj;} / function (obj) { return obj[ iterator ]; // 此处iterator 为string }
iterator = lookupIterator(iterator);
// @ 5.09 obj参数 只是起一个中途的作用 用于iterator 返回结果
var value = iterator.call(context, obj);
var low = 0, high = array.length;
while (low < high) {
// @ 5.10 右移一位 学到了 省去了 Math.floor( (low + high) / 2 )这样的操作 而且速度更快 = =
var mid = (low + high) >>> 1;
// @ 5.11 循环中 (类似中分)设置 low 和 high
// low = mid + 1 的操作 一方面 剔除了 mid 这个选项 另一方面 也为跳出循环
// @mark 另一方面 有点问题 就是 当iterator.call(context, array[mid]) === value 时 执行的是high 依旧进入循环
// @solve 这是官方对这个方法的释义:
// Uses a binary search to determine the index at which the value should be inserted into the list in order to maintain the list's sorted order.
// If an iterator function is provided, it will be used to compute the sort ranking of each value, including the value you pass.
// 这个方法其实是局限的,用途是拿来获取obj 想要 插入 array 的位置
// 这也就代表着 array 必须是以一定规则排序好的 且必须从小到大!!!
// 来看个例子 _.sortedIndex([10, 20, 30, 40, 50, 60], 40);
// low 0 high 6 mid 3
// low 0 high 3 mid 1
// low 2 high 3 mid 2
// 循环中 只要 iterator.call(context, array[mid]) < value 一直执行 low = mid + 1 操作 直到 mid + 1 === high 跳出循环
// 而当 iterator.call(context, array[mid]) >= value 则只是将 high 设置为mid 再进入循环
// 这样也确实保证了 获得的 low 是按照次序最后一个小于 value 的位置
// 而倒序的情况 就不行了
// 例如_.sortedIndex([8, 7, 6, 5, 4, 3, 2, 1], 3);
// low 0 high 8 mid 4
// low 0 high 4 mid 2
// low 0 high 2 mid 1
// low 0 high 1 mid 0
// 因arr[4] > value high被设置为 4
// 再循环下去 都是从 0 - 4 的元素 依次做的操作 循环到最后low 还是为0
// 再例如
// _.sortedIndex([4, 3, 2, 1], 3);
// low 0 high 4 mid 2
// low 3 high 4 mid 3
// 最后返回 4
iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid;
}
return low;
};
// Safely create a real, live array from anything iterable.
_.toArray = function(obj) {
if (!obj) return [];
// @ 6.01 复制一份
if (_.isArray(obj)) return slice.call(obj);
// @ 6.02 string 利用 _.map
if (obj.length === +obj.length) return _.map(obj, _.identity);
// @ 6.03 object
return _.values(obj);
};
// Return the number of elements in an object.
_.size = function(obj) {
if (obj == null) return 0;
// @ 6.04 array / string object的情况 _.keys 内部 _.has 剔除原型链上属性 最后返回keyList的length
return (obj.length === +obj.length) ? obj.length : _.keys(obj).length;
};
// 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`.
// @ 6.05 guard == true || n == null ==> return arr[0] / string[0]
// n 大于 0 的情况下 返回从首个位置开始 n个数量 的元素数组 / string
_.first = _.head = _.take = function(array, n, guard) {
if (array == null) return void 0;
if ((n == null) || guard) return array[0];
if (n < 0) return [];
return slice.call(array, 0, 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. The **guard** check allows it to work with
// `_.map`.
// @ 6.06
// guard == true || n == null ==> return arr / str 的复制
// else 返回从首个位置开始 length - n个数量 的元素数组 / string
_.initial = function(array, n, guard) {
return slice.call(array, 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. The **guard** check allows it to work with `_.map`.
// @ 6.07 和_.first 恰好相反
_.last = function(array, n, guard) {
if (array == null) return void 0;
if ((n == null) || guard) return array[array.length - 1];
// @ 6.08 从第length - n个开始截取到尾部 总共n个
return slice.call(array, Math.max(array.length - n, 0));
};
// 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. The **guard**
// check allows it to work with `_.map`.
// @ 6.08 其实就相当于slice.call(arr, n); 当然在n 不传递的情况下 n 为 1
// 用途想是基本用在 arguments 的情况下 例如 _.rest(arguments)
_.rest = _.tail = _.drop = function(array, n, guard) {
return slice.call(array, (n == null) || guard ? 1 : n);
};
// Trim out all falsy values from an array.
// @ 6.09 有意思 内部调用 _.filter 而filter 内部是利用的predicate 的返回值 if ( predicate.call(etc.) )
// 即 _.identity 的返回值 而_.identity 内部是直接return value
// 即 用来剔除了 !!value === false 的value 选项 然后push 进result 数组 最后 返回
// 例:_.compact([0, 1, false, 2, '', 3]); ==> [1, 2, 3]
_.compact = function(array) {
return _.filter(array, _.identity);
};
// Internal implementation of a recursive `flatten` function.
// @ 6.11 闭包中的一个helper 方法
var flatten = function(input, shallow, output) {
// @ 6.12 shallow : 浅 单层
// _.every(input, _.isArray) input 每项都是一个arr
if (shallow && _.every(input, _.isArray)) {
// @ 6.13 利用apply调用的形式传递input数组 直接完成了简单的一层连接
// 官方例子: _.flatten([1, [2], [3, [[4]]]], true) ==> [1, 2, 3, [[4]]];
return concat.apply(output, input);
}
// @ 6.14 循环遍历 input(可能是 arr / string / object )
each(input, function(value) {
if (_.isArray(value) || _.isArguments(value)) {
// @ 6.15 是否‘浅’调用
// 浅调用 直接使用 apply 调用 传递 value
// 非浅调用 递归
shallow ? push.apply(output, value) : flatten(value, shallow, output);
} else {
// @ 6.16 value 不是 arr / arg 的情况 直接push
output.push(value);
}
});
return output;
};
// Flatten out an array, either recursively (by default), or just one level.
_.flatten = function(array, shallow) {
// @ 6.17 直接返回 flatten函数 的返回值数组
return flatten(array, shallow, []);
};
// Return a version of the array that does not contain the specified value(s).
_.without = function(array) {
// @ 6.19 直接调用的_.difference 其实是一样的 不过反而不接受一种情形: 直接array 直接传递一个数组的情况
// 因为此处只进行了 slice的操作 并未进行 [].concat的操作 详见 @ 6.18
// 如果传递 [1,2,3] 就相当于 _.difference( array, [ [1,2,3] ] );
// 这也就背离了 _.difference 的调用形式
return _.difference(array, slice.call(arguments, 1));
};
// Split an array into two arrays: one whose elements all satisfy the given
// predicate, and one whose elements all do not satisfy the predicate.
_.partition = function(array, predicate) {
var pass = [], fail = [];
// @ 6.20 循环 根据predicate 函数调用结果 == true 分别将 value push 进 pass fail数组 并最后返回
each(array, function(elem) {
(predicate(elem) ? pass : fail).push(elem);
});
return [pass, fail];
};
// 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, iterator, context) {
// @ 6.21 去重方法
// 根据参数的传递 重新分配 变量名
// isSorted 的参数位置是可传可不传的 不传/或传递的不是函数 默认为false
if (_.isFunction(isSorted)) {
context = iterator;
iterator = isSorted;
isSorted = false;
}
// @ 6.22 是否传递函数的情况 如果有 传递给iterator获得一个处理后的数组 以初始化数据
var initial = iterator ? _.map(array, iterator, context) : array;
var results = [];
var seen = [];
each(initial, function(value, index) {
// @ 6.23
// isSorted == true
// 那么使用的方法比较快捷
// 每一次 将value push 进 seen 然后每一次都以后面的值比对前一个值
// isSorted == false
// 调用_.contains 循环遍历seen数组 不存在 则push 存在 则跳过
if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) {
seen.push(value);
// @ 6.24 之所以设置results 因循环的是initial 循环中的value 是经过iterator函数处理过的
// 这里就保证了 push 进的是array中的元素
// @mark 函数写的简略 也有个问题存在 就是如果iterator 函数未传递
// 那么每次进行push的内容都是一样的 最后 返回的seen 和 results数组 其实拥有一摸一样的数组项
results.push(array[index]);
}
});
return results;
};
// Produce an array that contains the union: each distinct element from all of
// the passed-in arrays.
_.union = function() {
// @ 6.25 先调用_.flatten 传递 arguments 和一个 true 平铺一层出来 获得一个arr
// 再调用_.unip 进行去重 未传递isSorted 内部 会调用两重循环
// 例:_.union([1, 2, 3], [101, 2, 1, 10], [2, 1]); ==> [1, 2, 3, 101, 10]
return _.uniq(_.flatten(arguments, true));
};
// Produce an array that contains every item shared between all the
// passed-in arrays.
// @ 6.26 卧槽 有点叼呢 四个函数的调用 想想内部有多少个循环
_.intersection = function(array) {
// @ 6.27 获取其他参数
var rest = slice.call(arguments, 1);
// @ 6.28 _.uniq 先对array进行去重 未传递 isSorted 内部有两重循环
// _.filter 循环 _.uniq 去重后的数组 根据 func1 的返回值 push value 进 result 数组
return _.filter(_.uniq(array), function(item) { // func1
// @ 6.29 func1 返回值的判定 循环 rest数组 (第四重循环)
// // 不过 _.every 内部会根据 一次 func 2的返回值 为false 而 return 出来
return _.every(rest, function(other) { // func 2
// @ 6.30 func2 返回值的判定 _.contains 内部又是重循环(第五重) 使用 _.some 也会根据 一个 other[key] === item 的判定 而return出来
// 怎么说呢,到底是五重循环 真是有点恐怖
return _.contains(other, item);
});
});
// @ 6.31 最后返回的arr 是 几个 arr中 都存在的数值
// 例: _.intersection([1, 2, 3], [101, 2, 1, 10], [2, 1]); ==> [1, 2]
};
// Take the difference between one array and a number of other arrays.
// Only the elements present in just the first array will remain.
_.difference = function(array) {
// @ 6.18 ArrayProto 其实就是一个[] 之所以这样 估计是为节省 创建一个 []的开销
// slice.call(arguments, 1) 倒也可以换成 _.rest(arguments); 不过_.rest内部多了一些判断
// @mark 当然 这里我不明白的是 为何还用一个[] 来对一个 slice.call(arguments, 1) 数组进行一份复制
// @solve [].concat(1,2,3) ==> [1,2,3]
// [].concat([1,2,3]) ==> [1,2,3]
// 这么写 也就可以使得在array 之后的参数可以以一个个的形式 或者 一个数组的形式传递
var rest = concat.apply(ArrayProto, slice.call(arguments, 1));
// @ 6.19 直接利用_.filter 返回一个新数组 而filter内部利用的是each 将符合这个函数过滤的value push 进result数组
// 过滤的判断是利用_.contains 内部有一个_.some的循环 循环rest 数组
// 这里的作用也就相当于 两层循环 第一层循环 array 第二层循环rest 将v1 和 v2 以此比对
// 如果v1 和 v2 都不相等 则 将 v1 push 进 result
// _.difference([1, 2, 3, 4, 5], [5, 2, 10]); ==> [1, 3, 4]
// _.difference([1, 2, 3, 4, 5], 5, 2, 10); ==> [1, 3, 4]
return _.filter(array, function(value){ return !_.contains(rest, value); });
};
// Zip together multiple lists into a single array -- elements that share
// an index go together.
_.zip = function() {
// @ 7.01 调用_.pluck 内部调用 _.map 依次获取 数组的length push 进 result数组 并在最后concat 0
// 之所以concat 0 是为避免_.pluck 获得的数组为空的情形 即 _.max 内部 一个Math.max.apply(Math, []) 时,其返回的值是 -Infinity
var length = _.max(_.pluck(arguments, 'length').concat(0));
var results = new Array(length);
for (var i = 0; i < length; i++) {
// @ 7.02 一个特别技巧 循环次数为最大的数组length
// 然后 每一次添加的数组项 是调用每一个_.map返回的数组 而传递的 i又作为了key
// 因此results数组的每一项 对应的数组 其每一项的值 都是arguments位置传递来的数组key对应位置的值
// 例: _.zip(['moe', 'larry', 'curly'], [30, 40, 50], [true, false, false]);
// ==> [["moe", 30, true], ["larry", 40, false], ["curly", 50, false]]
results[i] = _.pluck(arguments, '' + i);
}
return results;
};
// 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.
_.object = function(list, values) {
if (list == null) return {};
var result = {};
for (var i = 0, length = list.length; i < length; i++) {
// @ 7.03 argurmtents 接受两种情形
// situation1: list和values参数 [key1, key2], [value1, value2]
// situation2: 一个list参数 [ [key1, value1], [key2, value2] ]
if (values) {
result[list[i]] = values[i];
} else {
result[list[i][0]] = list[i][1];
}
}
return result;
};
// If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**), // @ 7.04 笑尿
// we need this function. Return the position of the first occurrence of an
// item in an array, or -1 if the item is not included in the array.
// Delegates to **ECMAScript 5**'s native `indexOf` if available.
// If the array is large and already in sort order, pass `true`
// for **isSorted** to use binary search.
_.indexOf = function(array, item, isSorted) {
if (array == null) return -1;
var i = 0, length = array.length;
if (isSorted) {
// @ 7.04 这里的逻辑有点叼呢 如果 isSorted 参数位置传入的是个number
// 只设定 相应的 开始搜索位置 i (还可接受负数)
if (typeof isSorted == 'number') {
i = (isSorted < 0 ? Math.max(0, length + isSorted) : isSorted);
} else {
// @ 7.05 如果传递的不是一个数字 并且是已经通过了 if (isSorted) 的判定
// 需要注意的是 这时的array 必须是以一定规则从小到大排序好的
// 那么利用_.sortedIndex 找到item该插入的位置 也就是item 所在的位置 并进行比对返回 i 或 -1
i = _.sortedIndex(array, item);
return array[i] === item ? i : -1;
}
}
// @ 7.06 另一方面 其他逻辑 以及上面设定完i后的逻辑都走入下面
if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted);
// @ 7.07 Array.prototype.indexOf 不存在的情况下只好利用循环
for (; i < length; i++) if (array[i] === item) return i;
return -1;
};
// Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
_.lastIndexOf = function(array, item, from) {
if (array == null) return -1;
var hasIndex = from != null;
if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) {
return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item);
}
var i = (hasIndex ? from : array.length);
// @ 7.08 确实不错 尽管 lastIndexOf 没有现有的函数可使用
// 但是使用while循环 以及i--的形式 那么也就确定了是从后开始遍历 并且少了一两个判断
while (i--) if (array[i] === item) return i;
return -1;
};
// Generate an integer Array containing an arithmetic progression. A port of
// the native Python `range()` function. See
// [the Python documentation](http://docs.python.org/library/functions.html#range).
// @ 7.09 来自python 值得拥有 orz
_.range = function(start, stop, step) {
if (arguments.length <= 1) {
stop = start || 0;
start = 0;
}
step = arguments[2] || 1;
var length = Math.max(Math.ceil((stop - start) / step), 0);
var idx = 0;
var range = new Array(length);
while(idx < length) {
range[idx++] = start;
start += step;
}
return range;
};
// Function (ahem) Functions
// ------------------
// Reusable constructor function for prototype setting.
var ctor = function(){};
// Create a function bound to a given object (assigning `this`, and arguments,
// optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if
// available.
_.bind = function(func, context) {
var args, bound;
// @ 1.29 Function.prototype.bind
// 返回一个指定了this 的 函数
if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
if (!_.isFunction(func)) throw new TypeError;
// @ 1.30 获得参数func, context 以外的参数
args = slice.call(arguments, 2);
// @ 1.31 还真有点叼呢 这个bind 在 Function.prototype.bind 不存在的情况下
// @ 1.32 返回的这个bound函数还接受两种情况 一个是函数调用 一个是构造函数new式调用
return bound = function() {
// @ 1.33 简单函数调用情况 指定this 为context
if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
// @ 1.34 构造函数形式调用情况 也就不管context什么事了
// 之所以在构造函数情况不直接return func 为避免_.bind(func) 返回的函数是一个引用而在一些情况下被修改
// 闭包中的ctor函数 指定 prototype对象为func.prototype 即同一个 模拟 构造函数的场景
ctor.prototype = func.prototype;
var self = new ctor;
// @ 1.35 在实例生成完之后 self原型链上拥有了func.prototype将ctor.prototype上的属性重置
ctor.prototype = null;
// @ 1.36 传递参数 再将构造函数func 中的this.name 这类 整合进 self对象
// 如果构造函数有apply调用下有返回值(比如安全模式 内部 return new / 或者内部return 打断)的情况
var result = func.apply(self, args.concat(slice.call(arguments)));
// @ 1.37 其实这里可以用_.isObject的 毕竟实现是一模一样
if (Object(result) === result) return result;
// @ 1.38 否则 返回self对象 且已经整合进实例属性的self对象
return self;
};
};
// Partially apply a function by creating a version that has had some of its
// arguments pre-filled, without changing its dynamic `this` context. _ acts
// as a placeholder, allowing any combination of arguments to be pre-filled.
// @ 8.01 用于整合出一个指定了相应参数(已传递)的 新函数 可以接受新的参数
// 用于放在原函数参数的后面
// 例:var add = function(a, b) { return a + b; };
// var add5 = _.partial(add, 5); // 指定了第一个参数位置传递的为5
// add5(10); ==> 15;
_.partial = function(func) {
var boundArgs = slice.call(arguments, 1);
return function() {
var position = 0;
// @ 8.02 复制 boundArgs
var args = boundArgs.slice();
// @ 8.03 进一步处理 为照顾参数传递_(内部调用)的情况 如果_.partial 参数位置传递了一个 _
// 过滤掉 并将参数设置为 返回的函数传入的参数
// @ mark 这样 有一点问题就是
// var func2 = _.partial(func, _, 1)
// 最终执行func2(2, 3) 时 等同于 func(2, 1, 3) 而不是func(1, 2, 3)
for (var i = 0, length = args.length; i < length; i++) {
if (args[i] === _) args[i] = arguments[position++];