第3章 新手的第一堂函数课:定义与参数

1. 函数与对象的区别

function foo(){}

class Cls {}

foo();  // 函数是可调用的
Cls();
// Uncaught TypeError: Class constructor Cls cannot be invoked without 'new'

2. 回调函数

  • 定义:将某个函数作为参数传入另一个函数,传入函数会在应用程序执行的未来某个时间点才执行,这个传入的函数被称为回调函数
    function foo(fn){ // 接收一个函数fn作为参数
        return fn(); // 返回时调用传入的函数
    }
    

3. 函数作为对象的意义

3.1 存储函数

// 需求:存储唯一函数集合(即添加的函数唯一且不重复)
var store = {
    nextId: 1,
    cache: {},
    add: function (fn) {
        if (!fn.id) {       // 检查该函数是否已经存在id属性
            fn.id = this.nextId++;
            this.cache[fn.id] = fn;
            return true;
        }
        return false;
    }
};

function fn_01(){}
function fn_01(){}
function fn_02(){}

console.log(store.add(fn_01));      // true
console.log(store.add(fn_01));      // false
console.log(store.add(fn_02));      // true
console.log(fn_01.id);      // 1
console.log(fn_02.id);      // 2

3.2 自记忆函数

  • 定义:当函数计算得到结果时就将该结果按照参数存储起来,如果另一个调用也使用相同的参数,则可以直接赶回上次存储的结果而不是再计算一边。这样可以避免重复且复杂的计算。通常用于动画中的计算、搜索不经常变化的数据或任何耗时的数学计算。
// 判断一个值是否是素数(除了1和它本身不再有其他因数)
function isPrime(value) {
    // 建立缓存
    if (!isPrime.answers) {
        isPrime.answers = {};
    }
    // 检查是否已经计算过这个值
    if (isPrime.answers[value] !== undefined) {
        return isPrime.answers[value];
    }
    var prime = value !== 0 && value !== 1;
    for (var i = 2; i < value; i++) {
        if  (value % i === 0) {
            prime = false;
            break;
        }
    }
    // 存储计算的值
    return isPrime.answers[value] = prime;
}

console.log(isPrime(0));        // false    
console.log(isPrime(1));        // false
console.log(isPrime(5));        // true
console.log(isPrime(5));        // true
console.log(isPrime.answers);   // {0: false, 1: false, 5: true}

4. 函数定义

4.1 函数声明和函数表达式

  • 函数声明
function fn(params) { /** 函数体 */ }
  • 函数表达式
var fn = function(params) { /** 函数体 */ }
  • 立即调用函数表达式(IIFE)
// 这一特性可以模拟模块化
(function(val) {console.log(val)})(3);      // 3

(function(val) {console.log(val)}(3));  // 3  不推荐使用

// 如果去掉包裹函数表达式的括号会报错,
// 因为这个语句以function开头,解析器会认为这是一个函数声明。
// 每个函数声明必须有名字,而这里没有
// 所以函数报错

// 立即调用函数表达式的常用形式(使用一元操作符将函数声明改为表达式)
// 不影响最终结果
+function(val) {console.log(val)}(3);
-function(val) {console.log(val)}(3);
!function(val) {console.log(val)}(3);
~function(val) {console.log(val)}(3);

4.2 箭头函数(ES6)

var vals = [1, 5, 6, 2, 9, 6, 0];

// 箭头函数最简式:param => expression
vals.sort((a, b) => a - b);

console.log(vals);
// [0, 1, 2, 5, 6, 6, 9]

var getName = (() => "Wango")();
console.log(getName);   // Wango
// 箭头函数中只有一条语句且没有{}代码块和return时,返回值为这一条语句的结果

var getName = (() => {"Wango"})();
console.log(getName);   // undefined
// 箭头函数的函数体时一个代码块时,值是return语句的值,
// 这里没有return语句,所以值是undefined

函数构造函数(主要用在动态创建和执行代码)和生成器函数暂未涉及

5 函数的参数

  • 实参:调用函数时所传递给函数的值
  • 形参:定义函数时所列举的变量
  • 实参的数量大于形参时,额外的实参不会赋值给任何形参
  • 剩余参数(ES6)
// 用...接收多个参数,并将参数放入remaining数组中
// 只有最后一个参数才能时剩余参数,
// 否则会:SyntaxError: Rest parameter must be last formal parameter
function add(first, ...remaining) {
    var sum = first;
    for (var i = 0; i < remaining.length; i++) {
        sum += remaining[i];
    }
    return sum;
}

console.log(add(1, 2, 3));
  • 默认参数(ES6)
// ES6之前处理默认参数的方法
function setInfo(name, age, gender) {
    gender = typeof gender === "undefined" ? "Unknown" : gender;
    // 或者使用更简洁的判定方式
    // gender = gender || "Unknown";
    return {
        name: name,
        age: age,
        gender: gender
    };
}

console.log(setInfo("Wango", 24, "male"));
// {name: "Wango", age: 24, gender: "male"}
console.log(setInfo("Lily", 25));
// name: "Lily", age: 25, gender: "Unknown"}

// ES6处理默认参数的方法
// 为形参赋值
// 若指定了实参的值,默认值会被覆盖,否则使用默认值
function setInfo(name, age, gender = "Unknown") {
    return {
        name: name,
        age: age,
        gender: gender
    };
}

console.log(setInfo("Wango", 24, "male"));
// {name: "Wango", age: 24, gender: "male"}
console.log(setInfo("Lily", 25));
// name: "Lily", age: 25, gender: "Unknown"}

对后面的默认参数赋值时可以引用前面的默认参数

原文地址:https://www.cnblogs.com/hycstar/p/13997350.html