Javascript。创建类数组对象。是否可以使 .length 自动递增?
Javascript. Creating Array-like objects. Is it possible to make .length autoincrement?
关于类数组对象的创建和继承有很多内容,但我的一个问题仍未得到解答:是否有可能以某种方式向对象添加自动递增的 .length
属性 ?
示例脚本:
function ArrayLike(){
this.length=0;
this.splice=[].splice;
}
var Arr=new ArrayLike();
Arr[0]=0;
Arr[1]=1;
alert(Arr.length); //alerts "0" instead of "2"
当然,我可以添加一些方法,例如:
function ArrayLike(){
this.length=0;
this.splice=[].splice;
this.push=function(toadd){
this[this.length++]=toadd;
}
}
但这不是我想要做的。
那么当我以某种方式向对象添加内容时是否可以增加 .length Arr[N]='blah-blah-blah'
?
is it possible somehow to add autoincrement .length property
不是真的;至少,您不能做 Array
围绕 length
和索引属性所做的所有事情。
在支持 ES5 的环境中,您可以使用 getter 和 setter 函数使 length
成为 属性。但是找出 length
应该是什么是很尴尬的,因为(到目前为止)没有 "catch all" 设置它的方法,当代码如下:
arr[1] = "foo";
...运行。 (ES6 很可能通过代理实现包罗万象的 属性 setters。)
因此您的 length
必须从对象内容中找出它,这不会非常有效,例如:
var explicitLength;
Object.defineProperty(this, "length", {
get: function() {
var length;
// Find our length from our max index property
length = Object.keys(this).reduce(function(maxIndex, key) {
var index = key === "" ? NaN : +key;
if (!isNaN(index) && index > maxIndex) {
return index;
}
return maxIndex;
}, -1) + 1;
// If we have an explicitly-set length, and it's greater,
// use that instead. Note that if explicitLength is
// `undefined`, the condition will always be false.
if (explicitLength > length) {
length = explicitLength;
}
return length;
},
set: function(value) {
explicitLength = value;
// You might choose to have code here removing properties
// defining indexes >= value, likes Array does
}
});
请注意,即使我们有明确设置的长度,我们仍然需要检查自然长度是否更大,以处理:
arr.length = 2;
arr[5] = "foo";
console.log(arr.length); // Should be 6, not 2
在 属性 检索上做那么多工作显然不是一件好事,所以我会避开这种事情。
上面我说了“...所以我会避开这种事情。”
嗯,这一切都很好,但是您应该怎么做呢?
假设您有想要使用的函数 nifty
和 spiffy
。你有两个现实的选择:
使用实际数组,并将自定义方法混合到每个数组实例中
增强Array.prototype
#1 使用实际数组并混入您的方法
你会在一个方便的对象上定义你的方法,比如 ArrayMixin
:
var ArrayMixin = {
nifty: function() {
// ...do something nifty with `this`
},
spiffy: function() {
// ...do something spiffy with `this`
}
};
然后有一个用于创建数组的构建器函数:
function createArray(arr) {
arr = arr || []; // `arr` is optional
extend(arr, ArrayMixin);
return arr;
}
你问这个 extend
函数是什么?将属性从一个对象复制到另一个对象的东西。如果您使用任何库或框架(jQuery、Underscore、Angular、PrototypeJS 等),它们很可能具有执行此操作的某种 extend
函数。通常看起来 something 像这样(这是非常即兴的)。
function extend(target) {
var arg, obj, key;
for (arg = 1; arg < arguments.length; ++arg) {
obj = arguments[arg];
for (key in obj) {
if (obj.hasOwnProperty(key)) {
target[key] = obj[key];
}
}
}
return target;
}
请注意,这是一个浅拷贝,它不会费心去尝试使它添加到 target
的属性不可枚举。根据需要调整。
那么当你想要一个数组时:
var a = createArray(['one', 'two', 'three']);
...并且 a
上面有 nifty
和 spiffy
。
示例:
var ArrayMixin = {
nifty: function() {
this.forEach(function(entry, index) {
this[index] = entry.toUpperCase();
}, this);
return this;
},
spiffy: function() {
this.forEach(function(entry, index) {
this[index] = entry.toLowerCase();
}, this);
return this;
}
};
function createArray(arr) {
arr = arr || []; // `arr` is optional
extend(arr, ArrayMixin);
return arr;
}
function extend(target) {
var arg, obj, key;
for (arg = 1; arg < arguments.length; ++arg) {
obj = arguments[arg];
for (key in obj) {
if (obj.hasOwnProperty(key)) {
target[key] = obj[key];
}
}
}
return target;
}
var a = createArray(['one', 'two', 'three']);
a.nifty();
snippet.log(a.join(", "));
a.spiffy();
snippet.log(a.join(", "));
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
增强Array.prototype
人们对增强 Array.prototype
感到害怕,但这并没有什么错。它造成麻烦的唯一原因是,如果人们使用期望 for-in
循环遍历数组索引 和 的损坏代码,您必须增强原型而不使新方法成为非-可数的。但是,该代码又被破坏了; for-in
不是 用于遍历数组索引,而是用于遍历对象属性。
所以这里是增强 Array.prototype
:
(function(aproto) {
var add;
if (Object.defineProperty) {
// Function to add a non-enumerable property
add = function(obj, prop, value) {
Object.definePrperty(obj, prop, {
value: value
});
};
} else {
// Old JavaScript engine, oh well
add = function(obj, prop, value) {
obj[prop] = value;
};
}
add(aproto, "nifty", function() {
// ...do something nifty with `this`
});
add(aproto, "spiffy", function() {
// ...do something spiffy with `this`
});
})(Array.prototype);
现在用起来真的很干净:
var a = ['one', 'two', 'three'];
...并且 a
上面有 nifty
和 spiffy
。
示例:
(function(aproto) {
var add;
if (Object.defineProperty) {
// Function to add a non-enumerable property
add = function(obj, prop, value) {
Object.defineProperty(obj, prop, {
value: value
});
};
} else {
// Old JavaScript engine, oh well
add = function(obj, prop, value) {
obj[prop] = value;
};
}
add(aproto, "nifty", function() {
this.forEach(function(entry, index) {
this[index] = entry.toUpperCase();
}, this);
});
add(aproto, "spiffy", function() {
this.forEach(function(entry, index) {
this[index] = entry.toLowerCase();
}, this);
});
})(Array.prototype);
var a = ['one', 'two', 'three'];
a.nifty();
snippet.log(a.join(", "));
a.spiffy();
snippet.log(a.join(", "));
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
关于类数组对象的创建和继承有很多内容,但我的一个问题仍未得到解答:是否有可能以某种方式向对象添加自动递增的 .length
属性 ?
示例脚本:
function ArrayLike(){
this.length=0;
this.splice=[].splice;
}
var Arr=new ArrayLike();
Arr[0]=0;
Arr[1]=1;
alert(Arr.length); //alerts "0" instead of "2"
当然,我可以添加一些方法,例如:
function ArrayLike(){
this.length=0;
this.splice=[].splice;
this.push=function(toadd){
this[this.length++]=toadd;
}
}
但这不是我想要做的。
那么当我以某种方式向对象添加内容时是否可以增加 .length Arr[N]='blah-blah-blah'
?
is it possible somehow to add autoincrement .length property
不是真的;至少,您不能做 Array
围绕 length
和索引属性所做的所有事情。
在支持 ES5 的环境中,您可以使用 getter 和 setter 函数使 length
成为 属性。但是找出 length
应该是什么是很尴尬的,因为(到目前为止)没有 "catch all" 设置它的方法,当代码如下:
arr[1] = "foo";
...运行。 (ES6 很可能通过代理实现包罗万象的 属性 setters。)
因此您的 length
必须从对象内容中找出它,这不会非常有效,例如:
var explicitLength;
Object.defineProperty(this, "length", {
get: function() {
var length;
// Find our length from our max index property
length = Object.keys(this).reduce(function(maxIndex, key) {
var index = key === "" ? NaN : +key;
if (!isNaN(index) && index > maxIndex) {
return index;
}
return maxIndex;
}, -1) + 1;
// If we have an explicitly-set length, and it's greater,
// use that instead. Note that if explicitLength is
// `undefined`, the condition will always be false.
if (explicitLength > length) {
length = explicitLength;
}
return length;
},
set: function(value) {
explicitLength = value;
// You might choose to have code here removing properties
// defining indexes >= value, likes Array does
}
});
请注意,即使我们有明确设置的长度,我们仍然需要检查自然长度是否更大,以处理:
arr.length = 2;
arr[5] = "foo";
console.log(arr.length); // Should be 6, not 2
在 属性 检索上做那么多工作显然不是一件好事,所以我会避开这种事情。
上面我说了“...所以我会避开这种事情。”
嗯,这一切都很好,但是您应该怎么做呢?
假设您有想要使用的函数 nifty
和 spiffy
。你有两个现实的选择:
使用实际数组,并将自定义方法混合到每个数组实例中
增强
Array.prototype
#1 使用实际数组并混入您的方法
你会在一个方便的对象上定义你的方法,比如 ArrayMixin
:
var ArrayMixin = {
nifty: function() {
// ...do something nifty with `this`
},
spiffy: function() {
// ...do something spiffy with `this`
}
};
然后有一个用于创建数组的构建器函数:
function createArray(arr) {
arr = arr || []; // `arr` is optional
extend(arr, ArrayMixin);
return arr;
}
你问这个 extend
函数是什么?将属性从一个对象复制到另一个对象的东西。如果您使用任何库或框架(jQuery、Underscore、Angular、PrototypeJS 等),它们很可能具有执行此操作的某种 extend
函数。通常看起来 something 像这样(这是非常即兴的)。
function extend(target) {
var arg, obj, key;
for (arg = 1; arg < arguments.length; ++arg) {
obj = arguments[arg];
for (key in obj) {
if (obj.hasOwnProperty(key)) {
target[key] = obj[key];
}
}
}
return target;
}
请注意,这是一个浅拷贝,它不会费心去尝试使它添加到 target
的属性不可枚举。根据需要调整。
那么当你想要一个数组时:
var a = createArray(['one', 'two', 'three']);
...并且 a
上面有 nifty
和 spiffy
。
示例:
var ArrayMixin = {
nifty: function() {
this.forEach(function(entry, index) {
this[index] = entry.toUpperCase();
}, this);
return this;
},
spiffy: function() {
this.forEach(function(entry, index) {
this[index] = entry.toLowerCase();
}, this);
return this;
}
};
function createArray(arr) {
arr = arr || []; // `arr` is optional
extend(arr, ArrayMixin);
return arr;
}
function extend(target) {
var arg, obj, key;
for (arg = 1; arg < arguments.length; ++arg) {
obj = arguments[arg];
for (key in obj) {
if (obj.hasOwnProperty(key)) {
target[key] = obj[key];
}
}
}
return target;
}
var a = createArray(['one', 'two', 'three']);
a.nifty();
snippet.log(a.join(", "));
a.spiffy();
snippet.log(a.join(", "));
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
增强Array.prototype
人们对增强 Array.prototype
感到害怕,但这并没有什么错。它造成麻烦的唯一原因是,如果人们使用期望 for-in
循环遍历数组索引 和 的损坏代码,您必须增强原型而不使新方法成为非-可数的。但是,该代码又被破坏了; for-in
不是 用于遍历数组索引,而是用于遍历对象属性。
所以这里是增强 Array.prototype
:
(function(aproto) {
var add;
if (Object.defineProperty) {
// Function to add a non-enumerable property
add = function(obj, prop, value) {
Object.definePrperty(obj, prop, {
value: value
});
};
} else {
// Old JavaScript engine, oh well
add = function(obj, prop, value) {
obj[prop] = value;
};
}
add(aproto, "nifty", function() {
// ...do something nifty with `this`
});
add(aproto, "spiffy", function() {
// ...do something spiffy with `this`
});
})(Array.prototype);
现在用起来真的很干净:
var a = ['one', 'two', 'three'];
...并且 a
上面有 nifty
和 spiffy
。
示例:
(function(aproto) {
var add;
if (Object.defineProperty) {
// Function to add a non-enumerable property
add = function(obj, prop, value) {
Object.defineProperty(obj, prop, {
value: value
});
};
} else {
// Old JavaScript engine, oh well
add = function(obj, prop, value) {
obj[prop] = value;
};
}
add(aproto, "nifty", function() {
this.forEach(function(entry, index) {
this[index] = entry.toUpperCase();
}, this);
});
add(aproto, "spiffy", function() {
this.forEach(function(entry, index) {
this[index] = entry.toLowerCase();
}, this);
});
})(Array.prototype);
var a = ['one', 'two', 'three'];
a.nifty();
snippet.log(a.join(", "));
a.spiffy();
snippet.log(a.join(", "));
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>