// 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.
var restArgs = function(func, startIndex) {
startIndex = startIndex == null ? func.length - 1 : +startIndex;
return function() {
var length = Math.max(arguments.length - startIndex, 0),
rest = Array(length),
index = 0;
for (; index < length; index++) {
rest[index] = arguments[index + startIndex];
}
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);
}
var args = Array(startIndex + 1);
for (index = 0; index < startIndex; index++) {
args[index] = arguments[index];
}
args[startIndex] = rest;
return func.apply(this, args);
};
};
// An internal function for creating a new object that inherits from another.
var baseCreate = function(prototype) {
if (!_.isObject(prototype)) return {};
if (nativeCreate) return nativeCreate(prototype);
Ctor.prototype = prototype;
var result = new Ctor;
Ctor.prototype = null;
return result;
};
var shallowProperty = function(key) {
return function(obj) {
return obj == null ? void 0 : obj[key];
};
};
var deepGet = function(obj, path) {
var length = path.length;
for (var i = 0; i < length; i++) {
if (obj == null) return 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
var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
var getLength = shallowProperty('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.
_.each = _.forEach = function(obj, iteratee, context) {
iteratee = optimizeCb(iteratee, context);
var i, length;
if (isArrayLike(obj)) {
for (i = 0, length = obj.length; i < length; i++) {
iteratee(obj[i], i, obj);
}
} else {
var keys = _.keys(obj);
for (i = 0, length = keys.length; i < length; i++) {
iteratee(obj[keys[i]], keys[i], obj);
}
}
return obj; // If it's totally an object
}; //说明item不影响数组或者伪数组本身
// Return the results of applying the iteratee to each element.
_.map = _.collect = function(obj, iteratee, context) {
iteratee = cb(iteratee, context);
var keys = !isArrayLike(obj) && _.keys(obj),
length = (keys || obj).length,
results = Array(length);
for (var index = 0; index < length; index++) {
var currentKey = keys ? keys[index] : index;
results[index] = iteratee(obj[currentKey], currentKey, obj);
}
return results;
};
又开始看underscore了
restArg
var restArgs = function(func, startIndex) {
startIndex = startIndex == null ? func.length - 1 : +startIndex;
return function() {
var length = Math.max(arguments.length - startIndex, 0),
rest = Array(length),
index = 0;
for (; index < length; index++) {
rest[index] = arguments[index + startIndex];
}
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);
}
var args = Array(startIndex + 1);
for (index = 0; index < startIndex; index++) {
args[index] = arguments[index];
}
args[startIndex] = rest;
return func.apply(this, args);
};
};
哇,这么一大排是什么鬼东西?
赶紧看注释:
// 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.
阅读中我们知道这个是想模拟es6里一个叫rest param的东西?
于是搜了下es6里rest param究竟是个什么东西:网址
function f(a, b, ...theArgs) {
// ...
}
现在知道了:
所谓的restParam就是后面的不定参数表示,以数组方式返回,这三个点就代表正义
function f(a, b, ...theArgs) {
console.log(theArgs)
}
f(1, 2, 3, 4, 5) // [3, 4, 5]
等一下?那我们这个”…“还可以怎么操作?
分解数组
console.log(...[1, 2, 3, 4]) // 1 2 3 4
数组赋值
var [head, ...tail] = [1, 2, 3, 4]
console.log(tail) // [2, 3, 4]
数组内部解构返回新数组,数组外部解构则完成解构
f(1,2, ...[3, 4, 5]) // [3, 4, 5]
————————————————
接下来让我们回到代码中:
var restArgs = function(func, startIndex) {
startIndex = startIndex == null ? func.length - 1 : +startIndex;
return function() {
var length = Math.max(arguments.length - startIndex, 0),
rest = Array(length),
index = 0;
for (; index < length; index++) {
rest[index] = arguments[index + startIndex];
}
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);
}
var args = Array(startIndex + 1);
for (index = 0; index < startIndex; index++) {
args[index] = arguments[index];
}
args[startIndex] = rest;
return func.apply(this, args);
};
};
当我们没有三点时,我们只能尝试着去将函数做一次加工,重新整理它多余的参数,打包成一个我们所命名的变量(最好是数组),再重新跟着期望参数一同传入:
因此步骤大致为:
restArgs = function (func) {
return func() {
// process params
return func(expectedParams, packedParams)
}
}
我们知道获取expectedParams的长度方法就是直接用:fun.length
在原代码中即用到:
startIndex = startIndex == null ? func.length - 1 : +startIndex;
// 锁定开始截取的部分,如果规定,则打包部分不包括规定的传入参数
之后就是喜闻乐见地造数组和在新函数内部给原函数放入参数部分了
不过还有个问题,当期望参数足够少时,我们可以用switch分情况一个个输入进去
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);
}
但一旦期望参数太多,比如:
function func(a,b,c,d,e,f,g,h ) {}
教练:你怎么什么都想入进去?
我们就得特殊对待了,将期望参数也变成一个数组再用apply处理不失为一个好方法:
var args = Array(startIndex + 1);
for (index = 0; index < startIndex; index++) {
args[index] = arguments[index];
}
args[startIndex] = rest;
return func.apply(this, args);
于是rest param就这样被我们实现了
baseCreate , shallowProperty, deepGet
baseCreate
// An internal function for creating a new object that inherits from another.
var baseCreate = function(prototype) {
if (!_.isObject(prototype)) return {};
if (nativeCreate) return nativeCreate(prototype);
Ctor.prototype = prototype;
var result = new Ctor;
Ctor.prototype = null;
return result;
};
这段代码就是看你有没有object.create,是之前的nativeCreate更高一层的封装,没有object.create (可能你的浏览器或者编译器太弱)的话,就帮你再实现一次。
Ctor.prototype = prototype;
var result = new Ctor;
Ctor.prototype = null;
return result;
还记得之前的变量Ctor吗?
// Naked function reference for surrogate-prototype-swapping.
var Ctor = function(){};
还能这样用啊,万金油,看来写程序的时候这个方法可以一用,程序不需要每次都麻烦地创造一个空函数再删掉,感觉比object.create还好用,当然只是感觉。
shallowProperty
var shallowProperty = function(key) {
return function(obj) {
return obj == null ? void 0 : obj[key];
};
};
当然就是锁定一个key 然后返回一个专门从各个对象中找该key表示的属性的函数
deepGet
var deepGet = function(obj, path) {
var length = path.length;
for (var i = 0; i < length; i++) {
if (obj == null) return void 0;
obj = obj[path[i]];
}
return length ? obj : void 0;
};
比如,有个对象:
var obj = {
a: {
b: {
c: 1
}
}
}
我想取怎么办?当然是这样啦:
deepGet(obj, ['a', 'b', 'c'])
对了,其中还有个注意点:
“`
console.log(null == undefined) // true
“`
剩余部分:
var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
var getLength = shallowProperty('length');
var isArrayLike = function(collection) {
var length = getLength(collection);
return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX;
};
通过上面的函数衍生出的函数
在undescore的后面也经常使用:
getLength, isArrayLike