admin管理员组

文章数量:1279015

For quite sometime I've been wondering about this question: when working with AngularJS, should I use directly the model object properties on the view or can I use a function to get the that property value?

I've been doing some minor home projects in Angular, and (specially working with read-only directives or controllers) I tend to create scope functions to access and display scope objects and their properties values on the views, but performance-wise, is this a good way to go?

This way seems easier for maintaining the view code, since, if for some reason the object is changed (due to a server implementation or any other particular reason), I only have to change the directive's JS code, instead of the HTML. Here's an example:

//this goes inside directive's link function
scope.getPropertyX = function() {
    return scope.object.subobject.propX;
}

in the view I could simply do

<span>{{ getPropertyX() }}</span>

instead of

<span>{{ object.subobject.propX }}</span>

which is harder to maintain, amidst the HTML clutter that sometimes it's involved. Another case is using scope functions to test properties values for evaluations on a ng-if, instead of using directly that test expression:

scope.testCondition = function() {
    return scope.obj.subobj.propX === 1 && scope.obj.subobj.propY === 2 && ...;
}

So, are there any pros/cons of this approach? Could you provide me with some insight on this issue? It's been bothering me lately, on how an heavy app might behave when, for example a directive can get really plex, and on top of that could be used inside a ng-repeat that could generate hundreds or thousands of its instances.

Thank you

For quite sometime I've been wondering about this question: when working with AngularJS, should I use directly the model object properties on the view or can I use a function to get the that property value?

I've been doing some minor home projects in Angular, and (specially working with read-only directives or controllers) I tend to create scope functions to access and display scope objects and their properties values on the views, but performance-wise, is this a good way to go?

This way seems easier for maintaining the view code, since, if for some reason the object is changed (due to a server implementation or any other particular reason), I only have to change the directive's JS code, instead of the HTML. Here's an example:

//this goes inside directive's link function
scope.getPropertyX = function() {
    return scope.object.subobject.propX;
}

in the view I could simply do

<span>{{ getPropertyX() }}</span>

instead of

<span>{{ object.subobject.propX }}</span>

which is harder to maintain, amidst the HTML clutter that sometimes it's involved. Another case is using scope functions to test properties values for evaluations on a ng-if, instead of using directly that test expression:

scope.testCondition = function() {
    return scope.obj.subobj.propX === 1 && scope.obj.subobj.propY === 2 && ...;
}

So, are there any pros/cons of this approach? Could you provide me with some insight on this issue? It's been bothering me lately, on how an heavy app might behave when, for example a directive can get really plex, and on top of that could be used inside a ng-repeat that could generate hundreds or thousands of its instances.

Thank you

Share Improve this question asked Nov 3, 2014 at 23:31 NunoMNunoM 3133 silver badges19 bronze badges 6
  • one con is that there are many many digest cycles so minimizing function calls is more performant. If you put one of those functions inside an ng-repeat and add a console.log in it you will see many more logs than you might have first expected – charlietfl Commented Nov 3, 2014 at 23:43
  • 1 I don't think creating functions for all of your properties is a good idea. Not just will there be more function calls being made every digest cycle to see if the function return value has changed but it really seems less readable and maintainable to me. It could add a lot of unnecessary code to your controllers and is sort of making your controller into a view model. Your second case seems perfectly fine, plex operations seems like exactly what you would want your controller to handle. – Jason Goemaat Commented Nov 3, 2014 at 23:58
  • That's just the thing that keeps me wondering (even though both your ments are perfectly valid and I agree with them): that way we have to maintain the object properties both on the view code (when we want to display them) and on the controller code (when we want to conditionally show content on the view, by doing parisons of these properties, for example). – NunoM Commented Nov 4, 2014 at 0:31
  • @JasonGoemaat Is there really a difference between using a property directly and using a simple getter function? Either one will be evaluated every digest cycle. Is there really enough overhead in the function call itself that would cause a noticeable difference in performance? Or do I have a false understanding of something? – dnc253 Commented Nov 4, 2014 at 20:13
  • Thinking ahead I see Angular 2.0 will be incorporating Object.observe() so there may be more of an impact then. – Jason Goemaat Commented Nov 4, 2014 at 20:21
 |  Show 1 more ment

3 Answers 3

Reset to default 8

I don't think creating functions for all of your properties is a good idea. Not just will there be more function calls being made every digest cycle to see if the function return value has changed but it really seems less readable and maintainable to me. It could add a lot of unnecessary code to your controllers and is sort of making your controller into a view model. Your second case seems perfectly fine, plex operations seems like exactly what you would want your controller to handle.

