【问题标题】:Passing a constructor to Array.map?将构造函数传递给 Array.map?
【发布时间】:2011-09-24 02:02:39
【问题描述】:

我该怎么做这样的事情:

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

此代码在 Google V8 上引发错误:

SyntaxError: Unexpected number

我也试过了:

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

结果相同。

【问题讨论】:

  • 该代码不应导致 SyntaxError。 (即使操作是有问题的:如果在调用期间未使用new,则构造函数是.. . 只是一个正常的功能。)这就是所有的代码吗?你能创建一个最小的 jsfiddle 测试用例吗?它在 FF/IE 中有 SyntaxError 吗?
  • 我的代码有错误。 Date.constructor 是 Function 对象的构造函数。 Date 对象的构造函数是 Date.prototype.constructor 或只是 Date。
  • 找到了答案。我会在几个小时后发布。

标签: javascript arrays map constructor prototype-programming


【解决方案1】:

map 方法只是使用提供的回调函数迭代一个数组(https://developer.mozilla.org/en/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 对象的时间值来自 数组 a 通过传递 Date 对象 映射函数的构造函数。反正 代码中有错误(见 对 pst 的评论)- 设置

我明白了,就像这样:

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

【讨论】:

  • 这里的问题是通过将 Date 对象构造函数传递给 map 函数来创建具有来自数组 a 的时间值的 Date 对象数组。无论如何,代码中有错误(请参阅对 pst 的评论)
【解决方案2】:

这是你想要做的吗?

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

【讨论】:

  • 可以,但是没有匿名函数。
  • 为什么?这是一个简单、有效的解决方案,不会产生任何重大开销。
  • 因为如果我有一个匿名函数用它的参数调用对象的构造函数并返回结果,那么直接调用构造函数是合理的。无论如何,在这种情况下这是不可能的(见我的回答)
【解决方案3】:

Date 是一个函数,所以 Date.constructor 是一个函数的构造函数。 Date 对象构造函数的正确调用如下所示:

Date.prototype.constructor();

或者只是:

Date();

这里的问题是使用数组a中的时间值创建一个日期对象数组, 但是如果没有new 运算符 (ECMA-262 15.9.2),就不可能调用 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' } ]

【讨论】:

  • 错了,这些只是对象,而不是 Error 的实例。如果你映射.prototype.constructor,那么在调用添加到原型的函数时,突然this 没有定义。
  • @Antoniossss 因为作者接受了他们自己的答案。
  • 正确的作者回答。
【解决方案4】:

我认为 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 进行比较,这是违反直觉的并且暴露了烟雾镜实现。毛。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-07
  • 2012-01-15
  • 1970-01-01
  • 2012-08-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多