当前位置:  开发笔记 > 编程语言 > 正文

在不使用Array的情况下在JavaScript中实现类似于Array的行为

如何解决《在不使用Array的情况下在JavaScript中实现类似于Array的行为》经验,为你挑选了3个好方法。

有没有办法在JavaScript中创建类似数组的对象,而不使用内置数组?我特别关注这样的行为:

var sup = new Array(5);
//sup.length here is 0
sup[0] = 'z3ero';
//sup.length here is 1
sup[1] = 'o3ne';
//sup.length here is 2
sup[4] = 'f3our';        
//sup.length here is 5

我在这里看到的特殊行为是sup.length更改而没有调用任何方法.我从这个问题中了解到,在数组的情况下,[]运算符会被重载,这就解释了这种行为.有没有一种纯粹的JavaScript方式来复制这种行为,或者语言是不是足够灵活?

根据Mozilla文档,正则表达式返回的值也可以用这个索引做一些时髦的事情.用普通的javascript可以实现吗?



1> Vincent Robe..:

[]运算符是访问对象属性的本机方式.为了更改其行为,它无法覆盖语言.

如果您想要的是在[]运算符上返回计算值,则不能在JavaScript中执行此操作,因为该语言不支持计算属性的概念.唯一的解决方案是使用与[]运算符相同的方法.

MyClass.prototype.getItem = function(index)
{
    return {
        name: 'Item' + index,
        value: 2 * index
    };
}

如果您想要的是与类中的本机Array具有相同的行为,则始终可以直接在您的类上使用本机Array方法.在内部,您的类将像本机数组一样存储数据但会保持其类状态.jQuery这样做可以使jQuery类在保留其方法的同时具有数组行为.

MyClass.prototype.addItem = function(item)
{
    // Will add "item" in "this" as if it was a native array
    // it will then be accessible using the [] operator 
    Array.prototype.push.call(this, item);
}


从JavaScript 1.8.5开始,你*可以*具有计算属性.https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/defineProperty

2> Borgar..:

是的,您可以在JavaScript中轻松地将数组子类化为arraylike对象:

var ArrayLike = function() {};
ArrayLike.prototype = [];
ArrayLike.prototype.shuffle = // ... and so on ...

然后,您可以实例化类似对象的新数组:

var cards = new Arraylike;
cards.push('ace of spades', 'two of spades', 'three of spades', ... 
cards.shuffle();

不幸的是,这在MSIE中不起作用.它没有跟踪length财产.这相当于整个事情的缩小.

关于Dean Edwards 如何对JavaScript数组对象进行子类化的更详细问题.后来证明他的解决方法并不安全,因为一些弹出窗口拦截器会阻止它.

更新:值得一提的是Juriy"kangax"Zaytsev 关于此主题的绝对史诗文章.它几乎涵盖了这个问题的每个方面.



3> gsnedders..:

现在我们有ECMAScript 2015(ECMA-262第6版; ES6),我们有代理对象,它们允许我们Array在语言本身中实现行为,类似于:

function FakeArray() {
  const target = {};

  Object.defineProperties(target, {
    "length": {
      value: 0,
      writable: true
    },
    [Symbol.iterator]: {
      // http://www.ecma-international.org/ecma-262/6.0/#sec-array.prototype-@@iterator
      value: () => {
        let index = 0;

        return {
          next: () => ({
            done: index === target.length,
            value: target[index++]
          })
        };
      }
    }
  });

  const isArrayIndex = function(p) {
    /* an array index is a property such that
       ToString(ToUint32(p)) === p and ToUint(p) !== 2^32 - 1 */
    const uint = p >>> 0;
    const s = uint + "";
    return p === s && uint !== 0xffffffff;
  };

  const p = new Proxy(target, {
    set: function(target, property, value, receiver) {
      // http://www.ecma-international.org/ecma-262/6.0/index.html#sec-array-exotic-objects-defineownproperty-p-desc
      if (property === "length") {
        // http://www.ecma-international.org/ecma-262/6.0/index.html#sec-arraysetlength
        const newLen = value >>> 0;
        const numberLen = +value;
        if (newLen !== numberLen) {
          throw RangeError();
        }
        const oldLen = target.length;
        if (newLen >= oldLen) {
          target.length = newLen;
          return true;
        } else {
          // this case gets more complex, so it's left as an exercise to the reader
          return false; // should be changed when implemented!
        }
      } else if (isArrayIndex(property)) {
        const oldLenDesc = Object.getOwnPropertyDescriptor(target, "length");
        const oldLen = oldLenDesc.value;
        const index = property >>> 0;
        if (index > oldLen && oldLenDesc.writable === false) {
          return false;
        }
        target[property] = value;
        if (index > oldLen) {
          target.length = index + 1;
        }
        return true;
      } else {
        target.property = value;
        return true;
      }
    }
  });

  return p;
}

我不能保证这实际上是完全正确的,并且它不处理你将长度改为小于其先前值的情况(行为有点复杂才能正确;大致它删除属性以便length属性不变量),但它概述了如何实现它.它也不模仿[[Call]]和[[Construct]]的行为Array,这是ES6之前你不能做的另一件事 - 在ES代码中两者之间不可能有不同的行为,虽然这些都不难.

length以规范将其定义为工作方式实现属性:它拦截对象上属性的赋值,length如果属性是"数组索引",则更改属性.

与ES5和getter不同的是,这允许一个人进入length恒定时间(显然,这仍然取决于VM中的基础属性访问是恒定时间),并且它是唯一能够提供非恒定时间性能的情况是newLen - oldLen删除属性时未实现的情况(大多数VM中的删除速度很慢!).

推荐阅读
wurtjq
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有