As for performance it does make a difference according to a test I wrote (fiddle, tried to use jsperf but couldn't get different setup per test). The results are almost twice as fast, i.e. 223,000 digests/sec using properties versus 120,000 digests/sec using getter functions. Watches are created for bindings that use angular's $parse.

One thing to think about is inheritance. If you unment the ng-repeat list in the fiddle and inspect the scope of one of the elements you can see what I'm talking about. Each child scope that is created inherits the parent scope's properties. For objects it inherits a reference, so if you have 50 properties on your object it only copies the object reference value to the child scope. If you have 50 manually created functions it will copy each of those function to each child scope that it inherits from. The timings are slower for both methods, 126,000 digests/sec for properties and 80,000 digests/sec with getter functions.

I really don't see how it would be any easier for maintaining your code and it seems more difficult to me. If you want to not have to touch your HTML if the server object changes it would probably be better to do that in a javascript object instead of putting getter functions directly on your scope, i.e.:

$scope.obj = new MyObject(obj); // MyObject class

In addition, Angular 2.0 will be using Object.observe() which should increase performance even more, but would not improve the performance using getter functions on your scope.

It looks like this code is all executed for each function call. It calls contextGetter(), fnGetter(), and ensureSafeFn(), as well as ensureSafeObject() for each argument, for the scope itself and for the return value.

return function $parseFunctionCall(scope, locals) {
  var context = contextGetter ? contextGetter(scope, locals) : scope;
  var fn = fnGetter(scope, locals, context) || noop;

  if (args) {
    var i = argsFn.length;
    while (i--) {
      args[i] = ensureSafeObject(argsFn[i](scope, locals), expressionText);
    }
  }

  ensureSafeObject(context, expressionText);
  ensureSafeFunction(fn, expressionText);

  // IE stupidity! (IE doesn't have apply for some native functions)
  var v = fn.apply
        ? fn.apply(context, args)
        : fn(args[0], args[1], args[2], args[3], args[4]);

  return ensureSafeObject(v, expressionText);
};

},

By contrast, simple properties are piled down to something like this:

(function(s,l /**/) {
    if(s == null) return undefined;
    s=((l&&l.hasOwnProperty("obj"))?l:s).obj;
    if(s == null) return undefined;
    s=s.subobj;
    if(s == null) return undefined;
    s=s.A;
    return s;
})

Performance wise - it is likely to matter little

Jason Goemaat did a great job providing a Benchmarking Fiddle. Where you can change the last line from:

setTimeout(function() { benchmark(1); }, 500);

to

setTimeout(function() { benchmark(0); }, 500);

to see the difference.

But he also frames the answer as properties are twice as fast as function calls. In fact, on my mid-2014 MacBook Pro, properties are three times faster.

But equally, the difference between calling a function or accessing the property directly is 0.00001 seconds - or 10 microseconds.

This means that if you have 100 getters, they will be slower by 1ms pared to having 100 properties accessed.

Just to put things in context, the time it takes sensory input (a photon hitting the retina) to reach our consciousness is 300ms (yes, conscious reality is 300ms delayed). So you'd need 30,000 getters on a single view to get the same delay.

Code quality wise - it could matter a great deal

In the days of assembler, software was looked like this:

A collection of executable lines of code.

But nowadays, specifically for software that have even the slightest level of plexity, the view is:

A social interaction between munication objects.

The latter is concerned much more about how behaviour is established via munication objects, than the actual low-level implementation. In turn, the munication is granted by an interface, which is typically achieved using the query or mand principle. What matters, is the interface of (or the contract between) collaborating objects, not the low-level implementation.

By inspecting properties directly, you are hit the internals of an object bypassing its interface, thus couple the caller to the callee.

Obviously, with getters, you may rightly ask "what's the point". Well, consider these changes to implantation that won't affect the interface:

  • Checking for edge cases, such as whether the property is defined at all.
  • Change from getName() returning first + last names rather than just the name property.
  • Deciding to store the property in mutable construct.

So even an implementation seemingly simple may change in a way that if using getters would only require single change, where without will require many changes.

I vote getters

So I argue that unless you have a profiled case for optimisation, you should use getters.

Whatever you put in the {{}} is going be evaluated A LOT. It has to be evaluated each digest cycle in order to know if the value changed or not. Thus, one very important rule of angular is to make sure you don't have expensive operations in any $watches, including those registered through {{}}.

Now the difference between referencing the property directly or having a function do nothing else but return it, to me, seems negligible. (Please correct me if I'm wrong)

So, as long as your functions aren't performing expensive operations, I think it's really a matter of personal preference.

本文标签: javascriptAngularJSBest practice model properties on view or function callsStack Overflow