在进行一些调试时,我遇到了一种情况,其中最好概括toUpperCase
方法。以下是我提出的几种方法:
//Local
foo = [];
foo.toUpperCase = String(foo).toUpperCase;
foo.push("a");
foo.toUpperCase();
//Global
foo = [];
window.toUpperCase = function (obj) {return String(obj).toUpperCase();}
foo.push("a");
toUpperCase(foo);
//Prototype
foo = [];
Array.prototype.toUpperCase = String.prototype.toUpperCase;
foo.push("a");
foo.toUpperCase();
//Constructor Prototype
foo = [];
Array.prototype.constructor = String.prototype.toUpperCase;
foo.push("a");
foo.constructor();
//toString override
var foo = [];
foo.push("a");
var bar = String(foo);
foo.toString = function() { return bar.toUpperCase(); }
foo.toString();
大多数String和Array方法在spec:
中都有此免责声明因此,它可以转移到其他类型的对象以用作方法。
是否存在实现此类抽象的传统方法?
答案 0 :(得分:3)
如果您需要toUpperCase
函数只能使用数组,那么您可以像这样扩展Array
类:
Array.prototype.toUpperCase = function () {
return String(this).toUpperCase();
};
之后你可以写:
var foo = [];
foo.push('a');
foo.toUpperCase();
答案 1 :(得分:1)
您无需执行任何操作:
String.prototype.toUpperCase.call(anyObject)
如果您想缩短它,则需要将call
绑定到toUpperCase
:
var toUpperCase = Function.call.bind(String.prototype.toUpperCase);
答案 2 :(得分:0)
附加到全局对象的静态方法是先前实现的解决方案(代码少,数据更多)。
必须编写通用方法,以便它们只需要一组最小的方法。例如,大多数通用数组方法只需要它来提供长度和索引访问。
使用
Array.prototype.slice
转换数组中的arguments
时存在一个缺点:它会阻止浏览器JavaScript引擎执行优化推荐的方法是使用Array.slice泛型方法 - 数组泛型方法是专门用于其他类型的 - 但它不是ECMAScript规范的一部分
/**
* Implementation of standard Array methods (introduced in ECMAScript 5th
* edition) and shorthand generics (JavaScript 1.8.5)
*
* Copyright (c) 2013 Alex K @plusdude
* http://opensource.org/licenses/MIT
*/
(function (global, infinity, undefined) {
/*jshint bitwise:false, maxlen:95, plusplus:false, validthis:true*/
"use strict";
/**
* Local references to constructors at global scope.
* This may speed up access and slightly reduce file size of minified version.
*/
var Array = global.Array;
var Object = global.Object;
var Math = global.Math;
var Number = global.Number;
/**
* Converts argument to an integral numeric value.
* @see http://www.ecma-international.org/ecma-262/5.1/#sec-9.4
*/
function toInteger(value) {
var number;
// let number be the result of calling ToNumber on the input argument
number = Number(value);
return (
// if number is NaN, return 0
number !== number ? 0 :
// if number is 0, Infinity, or -Infinity, return number
0 === number || infinity === number || -infinity === number ? number :
// return the result of computing sign(number) * floor(abs(number))
(0 < number || -1) * Math.floor(Math.abs(number))
);
}
/**
* Returns a shallow copy of a portion of an array.
* @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.10
*/
function slice(begin, end) {
/*jshint newcap:false*/
var result, elements, length, index, count;
// convert elements to object
elements = Object(this);
// convert length to unsigned 32 bit integer
length = elements.length >>> 0;
// calculate begin index, if is set
if (undefined !== begin) {
// convert to integer
begin = toInteger(begin);
// handle -begin, begin > length
index = 0 > begin ? Math.max(length + begin, 0) : Math.min(begin, length);
} else {
// default value
index = 0;
}
// calculate end index, if is set
if (undefined !== end) {
// convert to integer
end = toInteger(end);
// handle -end, end > length
length = 0 > end ? Math.max(length + end, 0) : Math.min(end, length);
}
// create result array
result = new Array(length - index);
// iterate over elements
for (count = 0; index < length; ++index, ++count) {
// current index exists
if (index in elements) {
// copy current element to result array
result[count] = elements[index];
}
}
return result;
}
/**
* Returns the first index at which a given element
* can be found in the array.
* @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.14
*/
function indexOf(target, begin) {
/*jshint newcap:false*/
var elements, length, index;
// convert elements to object
elements = Object(this);
// convert length to unsigned 32 bit integer
length = elements.length >>> 0;
// calculate begin index, if is set
if (undefined !== begin) {
// convert to integer
begin = toInteger(begin);
// handle -begin, begin > length
index = 0 > begin ? Math.max(length + begin, 0) : Math.min(begin, length);
} else {
// default value
index = 0;
}
// iterate over elements
for (; index < length; ++index) {
// current index exists, target element is equal to current element
if (index in elements && target === elements[index]) {
// break loop, target element found
return index;
}
}
// target element not found
return -1;
}
/**
* Returns the last index at which a given element
* can be found in the array.
* @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.15
*/
function lastIndexOf(target, begin) {
/*jshint newcap:false*/
var elements, length, index;
// convert elements to object
elements = Object(this);
// convert length to unsigned 32 bit integer
length = elements.length >>> 0;
// calculate begin index, if is set
if (undefined !== begin) {
// convert to integer
begin = toInteger(begin);
// handle -begin, begin > length - 1
index = 0 > begin ? length - Math.abs(begin) : Math.min(begin, length - 1);
} else {
// default value
index = length - 1;
}
// iterate over elements backwards
for (; -1 < index; --index) {
// current index exists, target element is equal to current element
if (index in elements && target === elements[index]) {
// break loop, target element found
return index;
}
}
// target element not found
return -1;
}
/**
* Executes a provided function once per array element.
* @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.18
*/
function forEach(callback, scope) {
/*jshint newcap:false*/
var elements, length, index;
// convert elements to object
elements = Object(this);
// make sure callback is a function
requireFunction(callback);
// convert length to unsigned 32 bit integer
length = elements.length >>> 0;
// iterate over elements
for (index = 0; index < length; ++index) {
// current index exists
if (index in elements) {
// execute callback
callback.call(scope, elements[index], index, elements);
}
}
}
/**
* Tests whether all elements in the array pass the test
* implemented by the provided function.
* @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.16
*/
function every(callback, scope) {
/*jshint newcap:false*/
var elements, length, index;
// convert elements to object
elements = Object(this);
// make sure callback is a function
requireFunction(callback);
// convert length to unsigned 32 bit integer
length = elements.length >>> 0;
// iterate over elements
for (index = 0; index < length; ++index) {
// current index exists
if (index in elements &&
// callback returns false
!callback.call(scope, elements[index], index, elements)) {
// break loop, test failed
return false;
}
}
// test passed, controversy began..
return true;
}
/**
* Tests whether some element in the array passes the test
* implemented by the provided function.
* @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.17
*/
function some(callback, scope) {
/*jshint newcap:false*/
var elements, length, index;
// convert elements to object
elements = Object(this);
// make sure callback is a function
requireFunction(callback);
// convert length to unsigned 32 bit integer
length = elements.length >>> 0;
// iterate over elements
for (index = 0; index < length; ++index) {
// current index exists
if (index in elements &&
// callback returns true
callback.call(scope, elements[index], index, elements)) {
// break loop, test passed
return true;
}
}
// test failed
return false;
}
/**
* Creates a new array with all elements that pass the test
* implemented by the provided function.
* @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.20
*/
function filter(callback, scope) {
/*jshint newcap:false*/
var result = [], elements, length, index, count;
// convert elements to object
elements = Object(this);
// make sure callback is a function
requireFunction(callback);
// convert length to unsigned 32 bit integer
length = elements.length >>> 0;
// iterate over elements
for (index = count = 0; index < length; ++index) {
// current index exists
if (index in elements &&
// callback returns true
callback.call(scope, elements[index], index, elements)) {
// copy current element to result array
result[count++] = elements[index];
}
}
return result;
}
/**
* Creates a new array with the results of calling a provided function
* on every element in this array.
* @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.19
*/
function map(callback, scope) {
/*jshint newcap:false*/
var result = [], elements, length, index;
// convert elements to object
elements = Object(this);
// make sure callback is a function
requireFunction(callback);
// convert length to unsigned 32 bit integer
length = elements.length >>> 0;
// iterate over elements
for (index = 0; index < length; ++index) {
// current index exists
if (index in elements) {
// copy a return value of callback to result array
result[index] = callback.call(scope, elements[index], index, elements);
}
}
return result;
}
/**
* Apply a function against values of the array (from left-to-right)
* as to reduce it to a single value.
* @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.21
*/
function reduce(callback, value) {
/*jshint newcap:false*/
var elements, isset, length, index;
// convert elements to object
elements = Object(this);
// make sure callback is a function
requireFunction(callback);
// status of the initial value
isset = undefined !== value;
// convert length to unsigned 32 bit integer
length = elements.length >>> 0;
// iterate over elements
for (index = 0; index < length; ++index) {
// current index exists
if (index in elements) {
// initial value is set
if (isset) {
// replace initial value with a return value of callback
value = callback(value, elements[index], index, elements);
} else {
// current element becomes initial value
value = elements[index];
// status of the initial value
isset = true;
}
}
}
// make sure the initial value exists after iteration
requireValue(isset);
return value;
}
/**
* Apply a function against values of the array (from right-to-left)
* as to reduce it to a single value.
* @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.22
*/
function reduceRight(callback, value) {
/*jshint newcap:false*/
var elements, isset, index;
// convert elements to object
elements = Object(this);
// make sure callback is a function
requireFunction(callback);
// status of the initial value
isset = undefined !== value;
// index of the last element
index = (elements.length >>> 0) - 1;
// iterate over elements backwards
for (; -1 < index; --index) {
// current index exists
if (index in elements) {
// initial value is set
if (isset) {
// replace initial value with a return value of callback
value = callback(value, elements[index], index, elements);
} else {
// current element becomes initial value
value = elements[index];
// status of the initial value
isset = true;
}
}
}
// make sure the initial value exists after iteration
requireValue(isset);
return value;
}
/**
* Returns true if an argument is an array, false if it is not.
* @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.3.2
*/
function isArray(value) {
return "[object Array]" === Object.prototype.toString.call(value);
}
/**
* Tests if an argument is callable and throws an error if it is not.
* @private
*/
function requireFunction(value) {
if ("[object Function]" !== Object.prototype.toString.call(value)) {
throw new Error(value + " is not a function");
}
}
/**
* Throws an error if an argument can be converted to true.
* @private
*/
function requireValue(isset) {
if (!isset) {
throw new Error("reduce of empty array with no initial value");
}
}
/**
* Tests implementation of standard Array method.
* @private
*/
function supportsStandard(key) {
var support = true;
// a method exists
if (Array.prototype[key]) {
try {
// apply dummy arguments
Array.prototype[key].call(undefined, /test/, null);
// passed? implemented wrong
support = false;
} catch (e) {
// do nothing
}
} else {
support = false;
}
return support;
}
/**
* Tests implementation of generic Array method.
* @private
*/
function supportsGeneric(key) {
var support = true;
// a method exists
if (Array[key]) {
try {
// apply dummy arguments
Array[key](undefined, /test/, null);
// passed? implemented wrong
support = false;
} catch (e) {
// do nothing
}
} else {
support = false;
}
return support;
}
/**
* Assigns method to Array constructor.
* @private
*/
function extendArray(key) {
if (!supportsGeneric(key)) {
Array[key] = createGeneric(key);
}
}
/**
* Creates generic method from an instance method.
* @private
*/
function createGeneric(key) {
/** @public */
return function (elements) {
var list;
if (undefined === elements || null === elements) {
throw new Error("Array.prototype." + key + " called on " + elements);
}
list = Array.prototype.slice.call(arguments, 1);
return Array.prototype[key].apply(elements, list);
};
}
/**
* Assign ECMAScript-5 methods to Array constructor,
* and Array prototype.
*/
var ES5 = {
"indexOf": indexOf,
"lastIndexOf": lastIndexOf,
"forEach": forEach,
"every": every,
"some": some,
"filter": filter,
"map": map,
"reduce": reduce,
"reduceRight": reduceRight
};
for (var key in ES5) {
if (ES5.hasOwnProperty(key)) {
if (!supportsStandard(key)) {
Array.prototype[key] = ES5[key];
}
extendArray(key);
}
}
Array.isArray = Array.isArray || isArray;
/**
* Assign ECMAScript-3 methods to Array constructor.
* The toString method is omitted.
*/
[
"concat",
"join",
"slice",
"pop",
"push",
"reverse",
"shift",
"sort",
"splice",
"unshift"
].forEach(extendArray);
/**
* Test the slice method on DOM NodeList.
* Support: IE < 9
*/
/*jshint browser:true*/
if (document) {
try {
Array.slice(document.childNodes);
} catch (e) {
Array.prototype.slice = slice;
}
}
}(this, 1 / 0));
&#13;
/*globals define*/
// Assumes all supplied String instance methods already present
// (one may use shims for these if not available)
(function() {
'use strict';
var i,
// We could also build the array of methods with the following, but the
// getOwnPropertyNames() method is non-shimable:
// Object.getOwnPropertyNames(String).filter(function(methodName) {
// return typeof String[methodName] === 'function';
// });
methods = [
'quote', 'substring', 'toLowerCase', 'toUpperCase', 'charAt',
'charCodeAt', 'indexOf', 'lastIndexOf', 'startsWith', 'endsWith',
'trim', 'trimLeft', 'trimRight', 'toLocaleLowerCase',
'toLocaleUpperCase', 'localeCompare', 'match', 'search',
'replace', 'split', 'substr', 'concat', 'slice'
],
methodCount = methods.length,
assignStringGeneric = function(methodName) {
var method = String.prototype[methodName];
String[methodName] = function(arg1) {
return method.apply(arg1, Array.prototype.slice.call(arguments, 1));
};
};
for (i = 0; i < methodCount; i++) {
assignStringGeneric(methods[i]);
}
}());
&#13;
<强>参考强>