简体   繁体   中英

Proper use of const for defining functions

Are there any limits to what types of values can be set using const in JavaScript in particular functions? Is this valid? Granted it does work, but is it considered bad practice for any reason?

const doSomething = () => {
   ...
}

Should all functions be defined this way in ES6? It does not seem like this has caught on, if so.

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

Although using const to define functions seems like a hack, it comes with some great advantages that make it superior (in my opinion)

  1. It makes the function immutable, so you don't have to worry about that function being changed by some other piece of code.

  2. You can use fat arrow syntax, which is shorter & cleaner.

  3. Using arrow functions takes care of this binding for you.

example with function

 // define a function function add(x, y) { return x + y; } // use it console.log(add(1, 2)); // 3 // oops, someone mutated your function add = function (x, y) { return x - y; }; // now this is not what you expected console.log(add(1, 2)); // -1

same example with const

 // define a function (wow! that is 8 chars shorter) const add = (x, y) => x + y; // use it console.log(add(1, 2)); // 3 // someone tries to mutate the function add = (x, y) => x - y; // Uncaught TypeError: Assignment to constant variable. // the intruder fails and your function remains unchanged

It has been three years since this question was asked, but I am just now coming across it. Since this answer is so far down the stack, please allow me to repeat it:

Q: I am interested if there are any limits to what types of values can be set using const in JavaScript—in particular functions. Is this valid? Granted it does work, but is it considered bad practice for any reason?

I was motivated to do some research after observing one prolific JavaScript coder who always uses const statement for functions , even when there is no apparent reason/benefit.

In answer to " is it considered bad practice for any reason? " let me say, IMO, yes it is, or at least, there are advantages to using function statement.

It seems to me that this is largely a matter of preference and style. There are some good arguments presented above, but none so clear as is done in this article:

Constant confusion: why I still use JavaScript function statements by medium.freecodecamp.org/Bill Sourour, JavaScript guru, consultant, and teacher.

I urge everyone to read that article, even if you have already made a decision.

Here's are the main points:

Function statements have two clear advantages over [const] function expressions:

Advantage #1: Clarity of intent

When scanning through thousands of lines of code a day, it's useful to be able to figure out the programmer's intent as quickly and easily as possible.

Advantage #2: Order of declaration == order of execution

Ideally, I want to declare my code more or less in the order that I expect it will get executed.

This is the showstopper for me: any value declared using the const keyword is inaccessible until execution reaches it.

What I've just described above forces us to write code that looks upside down. We have to start with the lowest level function and work our way up.

My brain doesn't work that way. I want the context before the details.

Most code is written by humans. So it makes sense that most people's order of understanding roughly follows most code's order of execution.

There are some very important benefits to the use of const and some would say it should be used wherever possible because of how deliberate and indicative it is.

It is, as far as I can tell, the most indicative and predictable declaration of variables in JavaScript, and one of the most useful, BECAUSE of how constrained it is. Why? Because it eliminates some possibilities available to var and let declarations.

What can you infer when you read a const ? You know all of the following just by reading the const declaration statement, AND without scanning for other references to that variable:

  • the value is bound to that variable (although its underlying object is not deeply immutable)
  • it can't be accessed outside of its immediately containing block
  • the binding is never accessed before declaration, because of Temporal Dead Zone (TDZ) rules.

The following quote is from an article arguing the benefits of let and const . It also more directly answers your question about the keyword's constraints/limits:

Constraints such as those offered by let and const are a powerful way of making code easier to understand. Try to accrue as many of these constraints as possible in the code you write. The more declarative constraints that limit what a piece of code could mean, the easier and faster it is for humans to read, parse, and understand a piece of code in the future.

Granted, there's more rules to a const declaration than to a var declaration: block-scoped, TDZ, assign at declaration, no reassignment. Whereas var statements only signal function scoping. Rule-counting, however, doesn't offer a lot of insight. It is better to weigh these rules in terms of complexity: does the rule add or subtract complexity? In the case of const , block scoping means a narrower scope than function scoping, TDZ means that we don't need to scan the scope backwards from the declaration in order to spot usage before declaration, and assignment rules mean that the binding will always preserve the same reference.

The more constrained statements are, the simpler a piece of code becomes. As we add constraints to what a statement might mean, code becomes less unpredictable. This is one of the biggest reasons why statically typed programs are generally easier to read than dynamically typed ones. Static typing places a big constraint on the program writer, but it also places a big constraint on how the program can be interpreted, making its code easier to understand.

With these arguments in mind, it is recommended that you use const where possible, as it's the statement that gives us the least possibilities to think about.

Source: https://ponyfoo.com/articles/var-let-const

There are special cases where arrow functions just won't do the trick:

  1. If we're changing a method of an external API, and need the object's reference.

  2. If we need to use special keywords that are exclusive to the function expression: arguments , yield , bind etc. For more information: Arrow function expression limitations

Example:

I assigned this function as an event handler in the Highcharts API. It's fired by the library, so the this keyword should match a specific object.

export const handleCrosshairHover = function (proceed, e) {
  const axis = this; // axis object
  proceed.apply(axis, Array.prototype.slice.call(arguments, 1)); // method arguments
};

With an arrow function, this would match the declaration scope, and we won't have access to the API obj:

export const handleCrosshairHover = (proceed, e) => {
  const axis = this; // this = undefined
  proceed.apply(axis, Array.prototype.slice.call(arguments, 1)); // compilation error
};

There's another scenario where a constant function might be useful. If you have lots of constants in your code and need a function that specifically operates on those constants, it might be a good idea to turn that function into a constant itself:

const FLAG_ONE   = 1;
const FLAG_TWO   = 2;
const FLAG_THREE = 4;
// etc.

// resolves flag into string for debugging purposes:
const FLAG_NAME = flag => {
    switch ( flag ) {
        case FLAG_ONE: return 'one';
        // etc.
    }
};

It's not necessary in any way to define FLAG_NAME as a constant, but it will improve the legibility of your code to do so.

For my experience, I would suggest to use inheritance in ES6:

 class Animal { constructor(_type) { this._type = _type; } speak() { console.log(this._type === 'dog' ? 'Grruuu' : 'Meowww'); } } class Dog extends Animal { constructor() { super('dog'); } set speak(value) { return super.speak; // or throw any exception message here... // throw new Error('This method cannot be overriden.'); } get speak() { return (...args) => super.speak.call(this, ...args); } } class Cat extends Animal { constructor() { super('cat'); } set speak(value) { return super.speak; } get speak() { return (...args) => super.speak.call(this, ...args); } } let dog = new Dog(); dog.speak(); let cat = new Cat(); cat.speak(); cat.speak = 'foo'; cat.speak(); // Meowww 

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