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的严格模式中删除

posted @ 2019-11-24 22:15  我就是要学习  阅读(226)  评论(0编辑  收藏  举报