将构造函数传递给Array.map?

21

如何实现以下功能:

var a = [1,2,3,4];
a.map(Date.constructor);

这段代码在 Google V8 上会抛出一个错误:

SyntaxError: Unexpected number

我也尝试过:

a.map(Date.constructor, Date.prototype)

获得相同的结果。


那段代码不应该导致语法错误。(即使操作本身是有问题的:如果在调用过程中没有使用 new,那么构造函数就只是一个普通函数。)这就是所有的代码吗?你能创建一个最小的 jsfiddle 测试案例吗?在 FF/IE 中是否有语法错误? - user166390
我的代码有一个错误。Date.constructor 是 Function 对象构造函数。Date 对象构造函数是 Date.prototype.constructor 或者只是 Date。 - set
找到了答案。我会在几个小时内发布它。 - set
4个回答

8
我认为OP所寻求的与此严格类似:

var nums = [1, 2, 3];
var strs = nums.map(String);
//=> ['1', '2', '3']; // array of strings

我认为原因在于这种方法非常优雅,无论是简单的类型转换操作(如上所示),还是更有趣的任务,比如将某个东西的一种表示形式转换为另一种表示形式,就像这样:

function MyCoolObject(oldObject) {
    // generates new object by consuming old one
    // maybe attach some cool class methods via prototype
    return this;
}

var newList = oldList.map(MyCoolObj);
//=> array of MyCoolObj based on oldObject

这个问题看起来是由于将构造函数传递给Array.map时创建的新对象是window的扩展版本;也就是说,构造函数中的this指向全局作用域,这很糟糕,因为(1)你的目标不是挂载window上的道具,以及(2)通过这种方式创建的对象不是唯一的实例。
就算是原始的类型转换示例也并不完美,因为:
strs[0] instanceof String
//=> false // UGH!

到目前为止,我唯一想到的解决方案是以不同的方式编写构造函数 -- 当然对于Date这样的原生类型是无法实现的:

function Human(animal) {
    var h = new Object();
    h.species = 'human';
    h.name = animal.name;
    return h;
}

var humans = animals.map(Human);

通过将返回值定义为一个新对象,我们切断了全局作用域和 this 之间的连接;至少我认为在这里是这样做的。(你也可以返回一个 JSON 字面量而不是调用 Object。)
如果我想让这些对象具有有趣的原型,我必须单独定义它,然后显式地附加它:
// this object will be used as the prototype for new Human instances
var HumanProto = {
    species: 'human',
    speak: function() { console.log('My name is ' + this.name); },
    canDrink: function() { return this.age >= 21; }
}

// then, in Human, make this change
var h = new Object(HumanProto);

在这种情况下,仅返回JSON并不足够好,因为似乎没有有效的方法来设置对象文字的原型;即使可以,您也永远不希望this是真实的。
myObject.hasOwnProperty('prototype');
//=> true // only if myObject = { prototype: HumanProto }

我认为确保新对象具有所需原型的最佳方法是将将来的原型作为参数传递给new Object()

这种模式是否理想?我不知道。似乎有点奇怪,因为现在有两个与创建人类相关的符号:Human构造函数和显式原型HumanProto。更重要的是,如果您已经拥有一个不与此模式兼容的有趣自定义类的生态系统,那么这似乎是一个真正的障碍。

可能有更好的方法。也许有人会发布它。


我方案的另一个问题是你不能说 myHuman instanceof Human —— 你必须将其与 HumanProto 进行比较,这是不直观的,并暴露了幕后实现的细节。糟糕透了。 - Tom

3

你是想要做这件事吗?

var a = [1, 2, 3, 4];
a.map(function(obj) { return new Date(obj); });

18
好的,但是不使用匿名函数。 - set
1
为什么?这是一个简单、有效的解决方案,不会产生任何显著的开销。 - Jordan Running
9
因为如果我有一个匿名函数,它调用对象的构造函数并返回结果,那么直接调用构造函数是合理的。无论如何,在这种情况下是不可能直接调用构造函数的(请参见我的答案)。 - set

0

map方法只是使用提供的回调函数(https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/map)迭代数组。所以:

var a = [1,2,3,4];
a.map(function(b) { return b+10; }); // [11, 12, 13, 14]

你想从中获得什么?
Date.constructor(1);
Date.constructor(2);
Date.constructor(3);

来自评论的更新:

这里的问题是通过将Date对象构造函数传递给map函数,从数组a创建具有时间值的日期对象数组。无论如何,代码中存在一个错误(请参见pst的评论)——set

我明白了,所以可以这样:

var a = [1,2,3,4];
a.map(Date.prototype.constructor);

这里的问题是通过将Date对象构造函数传递给map函数,从数组a中创建带有时间值的Date对象数组。无论如何,代码中存在错误(请参见pst的注释)。 - set

-4
日期是一个函数,因此Date.constructor是函数的构造函数。 正确调用日期对象构造函数的方式如下:
Date.prototype.constructor();

或者只是:

Date();

问题是需要使用数组 a 中的时间值创建一个 Date 对象数组,但是没有 new 操作符就无法调用 Date 对象构造函数并传递参数。(参见 ECMA-262 15.9.2)
然而,对于任何可以像使用 new 操作符一样被调用为函数的对象构造函数(例如 Error 对象构造函数(ECMA-262 15.11.1)),都可以实现同样的结果。
$ var a = ['foo','bar','baz'];
$ a.map(Error);
> [ { stack: [Getter/Setter], arguments: undefined, type: undefined, message: 'foo' },
    { stack: [Getter/Setter], arguments: undefined, type: undefined, message: 'bar' },
    { stack: [Getter/Setter], arguments: undefined, type: undefined, message: 'baz' } ]

5
那是错误的,它们只是对象,而不是 Error 的实例。如果你映射 ".prototype.constructor",当调用添加到原型中的函数时,突然间 "this" 没有被定义。 - flying sheep
@Antoniossss 因为作者接受了他们自己的答案。 - Marian
正确,这是作者的回答。 - Antoniossss

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接