如何创建自定义数组构造函数,它是本机Array构造函数的扩展版本?
例如,jQuery看起来像一个包含其他方法的数组,例如$().addClass
。但是,它没有修改Array.prototype
,因为new Array().hasClass
是undefined
。
那么,如何在不修改Array.prototype
的情况下创建扩展数组实现?
Employees( ... ) //-> [{name: 'John', age: 32}, {name: 'Bob', age: 29}];
Employees( ... ).byAge(32)//-> [{name: 'John', age: 32}];
// and
Array().byAge //-> undefined
答案 0 :(得分:5)
jQuery对象不是Array,也不“覆盖”Array类。它只是array-like。
你可以通过browsing the source看到jQuery如何实现这一点;另请参阅Array Like Objects in Javascript和Why Are Array-Like Objects Used in Javascript Over Native Arrays。
答案 1 :(得分:1)
jQuery不是真正的数组实现:jQuery instanceof Array
是假的!
如果要创建数组的真实实例,和添加自定义方法,请使用此代码。它使用Function.prototype.bind
来调用具有任意数量参数的构造函数。
该实现的行为完全为真数组,但有一点除外:
Array
构造函数时,它会创建一个长度为此参数的数组。length
属性来设置 n 的长度。function Employees() {
// Deal with missing "new"
if (!(this instanceof Employees)) {
// Convert arguments to an array, because we have to shift all index by 1
var args = Array.prototype.slice.call(arguments);
args.unshift(this); // Shift all indexes, set "this"
return new (Function.prototype.bind.apply(Employees, args));
} else {
// Set length property.
var len = arguments.length,
/*
* fn_internalLength: Internal method for calculating the length
**/
fn_internalLength,
/*
* explicitLength: Deals with explicit length setter
**/
explicitLength = 0;
// Setting all numeric keys
while (len--) {
this[len] = arguments[len];
}
// Internal method for defining lengths
fn_internalLength = function() {
var allKeys = Object.keys(this).sort(function(x, y) {
// Sort list. Highest index on top.
return y - x;
}), i=-1, length = allKeys.length, tmpKey,
foundLength = 0;
// Loop through all keys
while (++i < length && (tmpKey = allKeys[i]) >= 0) {
// Is the key is an INTEGER?
if (tmpKey - tmpKey === 0 && tmpKey % 1 === 0) {
foundLength = 1*tmpKey + 1;
break;
}
}
// Return MAX(actual length, explictly set length)
return foundLength > explicitLength ? foundLength : explicitLength;
}.bind(this);
// Define the magic length property
Object.defineProperty(this, 'length',
{
get: fn_internalLength,
set: function(newLength) {
var length = fn_internalLength();
if (newLength < length) {
for (var i=newLength; i<length; i++) {
delete this[i];
}
}
// Set explicit length
explicitLength = newLength;
},
enumerable: false,
configurable: false
});
}
}
Employees.prototype = new Array;
// Example: Custom method:
Employees.prototype.print = function() {
return this.join('--'); // Using inherit Array.prototype.join
};
// Just like the Array, `new` is optional
console.log(new Employees(1,2).print());
console.log(Employees(1,2).print());
// Is the object an array?
console.log(new Employees() instanceof Array); // True!
// Can't believe it?
console.log(new Employees() instanceof Employees); // True!
答案 2 :(得分:0)
如何创建自定义数组构造函数,它是本机Array构造函数的扩展版本?
如何在不修改
Array.prototype
的情况下创建扩展数组实现?
使用代价高昂的Object.setPrototypeOf
,您可以创建对象,其继承数组 支持自动长度改变,
function MyArray() {
var arr = Array.apply(null, arguments); // Array never cares about the custom `this` :(
Object.setPrototypeOf(arr, MyArray.prototype); // fix prototype chain
// any futher custom modification of `arr` which isn't inherited goes here..
return arr;
}
MyArray.prototype = Object.create(Array.prototype); // safe inherit from `Array.prototype`
// now using it, set a custom inherited method
MyArray.prototype.foo = function () {return 'bar';};
// construct
var x = MyArray(1, 2, 3); // [1, 2, 3]
// length starts normally
x.length; // 3
// length automatically increases
x[3] = 4;
x.length; // 4
// can use length to trim
x.length = 1;
x[1]; // undefined
// have custom inherited properties
x.foo(); // "bar"
// but have not modified normal Arrays
try {
[].foo(); // throws a TypeError
} catch (e) {
console.log(e); // TypeError [].foo (undefined) is not a function
}
// finally, have
x instanceof MyArray; // true
x instanceof Array; // true
如果 length 的自动属性对您来说并不重要,您可以使用类似数组的对象。如果它们具有长度和 splice ,它们将在控制台中显示为 Array ,但修改对象时,>长度不会自动更改,因此您无法使用括号表示法等进行设置。