JavaScript独特的数组原型方法

时间:2012-08-20 16:57:04

标签: javascript performance prototype

澄清:

正如有些人所指出的,这确实看起来像“这段代码是否正确” - 问题。我真正感到好奇的主要是: .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:未更改,undefinedundefined,没有全局变量没有框架,并且此方法不是盲目实现的(if (!Array.prototype.unique){...} < / p>

3 个答案:

答案 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

  • unique1是嵌套循环。
  • unique2是您链接的快速算法。
  • unique3是您的版本。
  • unique4是我的。
    添加
  • unique5是Kooilnc的回答
  • unique6是primvdb的回答

虽然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]