js函数里的函数怎么调用,js在函数里面调用函数

大家好,给大家分享一下js函数里的函数怎么调用,很多人还不知道这一点。下面详细解释一下。现在让我们来看看!

目录

1、声明函数

2、函数的调用时机

3、函数作用域

4、函数调用栈

5、函数提升


1、声明函数

在 JavaScript 中定义函数的方法有 3 种:使用 function 语句、使用 Function() 构造函数和定义函数直接量。不管使用哪种方法定义函数,它们都是 Function 类型的实例,并将继承 Function 原型对象的方法和属性快码论文所有函数都是 Function 构造出来的,包括Object、Array、Function。

(1)可以使用 function() 语句来声明函数:

function fun(){    //空函数,函数名为 fun,可以通过fun()对函数进行全局调用
}
​
function (a,b){    //函数直接量,上面的是具名函数,去掉函数名就是匿名函数
    return a + b;
}
(function(a,b){    //匿名函数的调用方式
    return a + b;
}(1,2)
​
let a = function(x,y){    //等号右边也叫函数表达式
    return x + y;         
}

let a = function fun(x,y){  //不可以通过fun()对函数进行全局调用
    return x + y;           //这种方式声明的函数,在调用fun()时会报错,因为fun只在等号右边有效
}

与其他结构不同,function 结构是静态的,不会立即执行,只有调用函数时,才能被执行。

var 语句和 function 语句都是变量声明语句,他们声明的变量都在 JavaScript 预编译时被解析。在预编泽期,Javasecript 解释器会把代码中的 function 语句定义为一个函数变量,同时解析函数体内部代码,把函数体内所有参数、私有变量、嵌套函数作为属性注册到函数调用对象上,以便在执行期调用函教时能够快速执行。

(2)使用 Function() 构造器声明函数:

var fun = new Function();    //定义空函数

var fun = new Function("a","b","return a+b");    //通过构造函数来克隆函数结构
function fun(a,b){    //使用 function 语句定义函数结构,等价于上面
    return a + b;
}

//下面这几种方式是等价的
var fun = new Function("a","b","c","return a+b+c")
var fun = new Function("a,b,c","return a+b+c")
var fun = new Function("a,b","c","return a+b+c")

Function() 函数构造器不是很常用,因为一个函数体通常会包含很多代码,如果将这些代码以一行字符串的形式进行传递,代码的可读性会很差。

(3)使用箭头 ()=>{} 函数定义:

let fun1 = x => x*x;
let fun2 = (x,y) => x+y;    //圆括号不能省
let fun3 = (x,y) => {return x+y};    //花括号不能省
let fun4 =(x,y) => ({name:x,age:y})    //直接返回对象会出错,需要加个圆括号

使用箭头函数时,当参数列表超过一个时,不能省略小括号。当函数体不止一条语句时,不能省略 { }。

2、函数的调用时机

在讲函数调用时机之前,需要了解一下 JavaScript 的执行机制,JavaScript 是单线程的(同一时间只能做一件事),JavaScript 所有的任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。

let a = 1;                        let i = 0;                        for(let i=0;i<6;i++){
function fun(){                   for(i=0;i<6;i++){                     setTimeout(()=>{
    setTimeout(()=>{                  setTimeout(()=>{                      console.log(i);
        console.log(a);                   console.log(i);               },0)
    },0)                              },0)                          }
}                                 }                                 //结果:0 1 2 3 4 5
fun();    //调用fun()             //结果:6 6 6 6 6 6
a = 2;
//结果:控制台打印:2

第一个例子:由于 setTimeout() 属于异步操作,程序在执行到 setTimeout() 函数时,会将其推送至任务队列,转而去执行后面的同步任务(a = 2),当主线程的同步任务执行完毕后(此时 a =  2),在去执行事件队列里的任务,打印变量 a ,所以最终的结果是 2;

第二个例子,在每一次循环时将 setTimeout() 函数推送至任务队列,然后 i++,一直循环到 i=6,终止循环。然后去执行任务队列里的异步操作,任务队列里有 6 打印操作,因为 i 已经是 6 了,所以这六次打印的结果都是 6。

第三个例子:使用 let 声明 i 的情况和第二个例子有所不同,当 for 循环每执行一次,就会将 i 复制一份传递到 setTimeout() 函数内,当循环完毕后,去执行任务队列里的异步操作,此时任务队列里的每次打印操作都有自己的 i,所有打印结果为 0,1,2,3,4,5。

3、函数作用域

JavaScript 把函数视为一个封闭的结构体,与外界完全独立,在函数内声明的变量、参数、私有函数等对外是不可见的。

function fun(){                                function fun(){
    let a = 1;  /*函数内的局部变量a*/               let a = 1;  /*函数内的局部变量a*/
}                                              }
console.log(a);    /*undefined*/               fun();             //函数作用域在调用函数时创建,函数执行完毕后销毁
                                               console.log(a);    //undefined

函数作用域在调用函数时创建,函数执行完毕后销毁,每一次调用函数都会创建一个新的作用域,他们之间是相互独立的。在函数作用域中可以访问到全局作用域的变量,但是在全局作用域中一般无法访问到函数作用域的变量,函数作用域可以通过 return 语句向外界开放内部成员例如:

function outer(){
    var data = 1;     //外层函数的内部数据会一致缓存在内存中
    function inner(){ //如果一个函数用到了外部的变量,那么这个函数加这个外部变量,就叫做闭包。
        return data;
    }
    return inner;
}
var colsure1 = outer();    //拿到闭包之后可以决定什么时候执行它
var colsure2 = outer();    
console(colsure == colsure);    //false
 
var data1 = colsure1();    //执行拿到的闭包
var data2 = colsure2();
console(data1 == data2);    //true

由于调用了两次 outer() 方法,从而创建了两个 data 对象,因此两个闭包访问到的数据(data)在内存中的地址是不同的。

简单来说:如果一个函数用到了外部的变量,那么这个函数加这个外部变量,就叫做闭包。

在函数作用域中操作一个变量时,它会在自身作用域中寻找该变量,如果找到就直接使用,如果没有找到就会到上一级作用域中寻找,直至找到全局作用域,如果全局作用域中依然没有该变量,就会报错:ReferenceError。如果在函数中要访问全局变量,可以直接通过 window 对象来访问。

4、函数调用栈

JS 引擎在调用一个函数前,需要把函数所在的环境 push 到一个数组里,这个数组叫做调用栈,等函数执行完了,就会把环境弹(pop)出来,然后 return 到之前的环境,继续执行后续代码。

递归函数可以检测浏览器的调用栈最大长度:Chrome 浏览器的函数调用栈的长度大概在一万二到一万五之间。

function sum(n){    //定义递归函数:递归分为两步:递进 / 回归
    return n!==1 ? n+sum(n-1) : 1
}
sum(4);    //其递进 + 回归的过程如下:

/*         原位                4+                3+                  2+
* sum(4) ————————> 4+sum(3) ————————> 3+sum(2) ————————> 2+sum(1) ————————> 1
*          压栈                压栈              压栈                压栈
*
*                                                                         
*    2+           3+            4+          原位
* 1 ————————> 3 ————————> 6  ————————> 10 ————————> 回到sum(4)位置,继续向下执行;
*    弹栈         弹栈          弹栈         弹栈
*/


function computeMaxCallStackSize(){
    try{
        return 1 + computeMaxCallStackSize();
    }catch(event){
        //报错说明 stack overflow了
        return 1;
    }
}
computeMaxCallStackSize();    //Chrome的最大调用栈长:12577左右

各浏览器的调用栈最大长度不同,可通过上述函数对浏览器的调用栈长度进行检测,如果调用栈中压入的帧过多,程序就会崩溃。

5、函数提升
var fun = function () {                var fun = function () {    //这是赋值,右边的匿名函数声明不会提升
    console.log('fun1');                  console.log('fun2');
}                                      }
fun();  /*fun1*/                       fun();  /*fun2*/
------------------------------------------------------------------------------------------
function fun() {                      function fun() {    //不管具名函数声明在哪一行,它都会提升到它作用域的第一行
    console.log('fun1');                  console.log('fun2');
}                                     }
fun();  /*fun2*/                      fun();  // fun2

JS 引擎会在正式执行代码之前进行一次 "预编译",预编译简单理解就是在内存中开辟一些空间,存放一些变量和函数。

简单来说:预解析机制使得变量提升,从字面上理解就是变量和函数的声明会移动到函数或者全局代码的开头位置。

console.log(a) // 执行之前,变量提升作为window的属性, 值被设置为undefined
var a = 'hello JS' 

/* JavaScript 仅提升声明,而不提升初始化 */
num = 6;
num ++;
var num;
console.log(num) // 变量提升 值为undefined的num赋值为6,再自增 => 7

function hoistFunction() {
    fun();
    function fun() {        
        console.log('running...')    
    }
}
hoistFunction(); // 函数声明提升,可以在函数体之前执行
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值