JS中的函数
函数function
函数就是把实现某个功能的代码封装。提高了代码的使用率,减少页面中的冗杂代码,实现高内聚低耦合的要求。
创建函数
- 方式1、函数声明:存在函数提升,最先执行
function[函数名](形参变量1,...){
//函数体:基于JS需要实现的功能
return[处理后的结果];
}
function fun(){
console.log('a')
}
fun()
练习1:为什么在控制台输入console.log(1)除了输出1之外还会输出undefined?
因为console.log本身就是一个函数,console.log(1)代表执行时传入的实参为1,函数的功能是将结果在控制台输出,输出undefined是因为没有return语句
练习2:
console.log(alert(1)) //undedined 本质就是执行内置方法alert,该方法的功能就是弹出1的对话框,该方法返回值默认为undefined
练习3:return语句的使用
function sum(a,b){
if (a==undefined||b==undefined) {
return;//函数体中遇到return后面的代码不再执行
}
console.log(a+b)
}
sum(1,2)
- 方式2、函数表达式(匿名函数):把匿名函数本身作为一个值赋给其他东西。没有函数提升,等到代码执行到此处函数才会执行,存在变量提升
1)常用于事件处理中:
document.getElementById('btn').onclick=function(){
console.log('a')
}
2)还可以将匿名函数赋值给一个变量
var fun=function fun(){
console.log('a')
}
fun()
- 方式3、new操作符不推荐使用,需要进行两次解析,第一次解析常规代码,第二次解析传入的字符串,效率低
new Function(arg1,arg2,arg3..,body);
var fun=new Function('a','b','console.log(a+b)')
fun(1,2)
自执行函数:匿名函数自调用
- 优点:立即开启一个新的命名空间,避免污染全局空间
(function(){
var a=1
function test(){
console.log(++a)
}
window.fun=function(){//向外暴露一个函数
return {
test:test//返回一个对象,将自调用函数中的局部变量提升为全局变量
}
}
})()
fun().test()//2 fun是全局中的一个函数,返回值是一个对象,对象中有test方法
声明提升:所有的声明(包括变量和函数)都在JS的预编译阶段就进行了处理,将声明部分提升至相应作用域的顶部。
/* 函数声明方式定义的函数:整个函数都会被提升至作用域顶部 */
foo(); // "bar"
function foo() {
console.log("bar");
}
/* 函数表达式 */
baz(); // 类型错误:baz 不是一个函数
var baz = function() {
console.log("bar2");
};
/*函数提升和变量提升同时出现时,函数提升优先*/
console.log(typeof a)//function
var a='12'
function a(){
console.log('aaaa')
}
console.log(a)//12
/*函数提升时同时完成初始化,变量提升只完成声明,执行到声明代码位置处才进行初始化*/
函数的调用
- 直接调用
- 参数:函数调用时会将调用时传入的实参赋值给形参(在函数体中有一个形参的引用指向实参)
- 返回值:函数调用表达式的值就是函数返回值(函数有return语句时,返回值就是return后表达式的值;没有return语句函数返回值为undefined)
- 调用上下文:函数直接调用时它的调用上下文(this值)为全局对象window
- 作为方法调用
- 参数与返回值与直接调用处理相同
- 调用上下文:当函数作为一个方法调用时它的调用上下文为该方法所属的对象,在函数体中可以通过this引用该对象
注意:函数如果是直接调用this不是window就是undefined,函数如果作为方法调用this值就为调用函数的对象。
对于嵌套函数来说,this没有作用域的限制,不会从外部函数中继承this。
如果想在内部函数中访问外部函数的this,需要将外部函数的this保存到一个变量中,这样这个变量就和内部函数在同一个作用域中
let obj={
sayHello:function say(){
console.log(this)//obj
let self=this
sayHi()
function sayHi(){
console.log(this)//window 内部函数不会从外部函数中继承this
console.log(self)//obj
}
}
}
obj.sayHello()
- 作为构造函数调用
- 参数:如果没有实参列表,()可以省略;如果有实参列表则和函数直接调用和作为方法调用一样
- 返回值:通常不使用return,一般会将初始化后的新对象返回;如果显示的返回一个对象,就将这个对象作为调用表达式的值;如果使用return关键字但是没有指定具体返回值或者返回值不是引用类型,则忽略return语句,仍将新对象返回
- 通过call apply间接调用
- 相同点:两者都允许显示的指定this值;都可以指定调用的实参
- 区别:call(调用上下文,参数1,参数2...) apply(调用上下文,[参数1,参数2...])
函数的实参与形参
- JavaScript函数定义未指定函数形参的类型,函数调用也没有对传入的类型和个数进行检查。所以会出现调用函数的实参个数与定义时形参个数不匹配的情况。实参<形参:实参从左向右给形参赋值,剩下的形参设置为undefined;
//设置默认参数
function getValue(val1,val2){
/*if (val2===undefined) {
val2=1111
}*/
val2=val2||1111
return [val1,val2]
}
var val=getValue(12)
console.log(val)// [12, 1111]
- 实参对象arguments
- arguments:标识符arguments是指向实参对象的引用
- 当实参>形参个数时,无法直接获取未命名的实参,可以通过arguments进行访问。
- arguments是一个类数组对象,但除了length属性和索引元素之外没有任何Array属性,所以仅仅能获取长度和操作索引值
arguments应用场景:
* 通过arguments.length知道实参的数量(PS:确定函数签名中参数(形参)的数量使用Function.length)
* 通过arguments对象处理每个参数;
console.log(arguments)//undefined arguments对象只能在函数中使用
function fun(a,b,c){
let num=arguments.length//实参的数量
console.log(num)
console.log(a,b,c)
console.log(typeof arguments)//'object'
}
console.log(fun.length)//形参数量为3
//通过实参名字修改参数值,通过arguments可以获取到修改之后的值;同样的通过arguments修改的实参的值也可以通过参数名获取到修改之后的值,但是ES5移除了这个特性
function fun(x){
console.log(x)//222
arguments[0]=null
console.log(x)//null
}
fun(222)
将arguments对象转换为真正的数组
function fun (a,b,c){
// let args = Array.from(arguments); 通过Array.from()方法将其转换为数组
let args=[...arguments]//利用扩展运算符转换为数组
args.push(1000)
console.log(args)// [11, 23, 52, 1000]
}
fun(11,23,52)
- callee与caller属性:arguments定义了两个属性callee和caller
ES5非严格模式下规定calle指代正在执行的函数,当函数正在执行时才可调用,在匿名函数通过callee实现递归的调用自身时很有意义
caller是非标准的,但是大多数浏览器都实现了这个属性,它指代调用当前正在执行函数的函数,通过caller可以访问函数调用栈
//匿名函数没有名称。因此如果没有可访问的变量指向该函数,唯一能引用它的方式就是通过 arguments.callee。
//下面的例子并不是在匿名函数中使用,而是在命名函数表达式中探讨使用arguments.callee与使用函数名称的一些不同
function factorial (num) {
if (num<=1) {
return 1;
}else{
return num*arguments.callee(num-1);//通过arguments.callee解除函数名与函数内代码的耦合
// return num*factorial(num-1); 利用命名函数表达式实现调用函数自身
}
}
var truefactorial=factorial;//truefactorial指向function factorial
factorial=function () {//让变量factorial指向新创建的function函数
return 0;
}
console.log(factorial(5));//0 因为factorial引用的是后来定义的函数
console.log(truefactorial(5));//120 truefactorial引用的是一开始的函数
/**
* 如果factorial函数中使用return num*factorial(num-1);
* 那么truefactorial(5)是0
* 在解除了函数体内的代码和函数名的耦合后,truefactorial()可以正常计算阶乘
*/
注意:arguments.callee已经从ES5的严格模式中删除