0

Recently I've been learning node.js, and I've found two code fragments like this:

Fragment 1:

const fs = require('fs')
fs.readFile("content.txt", "utf8", (err, msg) => {
    console.log(msg);
})

Fragment 2:

const fs = require('fs')
fs.readFile("content.txt", (err, msg) => {
    console.log(msg);
})

They have only one difference that Fragment 1 passed 'utf8' as the second parameter while Fragment 2 skips to pass it. And although they have different results, both of them can function normally without a syntax error.

So I wonder how a javascript method is able to skip to pass the parameter? And how can I define a method/function like this?

JustAG33K
  • 1,403
  • 3
  • 13
  • 28
  • 2
    The count of arguments is optional when calling a function. Usually functions check (such functions you can pass a varying count of arguments) the arguments by type, and then execute alternative code depending on the type of a specific argument. – Teemu Sep 10 '21 at 10:29
  • The function will determine the [arity](https://cgi.cse.unsw.edu.au/~cs2041/doc/MDN_javascript_reference/Web/JavaScript/Reference/Global_Objects/Function/arity.html) of the arguments, and test to see if the second argument is a function or not, and then proceed based on that information. – Andy Sep 10 '21 at 10:30
  • @Teemu Thanks for your answer, you answer solved my confusing. – Paper_Folding Sep 10 '21 at 11:25

2 Answers2

3

You can achieve this effect in your own code by determining the arity within your method. This might be as simple as just checking the number of arguments, or it might be that you need to check the type of each/some argument(s)

An example:

function myArityAwareFunction(){
    if(arguments.length == 2)
        console.log("2 arguments", ...arguments)
    else if(arguments.length == 3)
        console.log("3 arguments", ...arguments)
    else
        throw ("This method must be called with 2 or 3 arguments");
}


myArityAwareFunction("foo","bar");
myArityAwareFunction("foo","bar","doo");
myArityAwareFunction("This will fail");
Jamiec
  • 133,658
  • 13
  • 134
  • 193
  • I thought javascript must have implemented some nice syntax for us, but I never thought like this easy with just a bunch of if and else if, but would things get more complicated when there comes quite a lot parameters? – Paper_Folding Sep 10 '21 at 11:10
  • @Paper_Folding there are also optional arguments with a default: https://jsfiddle.net/876Lv5pf/ – Jamiec Sep 10 '21 at 11:12
  • @Paper_Folding please read this https://stackoverflow.com/questions/4633125/is-it-possible-to-get-all-arguments-of-a-function-as-single-object-inside-that-f – Umer Abbas Sep 10 '21 at 11:16
0

With arrow function:

const fn = (...args) => {
  console.log(args.length);
  if (args.length < 2) {
    throw Error('missing arguments');
  } else if (args.length === 2) {
    /* ... */
  } else if (args.length === 3) {
    /* ... */
  } else {
    /* ... */
  }
};

or with switch block

const fn = (...args) => {
  console.log(args.length);
  switch(args.length) {
    case 0: /* ... */ break;
    case 1: /* ... */ break;
    /* ... */
    default: /* ... */ break;
  }
};

But it's much better to validate argument type rather than do something depend on args.length

So for your example with the fs code could look like this:

const readFile = (...args) => {
  if (typeof args[0] === 'string') {
    /* ... */
  } else {
    throw TypeError('Path must be a string');
  }
  if (typeof args[1] === 'string') {
    /* ... */
    if (typeof args[2] === 'function') {
      /* ... */
    } else {
      throw TypeError('Callback must be a function');
    }
  } else if (typeof args[1] === 'function') {
    /* ... */
  } else {
    throw TypeError('Callback must be a function');
  }
};
readFile({}); // TypeError: path must be a string
readFile('test.txt', 'utf-8', {}); // TypeError: Callback must be a function
readFile('test.txt', {}); // TypeError: Callback must be a function

readFile('test.txt', () => {}); // success
readFile('test.txt', 'utf-8', () => {}); // success
Max Starling
  • 967
  • 8
  • 8