简体   繁体   中英

Passing a constructor to Array.map?

How can i do something like this:

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

This code throws an Error on Google V8:

SyntaxError: Unexpected number

I'am also tried:

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

with the same result.

I think what the OP was looking for is strictly analogous to this:

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

I assume the reason is that this is really elegant, both in simple type-casting operations like above, and in more interesting tasks like converting one representation of something into a different representation, like so:

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

The problem with this appears to be that the new object, when created by passing the constructor to Array.map , is an extended version of the window ; that is, this within the constructor refers to the global scope, which sucks because (1) it wasn't your goal to hang props on window , and (2) the objects you create this way are not unique instances.

For what it's worth, the original type-casting example isn't all it's cracked-up to be, either, because:

strs[0] instanceof String
//=> false // UGH!

The only solution I've come up with so far requires writing the constructor differently -- which you obviously can't do for native types like Date :

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

var humans = animals.map(Human);

By defining the return value as a new object, we sever the connection between the global scope and this ; at least, that's what I assume is going on here. (You could also return a JSON literal instead of invoking Object .)

If I want these objects to have an interesting prototype, I have to define it separately and then attach it explicitly:

// 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);

In this case, it's not just as good to return JSON, because there don't appear to be any effective ways to set the prototype of an object-literal; and even if you could, you never want this to be true:

myObject.hasOwnProperty('prototype');
//=> true // only if myObject = { prototype: HumanProto }

I think the best way to ensure the new object has the desired prototype is to pass the would-be prototype as the argument to new Object() .

Is this pattern ideal? I don't know. It seems a little weird, since there are now two symbols associated with creating humans: Human the constructor function, and HumanProto the explicit prototype. More importantly, this seems like a real barrier if you've already got an ecosystem of fun custom classes that weren't written to be compatible with this pattern.

There is probably a better way out there. Maybe someone will post it.

Is this what you're trying to do?

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

The map method just iterates an array using the callback function provided (https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/array/map). So:

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

What are you trying to get from this:

Date.constructor(1);
Date.constructor(2);
Date.constructor(3);

Update from comment:

The problem here is to create an array of Date objects with time values from array a by passing the Date object constructor to a map function. Anyway there is a mistake in the code(see comment to pst) – set

I see, so something like:

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

The Date is a function, so the Date.constructor is a constructor of a function. Proper call of the Date object constructor looks like this:

Date.prototype.constructor();

Or just:

Date();

The problem here is to create an array of Date objects with time values from array a , but it is impossible to call the Date object constructor and pass an arguments to it without a new operator (ECMA-262 15.9.2) .

But it is possible for any object constructors that can be called as a functions with the same result as if i use the new operator (for instance the Error object constructor (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' } ]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM