(一)普通函数
1. 声明
完整函数声明包括关键字、函数名、形式参数、函数体、返回值这5个部分。
2. 调用
函数声明后,必须调用才会执行,使用 ()
调用函数。
3. 调用传参
形参:声明函数时传的参数(形式上的参数)
实参:调用函数时传的参数(实际上的参数)
注:1)函数声明时形参没有数量限制,有多个形参则用英文逗号分隔。
2)开发中尽量保持形参和实参个数一致,调用函数时传递的实参要与形参顺序一致。
3)有些函数为了避免传入的实参为空,会在设置形参时给个默认值。
4)函数体内一旦碰到return,立即结束,后面代码不再执行。
5)函数可以没有return,这种情况默认返回值为 undefined。
(二)具名函数和匿名函数
函数分为具名函数和匿名函数,有函数名的叫具名函数,没有函数名的叫匿名函数。
1. 具名函数
可以有参数、返回值,存在函数提升(允许先调用,后声明),
使用时直接在函数名后面加个小括号就行了。
注:普通函数一般都是具名函数!!可以先声明再使用,也可以先使用再声明。
2. 匿名函数
可以有参数、返回值,不存在函数提升(不能没声明,就调用),
使用时一般通过 函数表达式 和 立即执行函数 的方式。
1)函数表达式:一般赋值给变量使用。
2)立即执行函数:函数的定义和调用是同时进行的。
注!!
1)大多数情况下js代码末尾可以不写分号(结束符),但在立即执行函数里面,每行代码末尾必须写分号,不可以省略。
2)立即执行函数会形成一个单独的作用域,可以封装一些临时变量或者局部变量,避免污染全局变量。
(三)回调函数
回调函数:如果将函数 A 作为参数传递给函数 B ,那么在函数B中, A 就称为回调函数。
注!!
1)回调函数本质还是函数,只不过把它当成参数使用。
2)开发中,使用匿名函数作为回调函数很常见。
(四)递归函数
递归函数:函数在内部调用其本身,那么这个函数就是递归函数,作用和循环类似。
注:递归函数容易发生“栈溢出”错误,故必须加上退出条件return!!
(五)箭头函数
1. 释义
箭头函数与普通函数并无本质区别,只是写法更简洁。
箭头左边是参数,右边是函数体。
2. 语法
注:
1)箭头函数属于表达式函数(匿名函数的一种形式),因此不存在函数提升(不能先使用再声明)。
2)只有一个参数的时候,可以省略小括号;只有一行代码的时候,可以省略大括号和return。
3. 函数参数
1)动态参数:普通函数里面都有一个内置的arguments 属性,用于接收传入的所有实参,不过本质是个伪数组。
2)剩余参数:箭头函数里面没有arguments属性,所以只能用剩余参数。
注:
1)普通函数有arguments动态参数;箭头函数没有arguments动态参数,但是有剩余参数。
2)剩余参数和展开运算符的区别:
剩余参数在函数中使用,作为函数参数,一般放在形参最末端,函数体中使用时不加...,得到的剩余实参是真数组;
展开运算符在数组中使用,用于展开所有数组元素。
4. this 指向
注:普通函数里面的this,谁调用指向谁;箭头函数里面的this,谁调用指向谁的上一层。
因此,DOM事件里面的匿名函数(也是回调函数)一般不采用箭头函数写法,而是保持传统的function ( ) { }写法。
(六)构造函数
构造函数:专门用于创建对象的函数,如果一个函数使用 new 关键字调用,那么这个函数就是构造函数。
1. 构造函数和普通函数的区别:
1)构造函数命名以大写字母开头;普通函数命名是小驼峰命名。
2)构造函数只能由“new”操作符来创建新对象,称为实例化;普通函数调用的时候不用加new。
3)构造函数里面的属性和方法前面都有this;普通函数没有。
4)构造函数内不写return,默认返回值为新创建的对象;普通函数需要写return。
5)构造函数可以快速创建多个类似对象,且多个实例对象之间彼此独立互不影响;普通函数里面的属性和方法是共有的,函数一旦定义后,内部逻辑代码是不能更改的。
2. 构造函数实例化过程
step1:创建新对象(空对象)
step2:构造函数this指向新对象(空对象)
step3:执行构造函数代码,修改this,为空对象添加新的属性
step4:返回新对象
3. 构造函数的静态成员和实例成员
静态成员:分为静态属性(构造函数中的属性)和静态方法(构造函数中的方法),
实例成员:分为实例属性(实例对象中的属性)和实例方法(实例对象中的方法)。
4. 内置构造函数
在 JavaScript 中最主要的数据类型有 6 种,分别是字符串、数值、布尔、undefined、null 和 对象,常见的对象类型数据包括数组和普通对象。
其中字符串、数值、布尔、undefined、null 也被称为简单类型或基础类型,对象也被称为引用类型。
因为JavaScript 中内置了一些构造函数,绝大部分的数据处理都是基于这些构造函数实现的。例如通过Object、Array、String、Number创建对象。
注:JS中几乎所有的数据类型都可以基于构造函数创建,
其中基础类型也有专门的构造函数,称为包装类型。
正因为JS底层,把简单数据类型包装成了引用数据类型,所以string才可以string.length,number才可以number.toFixed (2)
5. 构造函数原型
1)原型对象 prototype
原型对象: 原型对象也称为原型,是为了解决构造函数里面内存浪费的问题,将方法定义在原型prototype上面。
因为如果方法定义在构造函数里面,则只有这个构造函数才可以调用;但方法定义在原型里面,就可以实现共享!!
因此,构造函数中,可以将公有部分(方法)写到原型对象prototype身上;私有部分(属性)写到构造函数中,这样每个实例对象new一次就创造一次,各不相同,独自拥有。
注!!构造函数都自动有原型。
2)对象原型 __proto__
注!!只要是对象(不管是实例对象还是原型对象),都有__proto__ 属性。
__proto__ 是JS非标准属性,[[prototype]] 和 __proto__ 意义相同。
其中实例对象里面,__proto__ 指向原型prototype;
而原型prototype里面,__proto__ 指向上一级构造函数的原型。由此引申出了原型链。
原型链:基于原型对象的继承使得不同构造函数的原型对象关联在一起,并且这种关联的关系是一种链状结构,于是我们将原型对象的这种链状结构关系称为“原型链”。
3)constructor 属性
注!!只要是对象(不管是实例对象还是原型对象),都有constructor 属性。
且constructor 属性都指向该构造函数。
以构造函数 Person 为例,
Person.prototype.constructor 指向 Person,
ldh.__proto__.constructor 也指向 Person,
因为【 实例对象.__proto__ === 构造函数.prototype 】
4) 原型继承
JS 中大多是借助原型对象实现继承的特性。
子类的原型 = new 父类