Optimize _.isFunction.

Former-commit-id: 0aaaa6d166c7eea94237388d61a11c6d183cbe1f
This commit is contained in:
John-David Dalton
2012-08-13 23:11:01 -07:00
parent b8c2a05db9
commit 01fb1a5775
3 changed files with 109 additions and 100 deletions

View File

@@ -857,7 +857,6 @@
// build replacement code // build replacement code
lodash.forOwn({ lodash.forOwn({
'Date': 'dateClass', 'Date': 'dateClass',
'Function': 'funcClass',
'Number': 'numberClass', 'Number': 'numberClass',
'RegExp': 'regexpClass', 'RegExp': 'regexpClass',
'String': 'stringClass' 'String': 'stringClass'

View File

@@ -55,6 +55,7 @@
'isArr', 'isArr',
'isArray', 'isArray',
'isFunc', 'isFunc',
'isFunction',
'isPlainObject', 'isPlainObject',
'methodName', 'methodName',
'noaccum', 'noaccum',

207
lodash.js
View File

@@ -663,8 +663,8 @@
var factory = Function( var factory = Function(
'arrayLikeClasses, ArrayProto, bind, compareAscending, concat, forIn, ' + 'arrayLikeClasses, ArrayProto, bind, compareAscending, concat, forIn, ' +
'funcClass, hasOwnProperty, identity, indexOf, isArguments, isArray, ' + 'funcClass, hasOwnProperty, identity, indexOf, isArguments, isArray, ' +
'isPlainObject, iteratorBind, objectClass, objectTypes, nativeKeys, ' + 'isFunction, isPlainObject, iteratorBind, objectClass, objectTypes, ' +
'propertyIsEnumerable, slice, stringClass, toString', 'nativeKeys, propertyIsEnumerable, slice, stringClass, toString',
'var callee = function(' + args + ') {\n' + iteratorTemplate(data) + '\n};\n' + 'var callee = function(' + args + ') {\n' + iteratorTemplate(data) + '\n};\n' +
'return callee' 'return callee'
); );
@@ -672,8 +672,8 @@
return factory( return factory(
arrayLikeClasses, ArrayProto, bind, compareAscending, concat, forIn, arrayLikeClasses, ArrayProto, bind, compareAscending, concat, forIn,
funcClass, hasOwnProperty, identity, indexOf, isArguments, isArray, funcClass, hasOwnProperty, identity, indexOf, isArguments, isArray,
isPlainObject, iteratorBind, objectClass, objectTypes, nativeKeys, isFunction, isPlainObject, iteratorBind, objectClass, objectTypes,
propertyIsEnumerable, slice, stringClass, toString nativeKeys, propertyIsEnumerable, slice, stringClass, toString
); );
} }
@@ -751,7 +751,7 @@
// Also check that the constructor is `Object` (i.e. `Object instanceof Object`) // Also check that the constructor is `Object` (i.e. `Object instanceof Object`)
var ctor = value.constructor; var ctor = value.constructor;
if ((!noNodeClass || !(typeof value.toString != 'function' && typeof (value + '') == 'string')) && if ((!noNodeClass || !(typeof value.toString != 'function' && typeof (value + '') == 'string')) &&
(toString.call(ctor) != funcClass || ctor instanceof ctor)) { (!isFunction(ctor) || ctor instanceof ctor)) {
// IE < 9 iterates inherited properties before own properties. If the first // IE < 9 iterates inherited properties before own properties. If the first
// iterated property is an object's own property then there are no inherited // iterated property is an object's own property then there are no inherited
// enumerable properties. // enumerable properties.
@@ -856,6 +856,92 @@
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/
/**
* Checks if `value` is an `arguments` object.
*
* @static
* @memberOf _
* @category Objects
* @param {Mixed} value The value to check.
* @returns {Boolean} Returns `true` if the `value` is an `arguments` object, else `false`.
* @example
*
* (function() { return _.isArguments(arguments); })(1, 2, 3);
* // => true
*
* _.isArguments([1, 2, 3]);
* // => false
*/
function isArguments(value) {
return toString.call(value) == argsClass;
}
// fallback for browsers that can't detect `arguments` objects by [[Class]]
if (noArgsClass) {
isArguments = function(value) {
return !!(value && hasOwnProperty.call(value, 'callee'));
};
}
/**
* Checks if `value` is an array.
*
* @static
* @memberOf _
* @category Objects
* @param {Mixed} value The value to check.
* @returns {Boolean} Returns `true` if the `value` is an array, else `false`.
* @example
*
* (function() { return _.isArray(arguments); })();
* // => false
*
* _.isArray([1, 2, 3]);
* // => true
*/
var isArray = nativeIsArray || function(value) {
return toString.call(value) == arrayClass;
};
/**
* Checks if `value` is a function.
*
* @static
* @memberOf _
* @category Objects
* @param {Mixed} value The value to check.
* @returns {Boolean} Returns `true` if the `value` is a function, else `false`.
* @example
*
* _.isFunction(''.concat);
* // => true
*/
function isFunction(value) {
return typeof value == 'function';
}
// fallback for older versions of Chrome and Safari
if (isFunction(/x/)) {
isFunction = function(value) {
return toString.call(value) == funcClass;
};
}
/**
* A shim implementation of `Object.keys` that produces an array of the given
* object's own enumerable property names.
*
* @private
* @param {Object} object The object to inspect.
* @returns {Array} Returns a new array of property names.
*/
var shimKeys = createIterator({
'args': 'object',
'exit': 'if (!(object && objectTypes[typeof object])) throw TypeError()',
'init': '[]',
'inLoop': 'result.push(index)'
});
/*--------------------------------------------------------------------------*/
/** /**
* Creates a clone of `value`. If `deep` is `true`, all nested objects will * Creates a clone of `value`. If `deep` is `true`, all nested objects will
* also be cloned otherwise they will be assigned by reference. If a value has * also be cloned otherwise they will be assigned by reference. If a value has
@@ -909,7 +995,7 @@
} }
// use custom `clone` method if available // use custom `clone` method if available
var isObj = objectTypes[typeof value]; var isObj = objectTypes[typeof value];
if ((isObj || thorough.value) && value.clone && toString.call(value.clone) == funcClass) { if ((isObj || thorough.value) && value.clone && isFunction(value.clone)) {
thorough.value = null; thorough.value = null;
return value.clone(deep); return value.clone(deep);
} }
@@ -1114,7 +1200,7 @@
'useHas': false, 'useHas': false,
'args': 'object', 'args': 'object',
'init': '[]', 'init': '[]',
'inLoop': 'if (toString.call(value) == funcClass) result.push(index)', 'inLoop': 'if (isFunction(value)) result.push(index)',
'bottom': 'result.sort()' 'bottom': 'result.sort()'
}); });
@@ -1137,52 +1223,6 @@
return hasOwnProperty.call(object, property); return hasOwnProperty.call(object, property);
} }
/**
* Checks if `value` is an `arguments` object.
*
* @static
* @memberOf _
* @category Objects
* @param {Mixed} value The value to check.
* @returns {Boolean} Returns `true` if the `value` is an `arguments` object, else `false`.
* @example
*
* (function() { return _.isArguments(arguments); })(1, 2, 3);
* // => true
*
* _.isArguments([1, 2, 3]);
* // => false
*/
function isArguments(value) {
return toString.call(value) == argsClass;
}
// fallback for browsers that can't detect `arguments` objects by [[Class]]
if (noArgsClass) {
isArguments = function(value) {
return !!(value && hasOwnProperty.call(value, 'callee'));
};
}
/**
* Checks if `value` is an array.
*
* @static
* @memberOf _
* @category Objects
* @param {Mixed} value The value to check.
* @returns {Boolean} Returns `true` if the `value` is an array, else `false`.
* @example
*
* (function() { return _.isArray(arguments); })();
* // => false
*
* _.isArray([1, 2, 3]);
* // => true
*/
var isArray = nativeIsArray || function(value) {
return toString.call(value) == arrayClass;
};
/** /**
* Checks if `value` is a boolean (`true` or `false`) value. * Checks if `value` is a boolean (`true` or `false`) value.
* *
@@ -1264,7 +1304,7 @@
'if (arrayLikeClasses[className]' + 'if (arrayLikeClasses[className]' +
(noArgsClass ? ' || isArguments(value)' : '') + ' ||\n' + (noArgsClass ? ' || isArguments(value)' : '') + ' ||\n' +
' (className == objectClass && length > -1 && length === length >>> 0 &&\n' + ' (className == objectClass && length > -1 && length === length >>> 0 &&\n' +
' toString.call(value.splice) == funcClass)' + ' isFunction(value.splice))' +
') return !length', ') return !length',
'inLoop': { 'inLoop': {
'object': 'return false' 'object': 'return false'
@@ -1317,11 +1357,11 @@
b = b._wrapped; b = b._wrapped;
} }
// use custom `isEqual` method if available // use custom `isEqual` method if available
if (a.isEqual && toString.call(a.isEqual) == funcClass) { if (a.isEqual && isFunction(a.isEqual)) {
thorough.value = null; thorough.value = null;
return a.isEqual(b); return a.isEqual(b);
} }
if (b.isEqual && toString.call(b.isEqual) == funcClass) { if (b.isEqual && isFunction(b.isEqual)) {
thorough.value = null; thorough.value = null;
return b.isEqual(a); return b.isEqual(a);
} }
@@ -1408,8 +1448,8 @@
// non `Object` object instances with different constructors are not equal // non `Object` object instances with different constructors are not equal
if (ctorA != ctorB && !( if (ctorA != ctorB && !(
toString.call(ctorA) == funcClass && ctorA instanceof ctorA && isFunction(ctorA) && ctorA instanceof ctorA &&
toString.call(ctorB) == funcClass && ctorB instanceof ctorB isFunction(ctorB) && ctorB instanceof ctorB
)) { )) {
return false; return false;
} }
@@ -1474,23 +1514,6 @@
return nativeIsFinite(value) && toString.call(value) == numberClass; return nativeIsFinite(value) && toString.call(value) == numberClass;
} }
/**
* Checks if `value` is a function.
*
* @static
* @memberOf _
* @category Objects
* @param {Mixed} value The value to check.
* @returns {Boolean} Returns `true` if the `value` is a function, else `false`.
* @example
*
* _.isFunction(''.concat);
* // => true
*/
function isFunction(value) {
return toString.call(value) == funcClass;
}
/** /**
* Checks if `value` is the language type of Object. * Checks if `value` is the language type of Object.
* (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
@@ -1636,21 +1659,6 @@
return value === undefined; return value === undefined;
} }
/**
* A shim implementation of `Object.keys` that produces an array of the given
* object's own enumerable property names.
*
* @private
* @param {Object} object The object to inspect.
* @returns {Array} Returns a new array of property names.
*/
var shimKeys = createIterator({
'args': 'object',
'exit': 'if (!(object && objectTypes[typeof object])) throw TypeError()',
'init': '[]',
'inLoop': 'result.push(index)'
});
/** /**
* Creates an array composed of the own enumerable property names of `object`. * Creates an array composed of the own enumerable property names of `object`.
* *
@@ -1738,8 +1746,7 @@
// http://code.google.com/p/fbug/source/browse/branches/firebug1.9/content/firebug/chrome/reps.js?r=12614#653 // http://code.google.com/p/fbug/source/browse/branches/firebug1.9/content/firebug/chrome/reps.js?r=12614#653
// http://trac.webkit.org/browser/trunk/Source/WebCore/inspector/InjectedScriptSource.js?rev=125186#L609 // http://trac.webkit.org/browser/trunk/Source/WebCore/inspector/InjectedScriptSource.js?rev=125186#L609
if (arrayLikeClasses[className] || (noArgsClass && isArguments(value)) || if (arrayLikeClasses[className] || (noArgsClass && isArguments(value)) ||
(className == objectClass && length > -1 && length === length >>> 0 && (className == objectClass && length > -1 && length === length >>> 0 && isFunction(value.splice))) {
toString.call(value.splice) == funcClass)) {
return length; return length;
} }
return keys(value).length; return keys(value).length;
@@ -2296,7 +2303,7 @@
if (!collection) { if (!collection) {
return []; return [];
} }
if (collection.toArray && toString.call(collection.toArray) == funcClass) { if (collection.toArray && isFunction(collection.toArray)) {
return collection.toArray(); return collection.toArray();
} }
var length = collection.length; var length = collection.length;
@@ -3176,7 +3183,7 @@
*/ */
function bind(func, thisArg) { function bind(func, thisArg) {
var methodName, var methodName,
isFunc = toString.call(func) == funcClass; isFunc = isFunction(func);
// juggle arguments // juggle arguments
if (!isFunc) { if (!isFunc) {
@@ -3253,13 +3260,15 @@
'var funcs = arguments,\n' + 'var funcs = arguments,\n' +
' length = funcs.length;\n' + ' length = funcs.length;\n' +
'if (length > 1) {\n' + 'if (length > 1) {\n' +
' for (var index = 1; index < length; index++)\n' + ' for (var index = 1; index < length; index++) {\n' +
' result[funcs[index]] = bind(result[funcs[index]], result);\n' + ' result[funcs[index]] = bind(result[funcs[index]], result)\n' +
' }\n' +
' return result\n' + ' return result\n' +
'}', '}',
'inLoop': 'inLoop':
'if (toString.call(result[index]) == funcClass)' + 'if (isFunction(result[index])) {\n' +
' result[index] = bind(result[index], result)' ' result[index] = bind(result[index], result)\n' +
'}'
}); });
/** /**
@@ -3691,7 +3700,7 @@
return null; return null;
} }
var value = object[property]; var value = object[property];
return toString.call(value) == funcClass ? object[property]() : value; return isFunction(value) ? object[property]() : value;
} }
/** /**