JavaScript--函数

1.什么是函数

在编程中,函数就是一个功能、就是一个行为、 就是一个动作。从专业的角度说的话,函数就是一段具有独立功能的代码的集合,是一段有名称的 代码。函数也可以说是定义一次但却可以调用或执行任意多次的一段代码。

2. 函数的定义

函数对任何语言来说都是一个核心的概念。通过函数可以封装任意多条语句,而且可以 在任 何地方、任何时候调用执行。ECMAScript 中的函数使用 function 关键字来声明,后跟一组参数 以及函数体。 

// 语法结构:
function 函数名称([参数列表]) {
// 函数体
// 如果存在返回值的话,使用return关键字返回
[return 返回值;]
}

3.函数调用

注意:函数的调用就是压栈的过程!!!

局部变量:定义在函数内部的变量。

注意:js中一定要使用var或者let定义变量,如果在函数 内部不使用关键字定义,则默认定义的是全局变量,而不是局部变量

注意:JavaScript虽然也是解释性语言,和python这些语言一致。但是JavaScript中,如果函数 定义在调用之后,这个是可以的。(Python这些不行,因为代码是从上而下执行的,在调用时,还 没有定义,所以会报错的)。

之所以可行,是因为浏览器在解析JS文件时找到函数声明,并在执 行剩余语句之前设置好函数,这个过程被称为函数提升(function hoisting)。

4. 函数的分类

1.有无参数        函数可以通过有没有参数来讲函数分为有参函数和无参函数。

2. 有无返回值    可以根据函数是否存在返回值,将函数分为有返回值的函数和无返回值的函数。

3. 定义者           可以通过定义者来将函数分为 :系统函数和自定义函数

5. 值传递和引用传递

注意:值传递就是传递值,而引用传递本质传递地址

        <script>
			// 值传递
			a = 10
			b = 20
			function change(x, y) {
			x = 30;
			y = 50;
			}
			change(a, b);
			alert(a +"--"+ b)   //结果:a,b值没有改变
			
			var arr = [1, 3, 4, 5]
			// 引用传递
			function change2(a) {
			a.push(1000)
			}
			change2(arr)
			alert(arr)         //结果:arr变为 [1, 3, 4, 5,1000]
		</script>

6.函数作为参数传递

在弱数据类型语言中,函数本身也是对象(强数据类型语言中不是),而函数参数,只要是对 象就可以充当参数,所以函数也可以当成参数传递进函数的。 

function fn() {
alert("这个第一个函数")
}
function show(fn) {
// fn是一个函数,所以就可以直接调用
fn()
alert("js中函数是可以当成参数的哦~~")
}
// 注意:传递fn是相当于将fn对应的函数地址传递过去了,也就是C语言中的指针
// show(fn()) 传递的是用fn函数的返回值,如果fn没有返回值,则传递的就是null
show(fn)

7.默认值参数

如果有参数一般有一个固定值,为了调用者方便,可以将这样的参数设置为默认值参数。

注意:默认值参数必须在普通参数后面定义!!!

// 默认值参数   注意:默认值参数必须在普通参数后面定义!!!

			function getCircleArea(r, PI=3.14) {
			return PI * r * r
			}
			alert(getCircleArea(2, 3))   //结果:2*2*3=12  

8.arguments 对象

ECMAScript函数不介意传递进来多少参数,也不会因为参数不统一而错误。实际上,函数体内 可以通过 arguments 对象来接收传递进来的参数。

<script>
			// arguments对象
			function test() {
			console.info(arguments.length)   //打印元素总个数       3
			console.log(arguments)           //打印传进来的所有元素 [5,67,'hi']
			if (arguments.length > 0) {
			console.log(arguments[0])        //打印下标为0的元素    5   
			}
			}
			test(5,67,'hi')
		</script>

注意:JavaScript因为是脚本语言,代码从上而下执行,所以没有函数重载。如果需要的话, 我们就可以使用arguments对象模拟一个类似功能。 

9. 匿名函数

顾名思义,就是在定义的时候没有给命名的函数,一般这种函数就是充当参数,只是使用一 次,那就没有必要取名了,或者定义的时候,赋给其他变量(当然这种情况下,本质还是有名字的)。 

<script>
			// 匿名函数
			(function (){
			alert("1.这个就是一个匿名函数");
			})
			();
			
			function show(fn) {
			fn();
			alert("12345678")
			}
			// 匿名函数充当参数
			show(function() {
			alert("2.这个是匿名函数当参数");
			})
			
			// 此时将一个匿名函数让b变量指向,所以b变量就是这个匿名函数
			var b = function () {
			alert("3.这个就是一个匿名函数")
			}
			// 所以就可以调用哦
			b();
		
		</script>

10. lambda表示式

(箭头函数) lambda表示式是ES6提供的,有些人又叫做箭头函数。主要是简化函数的一种写法。 

// 箭头函数,lambda表达式,简化函数写法
			// 如果没有参数,则直接写个()
			var fn = () => alert("hello");
			fn();                              //结果:hello
			
			
			// 等价于 function myFunc(x) { return x + 5}
			// 不用加return
			let myFunc1=(x) => x + 5;
			// 调用
			alert(myFunc1(20))                //结果:25
			
			// 只有一个参数是,可以有两种写法
			x=>x+5;
			// 但是建议使用第二种,因为这样更加清晰明了
			(x)=>x+5;
			// 如果存在多行代码,需要使用{}
			let myFunc2 = (x, y) => {
			let z = 5;
			return (x + y) * z;
			}
			alert(myFunc2(2,8));             //结果:50
			
// 延伸学习:ES6解构语法
			/* const {value, msg} = obj; */
			//解构语法,相当于定义变量:
			/* const value = obj.value;
			const msg = obj.msg; */
			// 当然如果变量名与属性名不一致,必须这样

			let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
			console.log(baz); // "aaa"
			
			let obj = { first: 'hello', last: 'world' };
			let { first: f, last: l } = obj;
		    console.log(f); // 'hello'
			console.log(l); // 'world'

11. 递归

递归是指函数自身调用自身的现象,在函数中调用函数,会形成不断压栈的过程,这样就形成 了死循环,所以递归一定要有结束条件。 

