Proper Use of Const for Defining Functions

Proper use of const for defining functions

There's no problem with what you've done, but you must remember the difference between function declarations and function expressions.

A function declaration, that is:

function doSomething () {}

Is hoisted entirely to the top of the scope (and like let and const they are block scoped as well).

This means that the following will work:

doSomething() // works!
function doSomething() {}

A function expression, that is:

[const | let | var] = function () {} (or () =>

Is the creation of an anonymous function (function () {}) and the creation of a variable, and then the assignment of that anonymous function to that variable.

So the usual rules around variable hoisting within a scope -- block-scoped variables (let and const) do not hoist as undefined to the top of their block scope.

This means:

if (true) {
doSomething() // will fail
const doSomething = function () {}
}

Will fail since doSomething is not defined. (It will throw a ReferenceError)

If you switch to using var you get your hoisting of the variable, but it will be initialized to undefined so that block of code above will still not work. (This will throw a TypeError since doSomething is not a function at the time you call it)

As far as standard practices go, you should always use the proper tool for the job.

Axel Rauschmayer has a great post on scope and hoisting including es6 semantics: Variables and Scoping in ES6

Use of 'const' for function parameters

The reason is that const for the parameter only applies locally within the function, since it is working on a copy of the data. This means the function signature is really the same anyways. It's probably bad style to do this a lot though.

I personally tend to not use const except for reference and pointer parameters. For copied objects it doesn't really matter, although it can be safer as it signals intent within the function. It's really a judgement call. I do tend to use const_iterator though when looping on something and I don't intend on modifying it, so I guess to each his own, as long as const correctness for reference types is rigorously maintained.

const best practice in function declaration

Assuming that you are talking about member functions (non member functions cannot be const ever), you should write const-correct code always, and that means that each and every function that does not modify the visible state of the object should be const.

Even if you don't ever create a constant object of the type, in many cases you will pass the object to a function through const references and in that case, only const member functions can be called on the object (Incidentally this means that you should pass the object by constant reference to any function that does not need to change the state of the received object)

Anonymous function VS const function - javascript

Anonymous function VS const function

Presumably you're comparing

var func = function() { };
// or
let func = function() { };

with

const func = function() { };

The primary reason for doing that isn't optimization. It's documenting via code that you never intend to change func, and having the engine protect you from accidentally doing so. (I should also note that as of ES2015, none of those functions is anonymous. They all have the name func, because ES2015 adds rules for assigning names to functions created via "anonymous" function expressions based on context, including a rule for simple assignments like the above.)

But regarding optimization:

As with most JavaScript optimization questions, the answer is: It depends. In theory, using const if you never mean to change the value in func means that the JavaScript engine has the option of optimizing on the assumption that the value will never change. Note that it doesn't exempt the engine from handling the possibility the symbol will be shadowed in a nested scope or similar.

Whether the engine actually does optimize in a meaningful way based on that knowledge the value wont' change will depend on the engine's implementation, and likely will vary from engine to engine.

Whether that makes looking up the function via the constant in order to call it any faster than looking it up via the variable will depend on the engine's implementation, as well.

Whether any absolute difference translates into actual gains in your program will depend on the above, how your program is structured, how frequently you're using the function, how the lookup time compares with what the function is actually doing (e.g., swamping), etc.

It depends. :-)

If you run into a situation where you think the lookup time is causing a real-world problem, profile it and see if it makes a difference.


Re your edit:

By anonymous function declaration I mean something like this:

function myFunction(fruit){
alert('I like ' + fruit);
};

and by const I mean this:

const myfunction = (fruit) => alert('I like ' fruit);

