在 javascript 中扩展 Array 对象的方法
Posted
技术标签:
【中文标题】在 javascript 中扩展 Array 对象的方法【英文标题】:Ways to extend Array object in javascript 【发布时间】:2012-07-05 11:23:58 【问题描述】:我尝试使用一些用户友好的方法在 javascript 中扩展 Array 对象,例如 Array.Add() 而不是 Array.push() 等...
我实现了 3 种方法来做到这一点。 不幸的是,第三种方式不起作用,我想问为什么?以及如何做到这一点。
//------------- 1st way
Array.prototype.Add=function(element)
this.push(element);
;
var list1 = new Array();
list1.Add("Hello world");
alert(list1[0]);
//------------- 2nd way
function Array2 ()
//some other properties and methods
;
Array2.prototype = new Array;
Array2.prototype.Add = function(element)
this.push(element);
;
var list2 = new Array2;
list2.Add(123);
alert(list2[0]);
//------------- 3rd way
function Array3 ()
this.prototype = new Array;
this.Add = function(element)
this.push(element);
;
;
var list3 = new Array3;
list3.Add(456); //push is not a function
alert(list3[0]); // undefined
在第三种方式中,我想在 Array3 类内部扩展 Array 对象。 如何做到这一点才不会得到“push is not a function”和“undefined”?
这里我添加了第四种方式。
//------------- 4th way
function Array4 ()
//some other properties and methods
this.Add = function(element)
this.push(element);
;
;
Array4.prototype = new Array();
var list4 = new Array4();
list4.Add(789);
alert(list4[0]);
这里我必须再次使用原型。 我希望避免在类构造函数之外使用额外的行作为 Array4.prototype。 我想有一个紧凑的定义类,所有部分都在一个地方。 但我想我不能这样做。
【问题讨论】:
如果你向 Array 添加一个方法,你会破坏数组上的 foreach() 你看过咖啡脚本吗?我会用一个例子来更新我的答案 我不会像第一个示例那样向 Array.prototype 添加方法。这是一个测试。我将创建一个扩展 Array 对象的类。例如 jsArray。 jsArray 对象将是数组,但具有更多功能。 我今天看到了咖啡脚本。我不喜欢它的语法。 @SpacedMonkey 如果有人使用我的自定义 js lib 可以调整他的 foreach() 不包括枚举的最后 2 个元素,因为它是对象的类型及其长度。 【参考方案1】:ES6
class SubArray extends Array
last()
return this[this.length - 1];
var sub = new SubArray(1, 2, 3);
sub // [1, 2, 3]
sub instanceof SubArray; // true
sub instanceof Array; // true
Using __proto__
(旧答案,不推荐,可能会导致performance issues)
function SubArray()
var arr = [ ];
arr.push.apply(arr, arguments);
arr.__proto__ = SubArray.prototype;
return arr;
SubArray.prototype = new Array;
现在您可以将您的方法添加到SubArray
SubArray.prototype.last = function()
return this[this.length - 1];
;
像普通数组一样初始化
var sub = new SubArray(1, 2, 3);
表现得像普通数组
sub instanceof SubArray; // true
sub instanceof Array; // true
【讨论】:
虽然在大多数 JavaScript 实现中都支持使用__proto__
,但它在被标记为 es6 中的旧功能之前并不是标准功能。如果大量使用它也会导致性能问题。使用它通常是个坏主意。
@TBotV63 同意。在 es6 中,您可以简单地扩展一个 Array 类。更新了我的答案
您应该将constructor
中的值传递给super
,如下所示:constructor(...items) super(...items)
在构造函数this.weight中添加了一个额外的属性,现在可以使用for-in进行迭代,如何使其不可迭代?
@shivshankar 你确定要使用for-in
而不是for-of
?前者将其视为一个对象,后者将其视为一个数组(/iterable)。【参考方案2】:
不久前,我阅读了 jQuery 的创建者 John Resig 写的 Javascript Ninja 一书。
他提出了一种使用普通 JS 对象来模拟类似数组的方法的方法。基本上,只需要length
。
var obj =
length: 0, //only length is required to mimic an Array
add: function(elem)
Array.prototype.push.call(this, elem);
,
filter: function(callback)
return Array.prototype.filter.call(this, callback); //or provide your own implemetation
;
obj.add('a');
obj.add('b');
console.log(obj.length); //2
console.log(obj[0], obj[1]); //'a', 'b'
我不是说它是好是坏。这是进行Array
操作的原始方式。好处是您无需扩展Array prototype
。
请记住,obj
是普通的 object
,而不是 Array
。因此obj instanceof Array
将返回false
。将obj
视为外观。
如果您对该代码感兴趣,请阅读摘录清单 4.10 模拟类似数组的方法。
【讨论】:
【参考方案3】:var SubArray = function()
var arrInst = new Array(...arguments); // spread arguments object
/* Object.getPrototypeOf(arrInst) === Array.prototype */
Object.setPrototypeOf(arrInst, SubArray.prototype); //redirectionA
return arrInst; // now instanceof SubArray
;
SubArray.prototype =
// SubArray.prototype.constructor = SubArray;
constructor: SubArray,
// methods avilable for all instances of SubArray
add: function(element)return this.push(element);,
...
;
Object.setPrototypeOf(SubArray.prototype, Array.prototype); //redirectionB
var subArr = new SubArray(1, 2);
subArr.add(3); subArr[2]; // 3
答案是一个紧凑的解决方法,它可以在所有支持的浏览器中按预期工作。
【讨论】:
【参考方案4】:你也可以在 ES6 中使用这种方式:
Object.assign(Array.prototype,
unique()
return this.filter((value, index, array) =>
return array.indexOf(value) === index;
);
);
结果:
let x = [0,1,2,3,2,3];
let y = x.unique();
console.log(y); // => [0,1,2,3]
【讨论】:
这扩展了 Array 原型,不应该这样做(因为它会影响整个站点) 除非你想影响整个网站。那就太好了。【参考方案5】:在第三个示例中,您只是为对象Array3
创建了一个名为prototype
的新属性。当您执行new Array3
(应该是new Array3()
)时,您正在将该对象实例化为变量list3
。因此,Add
方法将不起作用,因为有问题的对象this
没有有效的方法push
。希望你能理解。
编辑:查看Understanding JavaScript Context以了解有关this
的更多信息。
【讨论】:
我明白了,this.prototype 被视为 Array3 的属性,谢谢 另外不要忘记在实例化对象时添加()
。 Array
也是一个对象,所以 new Array()
(或者通常只是 []
)。它之所以有效,是因为浏览器很智能。【参考方案6】:
您不能在 JavaScript 中扩展数组对象。
相反,您可以做的是定义一个对象,该对象将包含在 Array 上执行的函数列表,并将这些函数注入该 Array 实例并返回这个新的 Array 实例。您不应该做的是更改 Array.prototype
以将您的自定义函数包含在列表中。
例子:
function MyArray()
var tmp_array = Object.create(Array.prototype);
tmp_array = (Array.apply(tmp_array, arguments) || tmp_array);
//Now extend tmp_array
for( var meth in MyArray.prototype )
if(MyArray.prototype.hasOwnProperty(meth))
tmp_array[meth] = MyArray.prototype[meth];
return (tmp_array);
//Now define the prototype chain.
MyArray.prototype =
customFunction: function() return "blah blah"; ,
customMetaData: "Blah Blah",
只是一个示例代码,您可以根据需要对其进行修改和使用。但我建议您遵循的基本概念保持不变。
【讨论】:
我发现分配prototype
是不好的做法。分配现有prototype
对象的属性总是更好。
你说你不能扩展 Array 对象但承认向 Array.prototype 添加函数是可能的......你的回答对我来说似乎矛盾
@Purefan - 这可能是矛盾的,但它确实有助于理解 Javascript 做什么(和不做什么)——这对于 JS 世界以外的人来说是违反直觉的。【参考方案7】:
方法名称应为小写。构造函数中不应修改原型。
function Array3() ;
Array3.prototype = new Array;
Array3.prototype.add = Array3.prototype.push
在 CoffeeScript 中
class Array3 extends Array
add: (item)->
@push(item)
如果您不喜欢这种语法,并且必须从构造函数中扩展它, 您唯一的选择是:
// define this once somewhere
// you can also change this to accept multiple arguments
function extend(x, y)
for(var key in y)
if (y.hasOwnProperty(key))
x[key] = y[key];
return x;
function Array3()
extend(this, Array.prototype);
extend(this,
Add: function(item)
return this.push(item)
);
;
你也可以这样做
ArrayExtenstions =
Add: function()
extend(ArrayExtenstions, Array.prototype);
function Array3()
Array3.prototype = ArrayExtenstions;
在过去,“prototype.js”曾经有一个 Class.create 方法。你可以把这一切都包装成这样的方法
var Array3 = Class.create(Array,
construct: function()
,
Add: function()
);
有关这方面的更多信息以及如何实现,请查看prototype.js 源代码
【讨论】:
是的,这是第二种方式。我故意使用 Add 而不是 add 因为我打算编写一些扩展方法,例如 Visual Basic 外观。 好的,我不能在构造函数中使用原型。有没有办法在构造函数中扩展 Array 对象? 是的,但是如果您必须实例化很多对象,这会很昂贵。有关示例实现,请参阅api.jquery.com/jQuery.extend。使用该技术,您可以。 function Array3() extend(this, add: function(item) return this.push(item) ) ; 哦,你也可以使用'Object.defineProperty'来设置getter和setter。 developer.mozilla.org/en/JavaScript/Reference/Global_Objects/… 它的语法对我来说是新的。我觉得 javascript 更熟悉了【参考方案8】:您是否要尝试做一些更复杂的事情,然后为“push”添加一个名为“Add”的别名?
如果不是,最好避免这样做。我建议这是一个坏主意的原因是因为 Array 是一个内置的 javascript 类型,修改它会导致所有脚本 Array 类型都有新的“添加”方法。名称与另一个第三方发生冲突的可能性很高,并可能导致第三方脚本失去其方法而有利于您的脚本。
我的一般规则是创建一个辅助函数来处理数组,如果它不存在于某个地方,并且只在非常必要时扩展数组。
【讨论】:
现在我只是学习一些关于 js 的新东西。我想为 js 编写一些扩展方法,使其看起来像 Visual Basic 核心功能。这对我写web项目来说更合适也更容易。 听起来是个很棒的练习!正是你需要了解更多 JS。 太棒了,祝你好运。我只是不希望您遇到人们在扩展内置类型和破坏第三方扩展以及花费数小时试图找出库无法正常工作的原因(我之前做过:S)的常见错误。跨度> 不,我不会使用任何其他第三方 API。它将是独立的。以上是关于在 javascript 中扩展 Array 对象的方法的主要内容,如果未能解决你的问题,请参考以下文章