function sum(n) {
// 必须有结束条件
if (n <= 1) {
return 1
}
// 自身调用自身
return n + sum(n - 1)
}
// 求1~100的和
alert(sum(100)

12. 函数的属性 

ECMAScript 中的函数是对象,因此函数也有属性和方法。每个函数都包含两个属性:length 和 prototype。

 对于 prototype 属性,它是保存实例方法的所在,也就是原型。

 prototype 下的两个方法:apply()和 和 call() ,每个函数都包含这 两个非继承而来的方法。 这两个方法的用途都在特定的作用域中调用函数,实际上等于设置函数 体内 this 对象的值.

注意!call需要一个一个将参数传递,而apply使用数组,一次传递,这个就是两者的区别,其他都一样的。这些方法都可以修改this指针的作用域。还有一个bind函数和call类似。

function box(num1,num2){
return num1+num2; //原函数
}
// apply 方法
// 第二个参数是数组形式
function sayBox(num1,num2){
return box.apply(this,[num1,num2]);//this 表示作用域,是window对象,表示box属于
widow全局对象
}
alert(sayBox("可达鸭", "草莓熊"))

// 当然也可以使用arguments关键字代替
function sayBox2(num1,num2){
return box.apply(this,arguments); //arguments 对象表示box所需要的参数
}
alert(sayBox2(2, 200));


// call方法
// 从第二个参数开始,一个一个的传递
function sayBox3(num1,num2){
return box.call(this, num1, num2); // 一个一个写
}
alert(sayBox3(1000, 1000)

call()和apply()的一种运用 

/**
* 当需要创建一个类的时候,设置类的属性和方法需要通过this关键字来引用
* 但是特别注意:this关键字在调用时会根据不同的调用对象变得不同
*/
var color = "red";
function showColor() {
alert(this.color);
}
/**
* 创建了一个类,有一个color的属性和一个show的方法
*/
function Circle(color) {
this.color = color;
}
var c = new Circle("yellow");
showColor.call(this); //使用上下文来调用showColor,结果是red
showColor.call(c); //上下文对象是c,结果就是yellow
/**
* 使用call和apply之后,对象中可以不需要定义方法了
*/

 补充:函数的bind方法:bind是Function自带的方法,它不能让函数执行,但是可以改变函数this的 指向(它第一个参数可以改变this的指向,第二个及后面的参数用来给函数传实参),bind不传参 得情况下,this指向不改变,bind(null)和bind(undefined),this指向均不改变

 13.全局函数

 14. 闭包(closured)

闭包是可访问一个函数作用域里变量的函数。简而言之:闭包就是一个函数,这个函数能够访 问其他函数的作用域中的变量。

function outer() {
var a = '变量1'
var inner = function () {
console.info(a)
}
// inner 就是一个闭包函数,因为他能够访问到outer函数的作用域
return inner
}

可以在一个函数里面嵌套另外一个函数。嵌套(内部)函数对其容器(外部)函数是私有的。它自身也形成了一个闭包。

一个闭包是一个可以自己拥有独立的环境与变量的的表达式(通常是函数,因此ES6有了块级作用域的概念)。 

闭包的作用:

  • 可以在函数的外部访问到函数内部的局部变量。
  • 让这些变量始终保存在内存中,不会随着函数的结束而自动销毁。 
<!DOCTYPE html>
   <html lang="en">
    <head>
     <meta charset="UTF-8">
     <title>闭包实例</title>
    </head>
    <body>
    <ul>
      <li>click me</li>
      <li>click me</li>
      <li>click me</li>
      <li>click me</li>
    </ul>
      <script>
          var elements=document.getElementsByTagName('li');
          var length=elements.length;
          for(var i=0;i<length;i++){
             elements[i].onclick=function(){
             alert(i);
      }
    }
     </script>
    </body>
</html>

依次点击4个li标签,正确的运行结果是(依次弹出4,4,4,4 )

每个li标签的onclick事件执行时,本身onclick绑定的function的作用域中没有变量 i,i为undefined,则解析引擎会寻找父级作用域,发现父级作用域中有i,且for循环绑定事件结束 后,i已经赋值为4,所以每个li标签的onclick事件执行时,alert的都是父作用域中的i,也就是4。这是作用域的问题 

 闭包只能取得包含函数中任何变量的最后一个值。因为别忘了闭包所保存的是整个变量对 象,而不是某个特殊的变量。 其实,用两个函数形成闭包只是一般形式。闭包真正的含义是,如果一个函数访问了此函数 的父级及父级以上的作用域变量,就可以称这个函数是一个闭包。

 let关键字,补上了JS没有块级作用域的短板。

<body>
		<ul>
		<li>click me</li>       <!-- 点击弹0 -->
		<li>click me</li>       <!-- 点击弹1 -->
		<li>click me</li>       <!-- 点击弹2 -->
		<li>click me</li>       <!-- 点击弹3 -->
		</ul>

		<script>
			var a = 1;
			(function test (){
			alert(a);
			})
			();
			var elements=document.getElementsByTagName('li');
			var length=elements.length;
			for(let i=0;i<length;i++){
			elements[i].onclick=function(){
			alert(i);
			}
			}

		</script>
	</body>

所以,JavaScript中,推荐使用闭包,当页面中存在多个组件,多份js文件被引入后,容易触 发全局变量污染问题,而闭包就是一种解决的很好方案(匿名闭包函数)-- 立即执行函数。

如: 使用闭包有一个优点,也是它的缺点:就是可以把局部变量驻留在内存中,可以避免使用全局 变量。(全局变量污染导致应用程序不可预测性,每个模块都可调用必将引来灾难,所以推荐使用 私有的,封装的局部变量)。

在闭包中使用this对象也可能会导致一些问题,this对象是在运行时基于函数的执行环境绑定 的,如果this在全局范围就是window,如果在对象内部就指向这个对象。而闭包却在运行时指向 window,因为闭包并不属于这个对象的属性或方法。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值