ES6 Class extends Array:ES5 Babel transpile 的解决方法
ES6 Class extends Array: workaround for ES5 Babel transpile
我有一些 ES6 class 继承自 Array
:
class Cache extends Array {
add(item) {
if(!item.doNotRemove)
this.push(item)
}
printLast() {
if(this.length > 0)
console.log(this[this.length - 1].text)
}
}
下面的代码工作正常
const myCache = new Cache()
myCache.add({text: 'hello'})
myCache.add({text: 'world'})
myCache.add({text: '!!!', doNotRemove: true})
myCache.printLast() // world
但我无法使用 Babel 将其转换为 ES5(我知道有一个 issue),目前作为一种解决方法,我采用了以下方法:
const CacheProto = {
add(item) {
if(!item.doNotRemove)
this.push(item)
},
printLast() {
if(this.length > 0)
console.log(this[this.length - 1].text)
}
}
function Cache() {
return Object.assign(Object.create(Array.prototype), CacheProto)
}
这满足上面的代码(myCache = new Cache()
等)。但是正如你所看到的,它只是一个 Array 实例扩展。
问题
是否可以使用 原始 class 解决方法?当然,没有extends Array
。 add
和 printLast
方法以及所有 Array.prototype
方法都可以放在原型链上,而不是实例上吗?
我做了一点plunker用于可能的研究。
这有点老套,但我很确定它能满足您的需求。
function Cache() {}
Cache.prototype = new Array;
Cache.prototype.add = function(item) {
if (!item.doNotRemove) {
this.push(item);
}
};
Cache.prototype.printLast = function() {
if (this.length <= 0) { return }
console.log(this[this.length - 1].text);
}
let test = new Cache();
test.add({foo:'bar', text: 'cake' });
test.add({baz:'bat', doNotRemove: true});
test.add({free:'hugs', text: 'hello'});
test.printLast();
console.log(test);
您可以直接使用 __proto__
操作原型,出于向后兼容性的原因,它现在也已经标准化,因此应该可以安全使用。
更多信息在这里 -> https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto
编辑:就像@Bergi 指出的那样,使用带有 Object.setPrototypeOf
的垫片也可以在未来证明这种技术。
function Cache() {
var arr = [];
arr.push.apply(arr, arguments);
arr.__proto__ = Cache.prototype;
//if using a shim, (better option).
//Object.setPrototypeOf(arr, Cache.prototype);
return arr;
}
Cache.prototype = new Array;
Cache.prototype.printLast = function () {
if(this.length > 0)
console.log(this[this.length - 1].text)
}
Cache.prototype.add = function (item) {
if(!item.doNotRemove)
this.push(item)
}
const myCache = new Cache()
myCache.add({text: 'hello'})
myCache.add({text: 'world'})
myCache.add({text: '!!!', doNotRemove: true})
myCache.printLast() // world
myCache.forEach(function (item) { console.log(item); });
console.log("is Array = " + Array.isArray(myCache));
如果你想要神奇的 .length
-影响 属性 赋值 (arr[42] = 21;
) 行为或大多数数组方法,你只需要扩展 Array
。如果不需要,使用数组作为内部数据结构似乎是最简单(也是最兼容)的解决方案:
class Cache {
constructor() {
this._data = [];
}
add(item) {
if(!item.doNotRemove)
this._data.push(item)
}
printLast() {
if(this.length > 0)
console.log(this._data[this._data.length - 1].text)
}
}
您可以轻松公开 .length
和其他方法。
从 Array.prototype
引入多种方法的简单方法是:
['reduce', 'filter', 'find', ...].forEach(method => {
Cache.prototype[method] = function(...args) {
return this._data[method](...args);
};
});
// Or if you want them to be non-enumerable
// (like they would if they were defined via `class` syntax)
Object.defineProperties(
Cache.prototype,
['reduce', 'filter', 'find', ...].reduce((obj, method) => {
obj[method] = {
value: function(...args) { return this._data[method](...args); },
enumerable: false,
configurable: true,
writeable: true,
};
return obj;
}, {})
);
在这里进行一些讨论后,我能够构建一个满足这两个要求的解决方案:将原始 ES6 class 保留为新功能的状态,并将此新功能放在原型上以及用于Array.prototype 方法。
class CacheProto {
add(item) {
if(!item.doNotRemove)
this.push(item)
}
printLast() {
if(this.length > 0)
console.log(this[this.length - 1].text)
}
}
function Cache() {
const instance = [];
instance.push.apply(instance, arguments);
Object.setPrototypeOf(instance, Cache.prototype);
return instance;
}
Cache.prototype = Object.create(Array.prototype);
Object.getOwnPropertyNames(CacheProto.prototype).forEach(methodName =>
Cache.prototype[methodName] = CacheProto.prototype[methodName]
);
这个 CacheProto
和问题中的原始 class 之间的唯一区别是 CacheProto
class 没有扩展 Array
。
最终plunker可以获得here。它包含此解决方案和所有中间变体。
我有一些 ES6 class 继承自 Array
:
class Cache extends Array {
add(item) {
if(!item.doNotRemove)
this.push(item)
}
printLast() {
if(this.length > 0)
console.log(this[this.length - 1].text)
}
}
下面的代码工作正常
const myCache = new Cache()
myCache.add({text: 'hello'})
myCache.add({text: 'world'})
myCache.add({text: '!!!', doNotRemove: true})
myCache.printLast() // world
但我无法使用 Babel 将其转换为 ES5(我知道有一个 issue),目前作为一种解决方法,我采用了以下方法:
const CacheProto = {
add(item) {
if(!item.doNotRemove)
this.push(item)
},
printLast() {
if(this.length > 0)
console.log(this[this.length - 1].text)
}
}
function Cache() {
return Object.assign(Object.create(Array.prototype), CacheProto)
}
这满足上面的代码(myCache = new Cache()
等)。但是正如你所看到的,它只是一个 Array 实例扩展。
问题
是否可以使用 原始 class 解决方法?当然,没有extends Array
。 add
和 printLast
方法以及所有 Array.prototype
方法都可以放在原型链上,而不是实例上吗?
我做了一点plunker用于可能的研究。
这有点老套,但我很确定它能满足您的需求。
function Cache() {}
Cache.prototype = new Array;
Cache.prototype.add = function(item) {
if (!item.doNotRemove) {
this.push(item);
}
};
Cache.prototype.printLast = function() {
if (this.length <= 0) { return }
console.log(this[this.length - 1].text);
}
let test = new Cache();
test.add({foo:'bar', text: 'cake' });
test.add({baz:'bat', doNotRemove: true});
test.add({free:'hugs', text: 'hello'});
test.printLast();
console.log(test);
您可以直接使用 __proto__
操作原型,出于向后兼容性的原因,它现在也已经标准化,因此应该可以安全使用。
更多信息在这里 -> https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto
编辑:就像@Bergi 指出的那样,使用带有 Object.setPrototypeOf
的垫片也可以在未来证明这种技术。
function Cache() {
var arr = [];
arr.push.apply(arr, arguments);
arr.__proto__ = Cache.prototype;
//if using a shim, (better option).
//Object.setPrototypeOf(arr, Cache.prototype);
return arr;
}
Cache.prototype = new Array;
Cache.prototype.printLast = function () {
if(this.length > 0)
console.log(this[this.length - 1].text)
}
Cache.prototype.add = function (item) {
if(!item.doNotRemove)
this.push(item)
}
const myCache = new Cache()
myCache.add({text: 'hello'})
myCache.add({text: 'world'})
myCache.add({text: '!!!', doNotRemove: true})
myCache.printLast() // world
myCache.forEach(function (item) { console.log(item); });
console.log("is Array = " + Array.isArray(myCache));
如果你想要神奇的 .length
-影响 属性 赋值 (arr[42] = 21;
) 行为或大多数数组方法,你只需要扩展 Array
。如果不需要,使用数组作为内部数据结构似乎是最简单(也是最兼容)的解决方案:
class Cache {
constructor() {
this._data = [];
}
add(item) {
if(!item.doNotRemove)
this._data.push(item)
}
printLast() {
if(this.length > 0)
console.log(this._data[this._data.length - 1].text)
}
}
您可以轻松公开 .length
和其他方法。
从 Array.prototype
引入多种方法的简单方法是:
['reduce', 'filter', 'find', ...].forEach(method => {
Cache.prototype[method] = function(...args) {
return this._data[method](...args);
};
});
// Or if you want them to be non-enumerable
// (like they would if they were defined via `class` syntax)
Object.defineProperties(
Cache.prototype,
['reduce', 'filter', 'find', ...].reduce((obj, method) => {
obj[method] = {
value: function(...args) { return this._data[method](...args); },
enumerable: false,
configurable: true,
writeable: true,
};
return obj;
}, {})
);
在这里进行一些讨论后,我能够构建一个满足这两个要求的解决方案:将原始 ES6 class 保留为新功能的状态,并将此新功能放在原型上以及用于Array.prototype 方法。
class CacheProto {
add(item) {
if(!item.doNotRemove)
this.push(item)
}
printLast() {
if(this.length > 0)
console.log(this[this.length - 1].text)
}
}
function Cache() {
const instance = [];
instance.push.apply(instance, arguments);
Object.setPrototypeOf(instance, Cache.prototype);
return instance;
}
Cache.prototype = Object.create(Array.prototype);
Object.getOwnPropertyNames(CacheProto.prototype).forEach(methodName =>
Cache.prototype[methodName] = CacheProto.prototype[methodName]
);
这个 CacheProto
和问题中的原始 class 之间的唯一区别是 CacheProto
class 没有扩展 Array
。
最终plunker可以获得here。它包含此解决方案和所有中间变体。