JavaScript函数Function的基本使⽤
javascript全局数组⽬录
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 是单线程的(同⼀时间只能做⼀件事),JavaSc
ript 所有的任务可以分成两种,⼀种是同步任务(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('')
}
}
hoistFunction(); // 函数声明提升,可以在函数体之前执⾏
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论