Cla*_*diu 16 javascript arrays overloading
有没有办法在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
Run Code Online (Sandbox Code Playgroud)
我在这里看到的特殊行为是sup.length更改而没有调用任何方法.我从这个问题中了解到,在数组的情况下,[]运算符会被重载,这就解释了这种行为.有没有一种纯粹的JavaScript方式来复制这种行为,或者语言是不是足够灵活?
根据Mozilla文档,正则表达式返回的值也可以用这个索引做一些时髦的事情.用普通的javascript可以实现吗?
Vin*_*ert 23
[]运算符是访问对象属性的本机方式.为了更改其行为,它无法覆盖语言.
如果您想要的是在[]运算符上返回计算值,则不能在JavaScript中执行此操作,因为该语言不支持计算属性的概念.唯一的解决方案是使用与[]运算符相同的方法.
MyClass.prototype.getItem = function(index)
{
return {
name: 'Item' + index,
value: 2 * index
};
}
Run Code Online (Sandbox Code Playgroud)
如果您想要的是与类中的本机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);
}
Run Code Online (Sandbox Code Playgroud)
Bor*_*gar 13
是的,您可以在JavaScript中轻松地将数组子类化为arraylike对象:
var ArrayLike = function() {};
ArrayLike.prototype = [];
ArrayLike.prototype.shuffle = // ... and so on ...
Run Code Online (Sandbox Code Playgroud)
然后,您可以实例化类似对象的新数组:
var cards = new Arraylike;
cards.push('ace of spades', 'two of spades', 'three of spades', ...
cards.shuffle();
Run Code Online (Sandbox Code Playgroud)
不幸的是,这在MSIE中不起作用.它没有跟踪length财产.这相当于整个事情的缩小.
关于Dean Edwards 如何对JavaScript数组对象进行子类化的更详细问题.后来证明他的解决方法并不安全,因为一些弹出窗口拦截器会阻止它.
更新:值得一提的是Juriy"kangax"Zaytsev 关于此主题的绝对史诗文章.它几乎涵盖了这个问题的每个方面.
现在我们有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;
}
Run Code Online (Sandbox Code Playgroud)
我不能保证这实际上是完全正确的,并且它不处理你将长度改为小于其先前值的情况(行为有点复杂才能正确;大致它删除属性以便length属性不变量),但它概述了如何实现它.它也不模仿[[Call]]和[[Construct]]的行为Array,这是ES6之前你不能做的另一件事 - 在ES代码中两者之间不可能有不同的行为,虽然这些都不难.
这length以规范将其定义为工作方式实现属性:它拦截对象上属性的赋值,length如果属性是"数组索引",则更改属性.
与ES5和getter不同的是,这允许一个人进入length恒定时间(显然,这仍然取决于VM中的基础属性访问是恒定时间),并且它是唯一能够提供非恒定时间性能的情况是newLen - oldLen删除属性时未实现的情况(大多数VM中的删除速度很慢!).