Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add _.create method #1907

Merged
merged 1 commit into from
Dec 1, 2014
Merged

Add _.create method #1907

merged 1 commit into from
Dec 1, 2014

Conversation

jamiebuilds
Copy link
Contributor

_.create is useful as both a "polyfill" for Object.create and a utility for simple prototype inheritance.

Implementation is adapted from Lodash's _.create method. I've kept the baseCreate function for the usage in the methods @jdalton mentioned here.

function Person(name, occupation) {
  this.name = name;
  this.occupation = occupation;
}

function Developer(name) {
  Person.call(this, name, 'developer');
}

Developer.prototype = _.create(Person.prototype, {
  constructor: Developer
});

var developer = new Developer('Jeremy Ashkenas');
developer instanceof Developer;
// >> true

developer instanceof Person;
// >> true

Ref: @jdalton #1901 (comment)

@jdalton
Copy link
Contributor

jdalton commented Oct 23, 2014

Might make executeBound take advantage of this in this PR too.

@@ -90,6 +91,23 @@
return cb(value, context);
};

// An internal function for creating a new object that inherts from another.
var baseCreate = nativeCreate || (function() {
function Object() {}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using function Object() {} will cause an issue in an old browser (not sure off the top of my head which) with the use of the object literal on line 107. The literal will incorrectly use the local Object constructor instead. I'll see if I can dig up more detail on that.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use Ctor?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lodash has the same thing in baseCreate, are you just dropping support for that browser or are you doing something magical?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use Ctor?

I use context.Object() which is gross but I really like the way the object looks in the dev console when the constructor is Object 😸

image

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jdalton Hmm, I do like the Object {} I think it'd be better to use Ctor for this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are you just dropping support for that browser or are you doing something magical?

Ours is:

function baseCreate(prototype) {
  return isObject(prototype) ? nativeCreate(prototype) : {};
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jdalton I was referring to the Object in:

if (!nativeCreate) {
  baseCreate = (function() {
    function Object() {} // <<
    return function(prototype) {
      if (isObject(prototype)) {
        Object.prototype = prototype;
        var result = new Object;
        Object.prototype = null;
      }
      return result || context.Object();
    };
  }());
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jdalton Hmm, I do like the Object {} I think it'd be better to use Ctor for this.

I'm not too concerned about that, this will only be for environments without Object.create

result || context.Object();

That references global.Object

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yap, I avoided issues by doing context.Object() as mentioned above.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oooooooh, I misread what you said.

A wild understanding appears

@jamiebuilds
Copy link
Contributor Author

@jdalton Updated executeBound.

// created object.
_.create = function(prototype, props) {
var result = baseCreate(prototype);
return props ? _.extend(result, props) : result;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't read the spec for Object.create, is it own props or in props used

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright well we may have to defer to 2.0 in that case

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could I update it to use _.keys until then?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@thejameskyle I haven't opened an issue yet, but I would like to maintain _.extend behaviour to use in props and add a _.assign function for own properties.

/cc @akre54 @jashkenas @jdalton

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ya know my stance on it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jdalton and for those who don't? 👉😁👈

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and for those who don't? 👉😁👈

I think _.assign (aliased as _.extend) is enough and other cases can fall to _.create. It's worked well in my experience. That said I also offer things like _.keysIn, _.forIn, and _.valuesIn so devs can easily create their own mixins.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@megawac I've updated _.create to use _.keys for now so it isn't blocked.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, I'm definitely for adding keysIn, valuesIn and forIn

This was referenced Oct 24, 2014
// Creates an object that inherits from the given prototype object.
// If additional properties are provided then they will be added to the
// created object.
_.create = function(prototype, props) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this belongs in the Objects part of the codebase

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not already? The closest header above says "Object Functions"

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My bad got lost in the source

On Sat, Oct 25, 2014 at 9:08 PM, James Kyle [email protected]
wrote:

In underscore.js:

@@ -998,6 +1012,23 @@
return obj;
};

  • // Creates an object that inherits from the given prototype object.
  • // If additional properties are provided then they will be added to the
  • // created object.
  • _.create = function(prototype, props) {

It's not already? The closest header above says "Object Functions"


Reply to this email directly or view it on GitHub
https://github.com/jashkenas/underscore/pull/1907/files#r19378463.

@michaelficarra
Copy link
Collaborator

This looks ready to go. Can I get a 👍?

@megawac
Copy link
Collaborator

megawac commented Oct 26, 2014

Yeah, I think we should merge #1910 > #1908 > #1907 > #1901 as they are modularally dependent.

for (i = 0; i < length; i++) {
key = keys[i];
result[key] = props[key];
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mind updating to use _.assign?

@jamiebuilds
Copy link
Contributor Author

@megawac Updated.

// An internal function for creating a new object that inherts from another.
var baseCreate = nativeCreate ? function(prototype) {
return _.isObject(prototype) ? nativeCreate(prototype) : {};
} : function(prototype) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests pass with

nativeCreate || function(prototype) {
   ...

Are better tests neaded or is that correct

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah okay, Object.create() or Object.create(1) for instance will error. In that case should the default value be {} or Object.create(null)?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Returning {} make more sense, since it'll mirror the non-nativeCreate path where we can't create a prototype-less object.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For some reason, I didn't think of setting prototype to null in the non-nativeCreate path. Ignore my previous comment.

@jamiebuilds
Copy link
Contributor Author

Anything else needed here?

@megawac
Copy link
Collaborator

megawac commented Nov 24, 2014

@thejameskyle mind just adding a test to cover this case https://github.com/jashkenas/underscore/pull/1907/files#discussion_r20761788?

Also thoughts on return nativeCreate(_.isObject(prototype) ? prototype : null) vs what you're doing now?

@jamiebuilds
Copy link
Contributor Author

Updated with more tests and returning nativeCreate(null) instead of {}. @jdalton this will be a small difference in behavior from lodash.

@megawac Squashed so it should be good now.

@jamiebuilds
Copy link
Contributor Author

Gah whoops, hold off I messed up with Object.create(null). Fixed

@jamiebuilds jamiebuilds force-pushed the create branch 2 times, most recently from 440a3c0 to 7de85c4 Compare November 29, 2014 21:03
@jamiebuilds
Copy link
Contributor Author

@jridgewell [ref] updated thanks.

@jridgewell
Copy link
Collaborator

Wait a second, I knew there was a reason I thought we couldn't create a prototype-less object without nativeCreate.

function Test() {}
Test.prototype = null;
var t = new Test();
t.__proto__ === Object.prototype; // => true
t.toString(); // => "[object Object]"

Ignore my comment telling you to ignore my comment. It would be better to take @jdalton's path here and instead return a new object literal, since we can't mirror functionality in the non-nativeCreate path.

@jamiebuilds
Copy link
Contributor Author

@jridgewell After speaking to @megawac I decided to inline the nativeCreate. jsperf

@jridgewell
Copy link
Collaborator

Both inline and separated are fine with me. But I think 70a120d-L117 should be prototype = _.isObject(prototype) ? prototype : {};, since it would return consistent results for browsers with and without nativeCreate.

@jamiebuilds
Copy link
Contributor Author

@jridgewell I was thinking about making that just if (!_.isObject(prototype) return {};

@jridgewell
Copy link
Collaborator

That would be fine too. 😄

@megawac
Copy link
Collaborator

megawac commented Nov 30, 2014

Alright LGTM now

/cc @michaelficarra

jashkenas added a commit that referenced this pull request Dec 1, 2014
@jashkenas jashkenas merged commit 7cfd0f9 into jashkenas:master Dec 1, 2014
@jashkenas jashkenas added the fixed label Dec 1, 2014
@jamiebuilds jamiebuilds deleted the create branch December 1, 2014 17:40
@jridgewell jridgewell mentioned this pull request Feb 20, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants