函数function

意义:实现功能的复用
和变量类似,必须先定义后使用;

定义函数

  1. 具名函数
function 函数名(){
  函数体语句
}
  1. 匿名函数
var 变量名=function(){
   函数体语句
}

调用函数

指执行函数体中的所有语句
完毕后,语句执行权交还给主程序
语法:函数名()

递归-在函数内调用自身

边界条件(递归出口):确定递归何时终止
递归模式(递归体):大问题如何分解为小问题

function factorial(n) {
            //递归出口
            if (n == 1)
                return 1;
            else return n * factorial(n - 1);
        }
        var result = factorial(6);
        console.log(result);

应用–深克隆

   function DeepClone(arr) {
        var result = [];
        for (var i = 0; i < arr.length; i++) {
            if (Array.isArray(arr[i]))
                result.push(DeepClone(arr[i]));
            else
                result.push(arr[i]);
        }
        return result;
    }
    var arr1 = [1, 2, 3, 5, [7, 8, [9, 10, 11]]];
    console.log(DeepClone(arr1));

函数参数

函数内的待定值

形参

用于函数定义
形参只在函数内部有效;
调用结束返回主调函数后,不能再使用该形参变量
机理:函数被调用时才分配内存单元;调用结束时即刻释放所分配的内存单元

实参

用于主调函数,进入被调函数后实参不能使用
可以是常量、变量、表达式、函数等;
调用时要传入实参
实参可多可少,多了没有形参接收,少了对应的形参值为undefined;
undefined进行任何运算结果是NaN

//数组排序
//函数作为参数
var arr = [33, 44, 2, 11];
        arr.sort(function compareNumbers(a, b) {
            return a - b;
        });
        console.log(arr);//[2, 11, 33, 44]

注意:当形参和实参不是指针类型,函数运行时形参和实参是不同的变量即两者在内存中位于不同的位置;形参将实参的内容复制一份,在该函数运行结束的时候形参被释放,而实参内容不会改变

function SumFromAToB(a, b) {
        for (var i = a, sum = 0; i <= b; i++) {
            sum += i;
        }
        console.log(a + '到' + b + '之间所有的整数和为' + sum);
    }
    SumFromAToB(3, 7);

arguments

是类数组对象——有length属性;不能调用数组方法
接收实参列表;属性名按传入实参的序列——第1个参数的属性名是’0’,第2个参数的属性名是’1’…

//若函数传递了三个实参,引用如下:
arguments[0]
arguments[1]
arguments[2]

模拟重载
注:js是弱类型的语言无重载机制,可用arguments模拟重载效果
强类型语言的重载:
C++允许在同一范围中声明几个功能类似的同名函数,但是这些同名函数的形参(包括参数的个数、类型或顺序)必须不同;实现了同一个函数完成不同功能

问题:传入不定数量的参数,求和

function add() {
    var sum = 0;
    for (; arguments.length --;) {
        sum += arguments[ arguments.length];
    }
    return sum;
}
console.log(add(1, 2, 3));   //6
console.log(add(1, 3));     //4
console.log(add(1, 2, 3, 5, 6, 2, 7));   //26

其中的循环语句等价于

for (i = 0; i <= arguments.length - 1; i++) {
	sum += arguments[i];
}

参数通过值传递

意味着将参数传递给函数时,函数接收的是原始值的副本;故若函数修改该参数,仅改变副本,而原始值保持不变
注:
对象通过引用传递
意味着当将一个参数传递给一个函数时,函数接收的是原始值的内存地址;故若函数修改该参数,原始值也随之改变

返回值

可被当作普通值,写在任何可写的地方

   function TwoNumSum(a, b) {
        return a + b;
    }
    var result = TwoNumSum(2, 3);
    console.log(TwoNumSum(2, TwoNumSum(2, 3)));

函数有且仅有一个返回值

   function IsEven(a) {
        flag = 0;
        if (a % 2 == 0)
            return ture;
        else
            //else可省——原因:函数只有一个返回值
            // 满足条件时,第一条return语句会被执行,之后的return语句不会被执行
            //不满足条件时,第一条return语句不会被执行,第二条执行
            return false;
    }
    console.log(IsEven(3));

作用域相关

JS是函数级作用域编程语言,变量只在定义所在的函数内部有意义

function fun() {
        var a = 10;//变量a在fun函数中被定义,故变量a只在fun函数内部有定义
        //fun函数就是变量a的作用域,变量a就被称作局部变量
        return a;
    }
    fun();
    console.log(a);//报错 a is not defined

全局变量

指不定义在任何函数内部的变量
特点:在任何函数内都可被访问和更改

变量初次赋值时,不加var会是全局变量

function fun() {
    a = 3;
}
fun();
console.log(a);//3

局部变量

指定义在函数内部的变量
形参是局部变量

  var a = 10;
    function fun(a) {
        a++;
        console.log(a);//8
    }
    fun(7)
    console.log(a);//10

遮蔽效应

局部变量与全局变量同名,则局部变量会遮蔽全局变量

var a = 10;
    function fun() {
        var a = 5;
        a++;
        console.log(a);//6
    }
    fun()
console.log(a);//10

另:考察预解析

var a = 10;
function fun() {
    //局部变量a声明被提升,此时a值为undefined
    //自加1,值为NaN
    a++;
    //被赋新值
    var a = 5;
    console.log(a);//5
}
fun();
console.log(a);//10

局部函数(函数嵌套)

指定义在函数内的函数

function fun() {	
	 function inner() {
	     alert('您好');
	 }
	 inner();
}
fun();

作用域链

变量的查找规则:从当前层开始,逐层向上

var a = 1, b = 2;
function fun() {
    var c = 3;
    function inner() {
        var a = 4;
        d = 5;
        console.log(a, b, c, d);//4,2,3,5
    }
    inner()
}
fun();

闭包

是函数本身和该函数声明时所处环境状态(周围的全局变量,局部变量)的组合

如何观察

在函数中引用另一函数作用域中的变量,即将函数“换一个地方”执行

记忆性

问题:
创建体温检测函数checkTemp(n),可以检查体温n是否正常,函数会返回布尔值。
但是,不同的小区有不同的体温检测标准,比如A小区体温合格线是37.1℃,而B小区体温合格线是37.3℃

  function creatCheckTemp(standardTemp) {
        function checkTemp(n) {
            if (n <= standardTemp)
                alert('体温正常');
            else
                alert('体温偏高');
        }
        return checkTemp;
    }
    var checkTemp_A = creatCheckTemp(37.1);
    checkTemp_A(37.2);
    checkTemp_A(37.0);
    var checkTemp_B = creatCheckTemp(37.3);
    checkTemp_B(37.2);
    checkTemp_B(37.0);

模拟私有变量

问题:定义变量a,要求是a只能进行指定操作(如加1、乘2)
注:在Java、C++等语言中,有私有属性的概念,但JavaScript中只能用闭包来模拟

function fun() {
        var a = 0;
        return {          
            getA: function () {
                return a;
            },
            add: function () {
                a++;
            },
            mul: function () {
                a *= 2;
            }
        };
    }
var obj = fun();
//1.想在fun函数外使用变量a,调用对象的getA()方法
console.log(obj.getA()); //0
//2.想让变量a进行+1操作,调用对象的add()方法
obj.add();
obj.add();
obj.add();
console.log(obj.getA()); //3
// 3.想让变量a进行*2操作,调用对象的mul()方法
obj.mul();
console.log(obj.getA());//6

不能滥用闭包

否则会造成网页的性能问题,严重时可能导致内存泄露。
所谓内存泄漏是指程序中己动态分配的内存由于某种原因未释放或无法释放

  function addCount() {
        var count = 0;
        return function () {
            count++;
            console.log(count);
        }
    }
    // 两次调用外部函数,获得两个内部函数,互不相关,产生两个独立闭包
    var fun1 = addCount();
    var fun2 = addCount();
    fun1();//1
    fun2();//1
    fun2();//2
    fun1();//2

立即执行函数IIFE

immediately invoked Function Expression立即调用函数表达式

(function () {
        statements
 })();

包裹函数的括号对功能:将函数变为表达式
最后括号对功能:调用

用于变量赋值

常规写法:不易看出在定义title变量;不紧凑

var age = 12;
var sex = '男';
if (age < 18) {
    var title = '小朋友';
}
else {
    if (sex == '男')
        var title = '先生';
    else
        var title = '女士';
}

IIFE写法

  var title = (function () {
    if (age < 18) {
      return "小朋友";
    } else {
      if (sex == "男") return "先生";
      else return "女士";
    }
  })();

模拟块级作用域

可封装一些临时/局部变量,避免污染全局变量
题目:

<body>
    <ul id="list">
        <li>公司简介</li>
        <li>联系我们</li>
        <li>营销网络</li>
    </ul>
</body>
<script>
    var list = document.getElementById("list");
    var li = list.children;
    for (var i = 0; i < li.length; i++) {
        li[i].onclick = function () {
            alert(i);  // 结果总是3.而不是0,1,2
        }
    }
</script>

机理:
for循环中的i对function来说是全局变量
点击事件是异步,for运行完后i已经变成3,用户才点击
解决方法1:IIEF
给每个li创建一个独立的作用域

<script>
var list = document.getElementById("list");
var li = list.children;
for (var i = 0; i < li.length; i++) {
     (function (j) {
         li[j].onclick = function () {
             alert(j);
         }) (i); //把实参i赋值给形参j
     }
}
</script>

解决方法2:ES6中let块级作用域

<script>
var list = document.getElementById("list");
var li = list.children;
for (let i = 0; i < li.length; i++) {
    li[i].onclick = function () {
        alert(i);  // 结果是0,1,2
    }
}
</script>

封装函数-喇叭花数

每一位数字的阶乘之和恰好等于它本身的三位数,即abc=a!+b!+c!

//封装阶乘函数
function factorial(n) {
    for (var i = 1, result = 1; i <= n; i++) {
        result *= i;
    }
    return result;
}
//穷举法
for (var i = 100; i <= 999; i++) {
    var i_str = i.toString()
    // abc分别表示个位十位百位
    var a = i_str[0], b = i_str[1], c = i_str[2];
    if (factorial(a) + factorial(b) + factorial(c) == i)
        console.log(i);
}

封装函数-斐波那契数列

斐波那契数列:1、1、2、3、5、8、13、21
规律:数列下标为0和1的项值都是1,从下标为2的项开始,每项等于前两项之和

function fib(n) {
  if (n == 1 || n == 2)
      return 1;
  return fib(n - 1) + fib(n - 2);
  }
console.log(fib(3));
console.log(fib(7));
//打印斐波那契数列前15项
for (var i = 1; i <= 15; i++)
console.log(fib(i));

预解析阶段

提升顺序:

  1. 函数提升(函数表达式不能提升)
  2. 变量声明提升(不会覆盖提升的函数)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值