Can-define value vs Value

I’ve noticed that for can-define using “value” to define a default value for a property is run only once when the module is loaded. For example if I add a tag to the dom, then destroy it, then add another of the same tag, the property will have the same value as the tag that was destroyed (i.e. not the default value). Using a function or Value the default will get loaded each time an tag is added to the dom.

Is this the intended behaviour of can-define? (is it a feature or a bug?). The reason I ask is I don’t way to rely on the functionality if it could be reverted in later version.

I’m not actually sure what you’re describing.

Can you describe the behavior independent of a component?

I think what you mean is that with something like

property : {value: function(){ 
  console.log("RUNNING");
  return 1;
})

RUNNING will only appear the first time that obj.property is actually read. It doesn’t happen when new Type() is created.

Yes, this is intended. I’m not 100% sure you can rely on it in all future versions, but it’s a nice performance improvement that we made work for 3.0.

Sorry, I probably should have mentioned that this is in a can.Component.

What I’m saying is if I do:

property: {
   value: new can.List()
}

the can.List does not ever seem to get re-instantiated when adding/removing component tags from the dom

if I do:

property: {
   value: function() {
     return new can.List();
   }
}

or

property: {
  Value: can.List
}

then it does

value should never have an object as its value. It should only have primitives or functions that return the value.

can.List does not ever seem to get re-instantiated

It can’t be re-initialized.

Here’s what the docs say:

If the default value should be an object of some type, it should be specified as the return value of a function (the above call signature) so that all instances of this map don’t point to the same object. For example, if the property value above had not returned an empty array but instead just specified an array using the next call signature below, all instances of that map would point to the same array (because JavaScript passes objects by reference).

cc @chasen (we should probably make this more clear or absolutist).

Btw, that it’s in a component doesn’t matter. DefineMap behaves the way it does regardless if it’s in a component or not.

I see. I probably should have read the documentation more carefully :slight_smile:

Just to be clear, I wanted to make sure that it was okay to do

property: {
  value: new can.List()
}

and have it always refer to the same instance. For example if the Map if for a “page” and you navigate away from the page and then back, some values would persist. Is this not recommended?

I wouldn’t recommend doing that; instead, I would pass the value/singleton into your component instead of depending on each instantiation of the component to reuse the same value. The latter just seems brittle and we might add a warning in the future not to pass an object to value.

Question: this is something that we’ve been doing while converting a project to using DefineMaps, but we always use it along with Type/type to ensure that the original object is converted into something else (essentially using value as default constructor arguments). Is this an acceptable case? Can we make a distinction about whether the object is a plain object versus a constructed one (especially an observable type/map-like)?