1、什么是函数
把一段相对独立的具有特定功能的代码块封装起来,形成一个独立实体,就是函数,起个名字(函数名),在后续开发中可以反复调用
函数的作用就是封装一段代码,将来可以重复使用
2、函数的定义
- 函数声明 (命名函数)
function 函数名(){
// 函数体
}
- 函数表达式( 匿名函数)
var fn = function() {
// 函数体
}
- 特点:
函数声明的时候,函数体并不会执行,只要当函数被调用的时候才会执行。
函数一般都用来干一件事情,需用使用动词+名词,表示做一件事情 tellStory
sayHello
等
3、函数的调用
-
调用函数的语法:
函数名();
-
特点:
函数体只有在调用的时候才会执行,调用需要()进行调用。 可以调用多次(重复使用)
代码示例:
// 声明函数
function sayHi() {
console.log("吃了没?");
}
// 调用函数
sayHi();
// 求1-100之间所有数的和
function getSum() {
var sum = 0;
for (var i = 0; i < 100; i++) {
sum += i;
}
console.log(sum);
}
// 调用
getSum();
4、函数的参数
- 语法:
// 函数内部是一个封闭的环境,可以通过参数的方式,把外部的值传递给函数内部
// 带参数的函数声明
function 函数名(形参1, 形参2, 形参...){
// 函数体
}
// 带参数的函数调用
函数名(实参1, 实参2, 实参3);
- 形参和实参
-
形式参数:在声明一个函数的时候,为了函数的功能更加灵活,有些值是固定不了的,对于这些固定不了的值。我们可以给函数设置参数。这个参数没有具体的值,仅仅起到一个占位置的作用,我们通常称之为形式参数,也叫形参。
-
实际参数:如果函数在声明时,设置了形参,那么在函数调用的时候就需要传入对应的参数,我们把传入的参数叫做实际参数,也叫实参。
示例1: 求两个数中的和
function sum(num1, num2) {
console.log(num1 + num2);
}
sum(100, 200); //形参和实参个数相等
sum(100, 400, 500, 700); //实参个数多于形参,只取到形参的个数
sum(200); //实参个数少于形参,多的形参定义为undefined,结果为NaN
5、函数的返回值
当函数执行完的时候,并不是所有时候都要把结果打印。我们期望函数给我一些反馈(比如计算的结果返回进行后续的运算),这个时候可以让函数返回一些东西。也就是返回值。函数通过return返回一个返回值
返回值语法:
//声明一个带返回值的函数
function 函数名(形参1, 形参2, 形参...){
//函数体
return 返回值;
}
//可以通过变量来接收这个返回值
var 变量 = 函数名(实参1, 实参2, 实参3);
函数的调用结果就是返回值,因此我们可以直接对函数调用结果进行操作。
返回值详解: 如果函数没有显示的使用 return语句 ,那么函数有默认的返回值:undefined
如果函数使用 return语句,那么跟再return后面的值,就成了函数的返回值
如果函数使用 return语句,但是return后面没有任何值,那么函数的返回值也是:undefined
函数使用return语句后,这个函数会在执行完 return 语句之后停止并立即退出,也就是说return后面的所有其他代码都不会再执行。
案例1、:判断一个数是不是质数
function isPrimeNumber(num){
for(var i=2;i<num;i++){
if(num%i==0){
return false;
}
}
return true;
}
var result=isPrimeNumber(8);
if(result){
console.log("这个数是质数");
}else{
console.log("这个数不是质数");
}
案例2:求一组数中的最大值
function getArrayMax(array){
//定义变量假设这个变量中存储的是最大值
var max=array[0];
for (var i = 0; i < array.length; i++) {
if (max<array[i]) {
max=array[i];
}
}
return max;
}
var arr=[46,25,15,68,28];
var max=getArrayMax(arr);
console.log(max);
案例3:通过函数实现冒泡排序
function sotrArray(arr) {
for (var i = 0; i < arr.length-1; i++) {
for (var j = 0; j < arr.length-1-i; j++) {
if (arr[j]>arr[j+1]) {
var temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
}
return arr;
}
console.log(sotrArray([9,8,45,62,84,1,23]));
案例4、输入某年某月某日,判断这一天是这一年的第几天?
function isLeapYear(year){
return year%4==0&&year%100!=0||year%400==0;
}
function getDays(year,month,day){
var days=day;
if(month==1){
return days;
}
var months=[31,28,31,30,31,30,31,31,30,31,30,31];
for(var i=0;i<month-1;i++){
days=days+months[i];
}
if (isLeapYear(year)&&month>2) {
days++;
}
return days;
}
console.log(getDays(2017,3,2));
案例5:求阶乘的和
//求一个数阶乘的和 比如5 5的阶乘+4的阶乘+3的阶乘+2的阶乘+1的阶乘
function getJieCheng(num) {
var num1=1;
for(i=1;i<=num;i++){
num1=num1*i;
}
return num1;
}
console.log(getJieCheng(5));
function getJieChengSum(num) {
var sum=0;
for (var i = 1; i <=num; i++) {
sum=sum+getJieCheng(i);
}
return sum;
}
console.log(getJieChengSum(5));
案例4和案例5表明函数也可以被作为参数。
6、arguments的使用
JavaScript中,arguments对象是比较特别的一个对象,实际上是当前函数的一个内置属性。也就是说所有函数都内置了一个arguments对象,arguments对象中存储了传递的所有的实参。arguments是一个伪数组,因此及可以进行遍历
案例:实现传递参数的累加,由于传递的参数的个数不一致,故使用arguments对象。
function f1() {
//arguments----->数组使用------伪数组---
var sum=0;
for(var i=0;i<arguments.length;i++){
sum+=arguments[i];
}
return sum;
}
console.log(f1(10,20,30));
7、函数其它
匿名函数
匿名函数:没有名字的函数
匿名函数如何使用:
将匿名函数赋值给一个变量,这样就可以通过变量进行调用
匿名函数自调用
关于自执行函数(匿名函数自调用)的作用:防止全局变量污染。
普通函数声明:
//声明一个普通函数,函数的名字叫sayHi
function sayHi(){
console.log("你好");
}
匿名函数声明:
//匿名函数
function (){
console.log("你好");
}
自调用函数:
匿名函数不能通过直接调用来执行,因此可以通过匿名函数的自调用的方式来执行
(function (){
console.log("你好");
})();
倘若需要传值,直接将参数写到括号内即可
(function (str){
console.log(str+"你好");
})("哈哈哈");
8、函数是一种数据类型
function fn() {}
console.log(typeof fn);
//function
-
函数作为参数
因为函数也是一种类型,可以把函数作为两一个函数的参数,在两一个函数中调用
-
函数做为返回值
因为函数是一种类型,所以可以把函数可以作为返回值从函数内部返回,这种用法在后面很常见。
function fn(b) {
var a = 10;
return function () {
alert(a+b);
}
}
fn(15)();
//25
9、作用域
作用域:变量可以起作用的范围
全局变量和局部变量
-
全局变量
在任何地方都可以访问到的变量就是全局变量,对应全局作用域
-
局部变量
只在固定的代码片段内可访问到的变量,最常见的例如函数内部。对应局部作用域(函数作用域)
不使用var声明的变量是全局变量,不推荐使用。(隐式全局变量)
变量退出作用域之后会销毁,全局变量关闭网页或浏览器才会销毁
全局变量是不能被删除的,隐式全局变量是可以被删除的
定义变量使用var是不会被删除的,没有var是可以删除
块级作用域
任何一对花括号({和})中的语句集都属于一个块,在这之中定义的所有变量在代码块外都是不可见的,我们称之为块级作用域。 在es5之前没有块级作用域的的概念,只有函数作用域,现阶段可以认为JavaScript没有块级作用域
词法作用域
变量的作用域是在定义时决定而不是执行时决定,也就是说词法作用域取决于源码,通过静态分析就能确定,因此词法作用域也叫做静态作用域。
在 js 中词法作用域规则:
-
函数允许访问函数外的数据.
-
整个代码结构中只有函数可以限定作用域.
-
作用域规则首先使用提升规则分析
-
如果当前作用规则中有名字了, 就不考虑外面的名字
var num = 123;
function foo() {
console.log( num );//123
}
foo();
if ( false ) {
var num = 123;
}
console.log( num );//123
9、作用域链
只有函数可以制造作用域结构, 那么只要是代码,就至少有一个作用域, 即全局作用域。凡是代码中有函数,那么这个函数就构成另一个作用域。如果函数中还有函数,那么在这个作用域中就又可以诞生一个作用域。
将这样的所有的作用域列出来,可以有一个结构: 函数内指向函数外的链式结构。就称作作用域链。
var num=10;
function f1() {
var num=20;
function f2() {
var num=30;
function f3() {
var num=50;
console.log(num);//50
}
f3();
}
f2();
}
f1();
10、预解析
JavaScript代码的执行是由浏览器中的JavaScript解析器来执行的。JavaScript解析器执行JavaScript代码的时候,分为两个过程:预解析过程和代码执行过程
预解析过程:
-
把变量的声明提升到当前作用域的最前面,只会提升声明,不会提升赋值。
-
把函数的声明提升到当前作用域的最前面,只会提升声明,不会提升调用。
-
先提升var,在提升function
var a = 25;
function abc (){
alert(a);//undefined
var a = 10;
}
abc();
//undifine
// 如果变量和函数同名的话,函数优先
console.log(a);
function a() {
console.log('aaaaa');
}
var a = 1;
console.log(a); //1
变量提升
-
变量提升
定义变量的时候,变量的声明会被提升到作用域的最上面,变量的赋值不会提升。
-
函数提升
JavaScript解析器首先会把当前作用域的函数声明提前到整个作用域的最前面
// 1、-----------------------------------
var num = 10;
fun();
function fun() {
console.log(num); //undefined
var num = 20;
}
2、-----------------------------------
var a = 18;
f1();
function f1() {
var b = 9;
console.log(a); //undefined
console.log(b);//9
var a = '123';
}
// 3、-----------------------------------
f1();
console.log(c);//9
console.log(b);//9
console.log(a);//报错
function f1() {
var a = b = c = 9;
console.log(a);//9
console.log(b);//9
console.log(c);//9
}
//3、正确步骤
function f1() {
/*
var a = b = c = 9;
var a;//局部变量
a=9;
隐式全局变量
b=9;
c=9;
*/
var a = b = c = 9;
console.log(a);//9
console.log(b);//9
console.log(c);//9
}
f1();
console.log(c);//9
console.log(b);//9
console.log(a);//报错