That first one isn't an anonymous function. (Actually, neither of them is.) It's a function named myFunction, created via a function declaration. (And being a declaration, there's no need for the ; at the end.) Function declarations cannot create anonymous functions,¹ the name is a required part of the declaration.²

That said, it doesn't really matter, as once the function is created (which happens at a different time from the expressions I showed above), it behaves very much like the var func = ... example in terms of how func is resolved, whether you can change func, etc.

Your second example varies from your first in not one, but three important ways:

  1. It assigns the function reference to a constant.

  2. It uses an arrow function, rather than a function function (which for lack of a better term I'm going to call a "simple" function).

  3. Your arrow function version returns the result of calling alert (because you're using a concise body on the arrow function). Your declaration version doesn't.

We've already dealt with any performance aspect of #1. #3 is, I suspect, unlikely to matter.

Re #2 (it being an arrow function): Calling an arrow function requires less work than calling a simple function (in theory). The engine doesn't have to set up the arguments pseudo-object, doesn't have to create a this binding. But if the arrow function uses this, it requires more work to look it up (it's like a variable in the outer scope the function closes over). But again, that's the theory; engines can optimize as long as side-effects are not apparent. For instance, if you don't use arguments in your code, modern engines avoid creating it anyway, even for simple functions. And I'd expect optimization around arrow function usage of this to be pretty good, since the this they see cannot change once the function exists.

So (wait for it): It depends.


¹ "Function declarations cannot create anonymous functions" - There's one thing that seems like an exception that rule, which is that export default function() { /*...*/ }; is a function declaration (yes, really) with no explicit name; the name default is used, though, so it doesn't create an anonymous function.

² "...the name is a required part of the declaration..." Except in the export default example above. That's the one exception.

C - Which is the advantage for the user of const in parameters of functions that are not pointers?

Some people here say that you should write const if you won't change the variable - as you can declare any local variable const. (The main goal is to keep you from accidently changing the variable, but also may help the compiler to optimize your code)

Other people say that you shouldn't declare function parameter const, because it will expose some of the implementation in the API.

I think that they both are right! That is why you can omit the const in function declaration: You can write void func(int); in a header, but implement it void func(const int i) {} in the code file.

Proper way of defining functions in ES6?

Using a function declarations means that you'll deal with function declaration hoisting in which all functions are processed before any code execution so you can technically use them before they are defined, as they are hoisted up to the top, and you won't have block scoping, as they would be function scoped:

foo(); //can be used before declaration appears!

function foo() {
{ //new block
function bar() {
alert(1);
}
}
bar(); //no problem, is function scoped
}
foo(); //alerts 1!

With function expressions, there will be no hoisting and you'll have block scoping if you use let or const:

function foo() {
{ //new block
const bar = () => {
alert(1);
}
}
bar(); //ReferenceError: Can't find variable: bar
}
foo();

Finally, using a named function expression allows for the error to provide a named for the function, instead of just an anonymous function for better debugging. Thus: prefer the last choice. It has expected block scoping, (it still has hoisting but it's invalid to access it before declaration in code), and it allows for a better debugging experience.

Also, don't forget that arrow functions do not bind their own this but take the this of the enclosing context! Function declarations always have a this referring to the global object window or undefined in strict mode.

Why is const unnecessary in function declarations in header files for parameters passed by value?

The statement you quote is a bit misleading, because in C, all arguments are passed by value.* I suppose it is trying to distinguish between the arguments themselves and, for the special case of arguments that are pointers, their referents.

In any event, the point is that const-qualifying a function parameter in the function declaration conveys no information whatever to callers. Regardless of such qualification, the function cannot modify the caller's copy of any argument anyway, because arguments are passed by value.

*Note, however, that arrays are never passed at all. In function call expressions, as in most contexts, array values "decay" to pointers, and those pointers are passed by value. This produces an effect similar, but not identical, to what you would have if arrays were passed by reference.

Using 'const' in class's functions

A const method can be called on a const object:

class CL2
{
public:
void const_method() const;
void method();

private:
int x;
};

const CL2 co;
CL2 o;

co.const_method(); // legal
co.method(); // illegal, can't call regular method on const object
o.const_method(); // legal, can call const method on a regulard object
o.method(); // legal

Furthermore, it also tells the compiler that the const method should not be changing the state of the object and will catch those problems:

void CL2::const_method() const
{
x = 3; // illegal, can't modify a member in a const object
}

There is an exception to the above rule by using the mutable modifier, but you should first get good at const correctness before you venture into that territory.

Why should I not use `const` in this simple function?

From the caller's perspective, both the first and the second form are the same.

Since the integers are passed by value, even if the function modifies a and b, the modified values are copies of the original and won't be visible to the caller.

However, from the function implementer's perspective there's a difference. In fact, in the second form:

int add(const int a, const int b)

you will get a compiler error if you try to modify the values of a and b inside the function's body, as they are marked as const.

Instead, you can change those values if you omit the const.

Again, those modifications will not be visible to the caller.

What is meant with const at end of function declaration?

A "const function", denoted with the keyword const after a function declaration, makes it a compiler error for this class function to change a member variable of the class. However, reading of a class variables is okay inside of the function, but writing inside of this function will generate a compiler error.

Another way of thinking about such "const function" is by viewing a class function as a normal function taking an implicit this pointer. So a method int Foo::Bar(int random_arg) (without the const at the end) results in a function like int Foo_Bar(Foo* this, int random_arg), and a call such as Foo f; f.Bar(4) will internally correspond to something like Foo f; Foo_Bar(&f, 4). Now adding the const at the end (int Foo::Bar(int random_arg) const) can then be understood as a declaration with a const this pointer: int Foo_Bar(const Foo* this, int random_arg). Since the type of this in such case is const, no modifications of member variables are possible.

It is possible to loosen the "const function" restriction of not allowing the function to write to any variable of a class. To allow some of the variables to be writable even when the function is marked as a "const function", these class variables are marked with the keyword mutable. Thus, if a class variable is marked as mutable, and a "const function" writes to this variable then the code will compile cleanly and the variable is possible to change. (C++11)

As usual when dealing with the const keyword, changing the location of the const key word in a C++ statement has entirely different meanings. The above usage of const only applies when adding const to the end of the function declaration after the parenthesis.

const is a highly overused qualifier in C++: the syntax and ordering is often not straightforward in combination with pointers. Some readings about const correctness and the const keyword:

Const correctness

The C++ 'const' Declaration: Why & How



Related Topics



Leave a reply



Submit