粗略看看JS函数

250 阅读5分钟

JS函数

1.函数的定义

​ 函数时JS里面比较有趣的一部分,因为函数实际上是对象。每个函数都是Function类型的实例,而Function也有属性和方法。因为函数是对象,所以函数名是指向函数对象的指针。函数的定义通常由三种方式:

函数声明的方式定义

这种方式也是用的最多的方式。

eg:

// 函数声明方式
function sum(num1,num2) {
    return num1+num2;
}

注意函数定义最后没有加分号。

函数表达式的方式定义

// 函数表达式
let sum = function (num1,num2){
    return num1+num2;
}

这种方式也不难理解,函数是对象嘛,对象自然就可以赋值,把函数赋值给一个变量,即该变量也指向了函数

使用Function构造函数

​ 函数是对象,对象可以通过构造函数建立,那么函数自然也可以通过构造函数建立。这个构造函数接收任意多个字符串,最后一个参数会被当成函数体,而之前的参数全部都是形参。

let sum = new Function("num1","num2","return num1+num2")

​ 但是不推荐这种语法定义函数,因为这段代码会被解释两次:第一次是将它当作常规 ECMAScript 代码,第二次是解释传给构造函数的字符串。这显然会影响性能。

2.箭头函数

​ 箭头函数是ES6里新增的定义函数表达式的方式。任何可以使用函数表达式的地方,都可以使用箭头函数:

let arrowSum = (a,b) =>{
    return a+b;
};
let functionExpressionSum = function (a,b){
    return a+b;
};
console.log(arrowSum(5,8))
console.log(functionExpressionSum(5,8))

​ 箭头函数因为其简单的语法,所以特比适合嵌入函数的场景。

let ints = [1,2,3];
console.log(ints.map((i) => {return i+1}))
console.log(ints.map(function (i){return i+1}))

​ 如果只有一个参数,那么也可以不用括号。只有没有参数,或者多个参数的情况下,才使用括号。

let double = (x) => { return 2 * x; };
let triple = x => { return 3 * x; };
// 没有参数需要括号
let getRandom = () => { return Math.random(); };
// 多个参数需要括号
let sum = (a, b) => { return a + b; };

​ 箭头函数函数也可以不用大括号,不使用大括号后面只能有一行代码,而且省略大括号会隐式返回这行代码的值:

let double = (x) => { return 2 * x; };
let triple = (x) => 3 * x;
// 可以赋值
let value = {};
let setName = (x) => x.name = "Matt";
setName(value);
console.log(value.name); // "Matt"

​ 箭头函数虽然语法简洁,但也有很多场合不适用。箭头函数不能使用 arguments、super 和 new.target,也不能用作构造函数。此外,箭头函数也没有 prototype 属性。

3.函数名

​ 函数名是指向函数的指针,所以它们跟其他对象指针变量具有相同的行为。这意味着一个函数名可以有多个名称。

function sum(num1, num2) {
    return num1 + num2;
}
console.log(sum(10, 10)); // 20
let anotherSum = sum;
console.log(anotherSum(10, 10)); // 20
sum = null;
console.log(anotherSum(10, 10)); // 20

​ 以上代码定义了一个名为 sum()的函数,用于求两个数之和。然后又声明了一个变量 anotherSum, 并将它的值设置为等于 sum。注意,使用不带括号的函数名会访问函数指针,而不会执行函数。此时, anotherSum 和 sum 都指向同一个函数。调用 anotherSum()也可以返回结果。把 sum 设置为 null 之后,就切断了它与函数之间的关联。而 anotherSum()还是可以照常调用,没有问题。

​ ES6的所有函数对象都会暴露一个只读的name属性,多数情况下 这个属性中保存的数就是一个函数标识符,或者说是一个字符串的变量名。即使函数没有名称,也会如实显示成空字符串,如果是使用Function构造函数创建,则会标识位anonymous。

function foo() {}
let bar = function() {};
let baz = () => {};
console.log(foo.name); // foo
console.log(bar.name); // bar
console.log(baz.name); // baz
console.log((() => {}).name); //(空字符串)
console.log((new Function()).name); // anonymous

4.理解参数

​ ECMAScript 函数的参数跟大多数其他语言不同。ECMAScript 函数既不关心传入的参数个数,也不 关心这些参数的数据类型。定义函数时要接收两个参数,并不意味着调用时就传两个参数。你可以传一 个、三个,甚至一个也不传,解释器都不会报错。哈哈哈哈哈,是不是觉得这个很神奇,看到这个不仅想起了我写c/c++的时候,形参和函数都得一一对应,那时候用vc6,没有语法检测难受死我了。

​ 之所以会这样,主要是因为 ECMAScript 函数的参数在内部表现为一个数组。函数被调用时总会接 收一个数组,但函数并不关心这个数组中包含什么。如果数组中什么也没有,那没问题;如果数组的元 素超出了要求,那也没问题。事实上,在使用 function 关键字定义(非箭头)函数时,可以在函数内 部访问 arguments 对象,从中取得传进来的每个参数值。

​ arguments 对象是一个类数组对象,但不是 Array 的实例,因此可以使用中括号语法访问其中的 元素(第一个参数是 arguments[0],第二个参数是 arguments[1])。而要确定传进来多少个参数, 可以访问 arguments.length 属性。

function sayHi1(name,message){
    console.log("hello"+name+","+message);
}
function sayHi(){
    console.log("Hello"+arguments[0]+","+arguments[1])
}
sayHi1()
sayHi()

​ 也可以通过argument对象得length属性检查传入参数得个数

function howManyArgs() {
    console.log(arguments.length);
}
howManyArgs("string", 45); // 2
howManyArgs(); // 0
howManyArgs(12); // 1

5.没有重载

​ ECMAScript 函数不能像传统编程那样重载。在其他语言比如 Java 中,一个函数可以有两个定义, 只要签名(接收参数的类型和数量)不同就行。如果在 ECMAScript 中定义了两个同名函数,则后定义的会覆盖先定义的。来看下面的例子:

function addSomeNumber(num) {
    return num + 100;
}
function addSomeNumber(num) {
    return num + 200;
}
let result = addSomeNumber(100); // 300

6.默认参数值

​ ES6之后就支持显示得定义默认参数,只需要在函数定义得参数后面用 = 就可以位参数赋一个默认值了;

function makeKing(name = 'Henry') {
    return `King ${name} VIII`;
}
console.log(makeKing('Louis')); // 'King Louis VIII'
console.log(makeKing()); // 'King Henry VIII'

默认参数值并不限于原始值或对象类型,也可以使用调用函数返回的值:

let romanNumerals = ['I', 'II', 'III', 'IV', 'V', 'VI'];
let ordinality = 0;
function getNumerals() {
    // 每次调用后递增
    return romanNumerals[ordinality++];
}
function makeKing(name = 'Henry', numerals = getNumerals()) {
    return `King ${name} ${numerals}`;
}
console.log(makeKing()); // 'King Henry I'