Передача конструктора в Array.map? - PullRequest
13 голосов
/ 28 июня 2011

Как я могу сделать что-то вроде этого:

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

Этот код выдает ошибку в Google V8:

SyntaxError: Unexpected number

Я также пытался:

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

с тем же результатом.

Ответы [ 4 ]

4 голосов
/ 12 мая 2015

Я думаю, что то, что искал 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 явный прототип.Что еще более важно, это выглядит как настоящий барьер, если у вас уже есть экосистема забавных пользовательских классов, которые не были написаны для совместимости с этим шаблоном.

Возможно, есть лучший выход.Может быть, кто-то опубликует это.

3 голосов
/ 28 июня 2011

Это то, что вы пытаетесь сделать?

var a = [1, 2, 3, 4];
a.map(function(obj) { return new Date(obj); });
0 голосов
/ 28 июня 2011

Date является функцией, поэтому Date.constructor является конструктором функции.Правильный вызов конструктора объекта Date выглядит следующим образом:

Date.prototype.constructor();

Или просто:

Date();

Проблема здесь заключается в создании массива объектов Date со значениями времени из массива a, но невозможно вызвать конструктор объекта Date и передать ему аргументы без оператора new (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' } ]
0 голосов
/ 28 июня 2011

Метод 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 со значениями времени из массив, передав объект Date конструктор для функции карты. Тем не мение в коде есть ошибка (см. комментарий к pst) - установите

Понятно, что-то вроде:

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