澄清:
正如有些人所指出的,这确实看起来像“这段代码是否正确” - 问题。我真正感到好奇的主要是: .hasOwnProperty
方法如何工作?
我的意思是:IE的JScript引擎(至少<9)不会总是使用散列表,所以我假设它只是迭代该对象的所有属性,然后检查它是否从原型链上方的另一个对象获取这些属性。这是一个公平的假设吗?毕竟:在某种程度上,每个代码都被转换为循环和分支,但是如果IE不执行散列表,那么这并不意味着.hasOwnProperty
只是一些糖,就在那里你不必写循环?我认为我从DC的博客文章或视频中得到了这个概念,很可能是他在谈论数组,以及他们在JS中的古怪事物(或者更确切地说:可能)。我找不到视频/博客帖子(?)ATM。看到JS阵列经常被滥用,我认为你们很多人会同意,我认为这个问题的答案可以作为一个不错的参考。这就是为什么我没有在codereview上发布它。
就目前的答案而言,由于我的问题是从错误的角度开始的(关注代码而不是其背后的机制),让我只是如此陈词滥调,感谢大家指出问题我没有想到。
最近,我为我正在制作的剧本增加了阵列原型(不要拍摄,还有)。为了不重新发明轮子,我去寻找其他人如何去做的一些例子。
扼杀This is quite common,这显然是不必要的复杂。我还发现this, as an alternative,除了海报对他的快速算法相当自负我还觉得还有改进的余地。
我知道我现在可能会成为一个自鸣得意的聪明人,我的unique
方法实际上看起来像这样:
Array.prototype.unique = function()
{
'use strict';
var i,obj,ret;
ret = [];
obj = {};
for (i=0;i<this.length;i++)
{
if (!obj.hasOwnProperty(this[i]))
{
ret.push(this[i]);
obj[this[i]] = i;
}
}
return ret;
};
这样,就没有必要再使用第二个循环AFAIK了吗?当然,除非hasOwnProperty
方法死得很慢,但在某种程度上我怀疑,在这种情况下:链只能回到1级,到Object.prototype
。
我发布的第二个链接包含一些统计数据和速度比较,但我们都知道它们在现实世界中几乎没有任何意义。任何人都可以指出我关于JS和基准测试的好文章的方向(除了John Resig's博客上的那篇文章吗?
所以,出于好奇:你们中有没有人发现这个方法存在问题? 更多信息:Object.prototype:未更改,undefined
是undefined
,没有全局变量没有框架,并且此方法不是盲目实现的(if (!Array.prototype.unique){...}
) < / p>
答案 0 :(得分:4)
这是一个正确考虑类型的实现,比天真的嵌套循环快得多,并维护原始数组的顺序:
Array.prototype.unique = function(){
var r, o, i, j, t, tt;
r = [];
o = {};
for(i = 0; i < this.length; i++){
t = this[i];
tt = o[t] = o[t] || [];
for(j = 0; j < tt.length; j++)
if(tt[j] === this[i])
break;
if(j == tt.length)
r.push(tt[j] = t);
}
return r;
}
我做了JSPerf to compare these implementations。
虽然unique2是最快的,但它有一个问题,它认为"1"
和1
相同。 unique4在速度方面排名第三,但比unique1快得多,并提供正确的输出。所有四种变化实际上都有不同的输出:
=> [1, "1", 1, 2, 3, 4, 1, 2, 3, "2", "3", "4", "true", "true", true].unique1()
// ["1", 4, 1, 2, 3, "2", "3", "4", "true", true]
=> [1, "1", 1, 2, 3, 4, 1, 2, 3, "2", "3", "4", "true", "true", true].unique2()
// [1, "2", "3", "4", true]
=> [1, "1", 1, 2, 3, 4, 1, 2, 3, "2", "3", "4", "true", "true", true].unique3()
// [1, 2, 3, 4, "true"]
=> [1, "1", 1, 2, 3, 4, 1, 2, 3, "2", "3", "4", "true", "true", true].unique4()
// [1, "1", 2, 3, 4, "2", "3", "4", "true", true]
答案 1 :(得分:3)
虽然没有得到广泛支持,但您可以使用Set
(来自ECMAScript Harmony)。它是原生的,所以它不应该对性能产生太大的影响(例如,不需要像indexOf
那样寻找实际的索引)。主要优点是您可以无缝地使用它来跟踪您已经拥有的项目,包括对象,并且您可以考虑相同的对象:
Array.prototype.unique = function() {
'use strict';
var ret = [];
var used = new Set();
for (var i = 0; i < this.length; i++) {
if (!used.has(this[i])) {
ret.push(this[i]);
used.add(this[i]);
}
}
return ret;
};
var a = {};
var b = {};
[1, 2, 1, a, a, null, b].unique(); // [1, 2, a, null, b]
答案 2 :(得分:0)
看起来很好,但是Felix Kling注意到它只适用于包含基元的数组。这个有点复杂,但适用于我认为的所有类型:
Array.prototype.unique = function(){
'use strict';
var im = {}, uniq = [];
for (var i=0;i<this.length;i++){
var type = (this[i]).constructor.name,
// ^note: for IE use this[i].constructor!
val = type + (!/num|str|regex|bool/i.test(type)
? JSON.stringify(this[i])
: this[i]);
if (!(val in im)){uniq.push(this[i]);}
im[val] = 1;
}
return uniq;
}
//testing
var test = [1,2,'string',[1,2,3],'string','1','2',
{yada:'yada'},/[a-z]/i,2,1,1,2,[1,2,3],
false, new Date(),/[a-b]/i,5,/[a-z]/i,'false']
,testunique = test.unique();
//=> testunique now
// [1,2,string,[1,2,3],1,2,[object Object],
// /[a-z]/i,false,Mon Aug 20 2012 20:20:11 GMT+0200,
// /[a-b]/i,5,false]