一、函数
1.概念
在JS中,可能会定义非常多的相同代码或者是功能相似的代码,这些代码可能需要大量重复使用。虽然for循环语句也能实现这一些简单的重复操作,但是比较具有局限性,此时我们就可以使用js中的函数。
函数:就是封装了一段可被重复调用执行的代码块。通过此代码块可以实现大量代码的重复使用。
2.使用
2.1 声明函数
语法:
function 函数名(){
}
- function:是声明函数的关键字,必须小写
- 由于函数一般是为了实现某个功能才定义,所以通常我们将函数名命名为动词,如:getSum
2.2 调用函数
语法:
//调用函数
函数名(); //通过调用函数名来执行函数体中的代码
- 调用的时候千万不要忘记后面有一对小括号
- **口诀:**函数不调用,自己不会执行
注意:声明函数本身并不会执行代码,只有调用函数的时候才会执行函数体中的代码。
快速体验
//需求:传入两个数字,计算两个数字的和 (求第一个数字到第二个数之间所有数的和)
//1.求1~100累加和
var sum = 0;
for(var i = 1; i <= 100; i++){
sum += i;
}
console.log(sum)
//2.求1~10的和
var sum = 0;
for(var i = 1; i <= 10; i++){
sum += i;
}
console.log(sum)
//函数
function getSum(num1,num2){
var sum = 0;
for(var i = num1; i <= num2; i++){
sum += i;
}
console.log(sum);
}
//调用函数
getSum(1,10);
getSum(1,100);
getSum(1,20);
2.3 函数基本使用
//函数的使用分为两步:声明函数 和 调用函数
//注意:1)function是声明函数的关键字,全部小写
// 2)函数是做某件事情,函数名一般是动词
// 3)函数不调用自己不会执行的
/*
1.声明函数:
function 函数名(){
//函数体
}
*/
function sayHi(){
console.log('hi~~~');
}
/*
2.调用函数:
函数名();
*/
sayHi();
3.函数的封装
- 函数的封装就是把一个或多个功能通过函数的方式放在函数里面,对外只提供一个简单的函数接口去调用。
- 简单的理解:封装类似于将电脑配件组合组装到机箱中。
需求:利用函数计算1~100之间累加的和
//1.声明函数
function getSum(){
var sum = 0;
for(var i = 1; i <= 100; i++){
sum += i;
}
console.log(sum);
}
//调用函数
getSum();
getSum();
4.函数的参数
4.1 函数中参数的语法
- 形参(形式参数):函数定时设置接口调用时传入
- 实参(实际参数):函数调用的时候传入小括号内的真实数据
参数 | 说明 |
---|---|
形参 | 形式上的参数,函数定义的时候传递的参数,此时我们并不知道是什么 |
实参 | 实际上的参数,函数调用的时候传入的参数,实参时传递给形参的 |
形参的作用:在函数内部某些值不能固定,我们就可以通过参数在调用函数时传递不同的值进去。
4.2 函数参数的运用
4.2.1 声明语法:
//带参数的函数声明
function 函数名(形参1,形参2,形参3,....){ //可以定义任意多的参数,中间用逗号分隔
//函数体
}
4.3.2调用
//带参数函数的调用
函数名(实参1,实参2,实参3,...)
- 调用的时候实参值时传递给形参的
- 形参简单理解为:不用声明的变量
- 实参和形参多个参数之间用逗号分隔
4.3.3 一个参数
/*
function 函数名(形参1,形参2,形参3,....){ //可以定义任意多的参数,中间用逗号分隔
//函数体
}
*/
function cook(foodName){
console.log('我正在做' + foodName);
}
/*
//带参数函数的调用
函数名(实参1,实参2,实参3,...)
*/
cook('酸辣土豆丝');
cook('麻婆豆腐');
4.3.4 两个参数
//1.利用函数求任意两个数字的和
function getSum(num1, num2){
console.log(num1 + num2);
}
getSum(1,3);
getSum(3,8);
//2.利用函数求任意两个数之间的和
function getSums(start,end){
var sum = 0;
for(var i = start; i <= end; i++){
sum += i;
}
console.log(sum);
}
getSums(1,100);
getSums(1,10);
//注意点:1)多个参数之间用逗号分隔 2)形参可以看作是不用声明的变量
4.3.5 函数参数匹配问题
//1.利用函数求任意两个数字的和
function getSum(num1, num2){
console.log(num1 + num2);
}
getSum(1,3);
//传入3个参数。实参的个数多于形参的个数,会取到实际的形参个数
getSum(1,2,3); //实际会取到1和2
//传入1个参数,多余的形参会被定义为undefined,最终的结果就会是NaN
getSum(1); //NaN
5.函数的返回值
5.1 return
返回值:函数调用整体代表的数据;函数执行完成后可以通过return 语句将制定的数据返回。
5.2 语法
//声明函数
function 函数名(形参){
...
return 需要返回的值;
}
//调用函数
var o = 函数名(); //此时调用函数就可以得到函数体内return后面的值
5.3 注意事项
- 在使用return语句的时候,函数会停止执行,并返回指定的值
- 如果函数没有reutrn,返回值就是undefined
5.4 案例
案例一
/*
1.我们函数只是实现了某种功能,最终结果我们之前是通过console.log()打印在控制台,
实际的开发中我们都是通过return实现的。
2.只要函数遇到return就把后面的结果返回给函数的调用者,函数名() = return后面的结果
*/
function getResult(){
return 666;
}
var result = getResult(); //调用了
console.log(result)
//求任意两个数字的和
function getSum(num1,num2){
return num1 + num2;
}
var sum = getSum(1,2);
console.log(sum);
案例二
需求:传入两个数字,返回较大的
//需求,传入两个数字,返回较大的
function getMax(num1,num2){
/* if(num1 > num2){
return num1;
} else {
return num2;
} */
return num1 > num2?num1:num2;
}
var max1 = getMax(1,5);
console.log(max1);
var max2 = getMax(11,5);
console.log(max2);
案例三
需求:求一个数组中 var arr = [5,2,99,101,67,77,88]中的最大值,要求使用函数,形参传入的是一个数组
function getMax(arr){ … return …} var arr = [5,2,99,101,67,77,88] ; var max = getMax(arr);
//求数组中最大值
function getMax(arr){
var max = arr[0];//我先认为最大值是数组中的第一个元素
//遍历数组
for(var i = 0; i < arr.length; i++){
if(max < arr[i]){ //如果我们认为的最大值小于了数组中的某个元素
max = arr[i];//max就得重新赋值,赋值为比原来大的数
}
}
//返回最大值
return max;
}
var arr = [5,2,99,101,67,200,77,88];
var max = getMax(arr); //调用函数
console.log('最大值是:' + max);
6.arguments
当不确定又多少个参数传递的时候,可以使用arguments来获取,JavaScript中,arguments实际上它是当前函数的一个内置对象。所有函数都内置了一个arguements对象,arguments对象中存储了传递的所有的实参,arguments展示形式是一个伪数组,因此可以进行遍历。
伪数组具有以下特点:
- 具有length属性
- 按索引方式存储数据
- 不具有数据中的push、prop等方法
**注意:**在函数内部使用该对象,用此对象获取函数调用时传递实参
//所有的函数内部JavaScript都给我们内置了arguments
//写一个函数,传入数值后,求数值中的最大值
function getMax(){
var max = arguments[0];//默认最大值是传入实参的第一个数字
//遍历出传入的实参
for(var i = 0; i < arguments.length; i++){
if(max < arguments[i]){ //如果出现我们认为的最大值小于里面的值
max = arguments[i];
}
}
//返回最大值
return max;
}
var max1 = getMax(1,2);
console.log(max1);
//------------------------------------
var max2 = getMax(1,2,3);
console.log(max2);
7.函数声明的两种方式
第一种方式:
命名函数:利用函数关键字function自定义函数式
//声明定义方式
function fn(){
//....
}
//调用
fn();
- 因为函数是有名字的,所以也被称之为命名函数
- 调用函数的代码也可以放在声明函数的前面,也可以放在声明函数的后面
第二种方式:
匿名函数:利用函数表达式的写法如下:
//定义函数
var fn = function(){
//...
}
//调用方式,函数调用必须写到函数体下面
fn();
- 应为函数没有名字,所以也被称之为匿名函数
- 这个fn里面存储的是一个函数
- 函数白哦大师原理根变量声明的方式一样的
- 函数调用的代码必须写到函数体后面
翻转数组案例
/*
var arr1 = [1,9,3,4,5];
console.log(reverse(arr1));
//利用函数,实现任意数组翻转
function reverse(arr){
var newArr = []; //翻转之后的新的数组
//遍历原来的数组
for(var i = arr.length - 1; i >= 0;i--){
newArr[newArr.length] = arr[i];// 翻转
}
return newArr;
}
*/
var reverse = function(arr){
var newArr = [];//新的数组用于存放反转后的数组中的数据
//遍历原来数组
for(var i = arr.length - 1; i >= 0; i--){
newArr[newArr.length] = arr[i]; //反转
}
return newArr;//返回翻转后的
}
var arr1 = [1,2,3,4,5];
console.log(reverse(arr1));
8.冒泡排序法
var arr = [1,4,2,9]
function sort(arr){
//外层:比较的轮数
for(var i = 0; i < arr.length - 1; i++){
//内层:每轮比较的次数
for(var j = 0; j < arr.length - i - 1; j++){
//比较大小
if(arr[j] > arr[j + 1]){
var temp = arr[j];//定义一个临时的变量,该变量暂时存储最大值
arr[j] = arr[j + 1] //前面一个位置放小的值
arr[j + 1] = temp; //后面这个位置就放大的
}
}
}
//返回重新排序的数组
return arr;
}
var arr = [1,4,2,9,20,8,7,16]
console.log(sort(arr))
9.函数是可以相互调用的
function f1(){
console.log('这是f1函数');
}
function f2(){
console.log('这是f2函数');
}
//函数之间是可以相互调用的
function f3(){
f1(); //调用了f1函数
console.log('这是f3函数');
f2(); //调用了f2函数
}
f3();
函数可以自己调用自己,但是必须要有结束条件
//我们要避免这种写法
// function f1(){
// console.log('测试');
// f1();
// }
// f1();
//递归
//1 1 2 3 5 8 13 21 34
/*
month
1 getSum(1) = 1
2 getSum(2) = 1
3 getSum(3) = getSum(2) + getSum(1) = 1 + 1 = 2
4 getSum(4) = getSum(3) + getSum(2) = 2 + 1 = 3
5 getSum(5) = getSum(4) + getSum(3) = 3 + 2 = 5
...
*/
function getSum(month){
if(month == 1){
return 1;
}
if(month == 2){
return 1;
}
return getSum(month - 1) + getSum(month - 2)
}
console.log(getSum(9));
二、作用域
1.概念
通常来说,一段程序代码中所用到的名字(变量名)并不总是有效和可用的,而限定这个名字的可用性的代码范围就是这个名字的作用域。,作用域的使用提高了程序逻辑的局部性,增强了程序的可靠性、减少了名字出现冲突可能。
- 全局作用域
- 作用域所有代码指定的环境(整个script标签内部)或者一个独立的js文件
- 局部作用域 (函数作用域)
- 作用域函数内的代码环境,就是局部作用域,因为跟函数有关系,所以我们称之为函数作用域
for(var i = 1; i <= 10; i++){
}
console.log(i);
//局部作用域 (函数作用域),在函数内部就是局部作用域,这个代码的名字旨在函数内部起效果和作用
function f1(){
//局部作用域
var num = 10;
num++;
return num;
}
f1();
console.log(num); //不能访问局部作用域
2.执行角度看
从执行效率角度来看局部和全局变量
- 全局变量,只有浏览器关闭的时候才会销毁,比较占用内层
- 局部变量,当我们程序执行完毕后就会销毁,比较节约内存资源
var num = 10; //全局作用域
console.log(num);
function f1(){
console.log(num); //可以
}
f1();
function f2(){
var num2 = 10; //局部作用域,只能在函数内部使用
num2++;
}
3.JS中没有块作用域
- 块作用域由 { } 包括
- 在其它编程语言中(如:java和python等),在if语句、循环语句中创建变量,仅仅只能在if语句、本循环中使用
java 由块级作用域:
if(true){
int num = 123;
}
System.out.print(num); //报错的
以上java代码会报错,因为代码中{ } 即是一块作用域,其中声明的变量num,在“{ }“之外不能使用
js中没有块级作用域(在ES6之前)
if(true){
int num = 123;
}
console.log(num); //123
if(true){
num = 10;
}
{
console.log(num);
console.log(num);
console.log(num);
console.log(num);
console.log(num);
}
{
console.log('...')
}
//对于if 或者是循环语句,后面的大括号(块作用域)是可以不写的,如果不写默认后面的第一行代码输入该块作用域
if(num < 10)
console.log('num是10或以上');
console.log('2222');
for(var i = 1; i <= 10; i++);
console.log(i)
4.变量的作用域
4.1 概念
在JavaScript中,根据作用域的不同,变量可以分为两种
- 全局变量
- 在全局作用域下声明的变量都叫做全局变量(在函数外部定义的变量)
- 全局变量在代码的任何地方都可以使用
- 在全局作用域下var声明的变量就是全局变量
- 特殊情况下,在函数内不使用var声明的变量也是全局变量(不建议使用)
- 局部变量
- 在局部作用域下声明的变量都是局部变量(函数内部定义的变量)
- 局部变量只能在该函数内部使用
- 在函数内部var声明的变量是局部变量
- 函数额形参实际上就是局部变量
4.2 区别
- 全局变量:
- 在任何一个地方都可以使用,只有在浏览器关闭的时候才会被销毁,因此比较占用内存
- 局部变量:
- 在函数内部使用,当执行函数时,函数内部代码块被执行的时候,会被初始化,当代码结束的时候就会被销毁。
5.作用域链
// function f1(){
// var num = 123;
// function f2(){
// console.log(num);
// }
// f2();
// }
// var num = 456;
// f1(); //123
var a = 1;
function fn1(){
var a = 2;
var b = '22'
fn2();
function fn2(){
var a = 3;
fn3();
function fn3(){
var a = 4;
console.log(a); //4
console.log(b); //22
}
}
}
fn1();
// 案例1:结果是几?
function f1(){
var num = 123;
function f2(){
var num = 0;
console.log(num);
}
f2();
}
var num = 456;
f1(); //0
//案例2:结果是几?
var a = 1;
function fn1(){
var a = 2;
var b = '22';
fn2();
function fn2(){
var a = 3;
fn3();
function fn3(){
var a = 4;
console.log(a); //4
console.log(b); //22
}
}
}
fn1();
6.预解析
6.1 概念
JavaScript代码由浏览器中的javaScript解析器来执行的。javaScript解析器在运行javaScript代码的时候会分为两步来执行:
- 预解析
- 在当前的作用域下,js代码执行之前,浏览器会默认把带有var 和function声明的变量在内存中进行提前声明或者是定义
- 代码执行
- 从上到下执行js语句
**注意:**预解析会把变量和函数的声明在代码执行之前执行完成。
6.2 变量预解析
预解析也叫:变量、函数提升。变量的声明会被提升到当前作用域的最上面
console.log(num); //undefined
var num = 10;
变量提升只提升声明,不提升赋值。
6.3 函数预解析
函数预解,函数的声明会被提升到当前作用域的最上面,但是不会调用函数
fn();
function fn(){
console.log('aaaa');
}
结果会执行打印。
注意:函数声明代表函数的整体,所以函数提升后,函数名代表了整个函数,但是函数没有被调用
函数表达式声明函数问题
fn();
var fn = function(){
console.log('aa');
}
结果:提示报错 —— ”fn is not a function“
解析:该段代码执行之前,会做变量声明提升(var fn),fn在提升之后的值是undefined,而fn调用时在fn被赋值为函数之前,此时fn的值是undefined,所以无法被作为函数调用
cript解析器来执行的。javaScript解析器在运行javaScript代码的时候会分为两步来执行:
- 预解析
- 在当前的作用域下,js代码执行之前,浏览器会默认把带有var 和function声明的变量在内存中进行提前声明或者是定义
- 代码执行
- 从上到下执行js语句
**注意:**预解析会把变量和函数的声明在代码执行之前执行完成。
6.2 变量预解析
预解析也叫:变量、函数提升。变量的声明会被提升到当前作用域的最上面
console.log(num); //undefined
var num = 10;
变量提升只提升声明,不提升赋值。
6.3 函数预解析
函数预解,函数的声明会被提升到当前作用域的最上面,但是不会调用函数
fn();
function fn(){
console.log('aaaa');
}
结果会执行打印。
注意:函数声明代表函数的整体,所以函数提升后,函数名代表了整个函数,但是函数没有被调用
函数表达式声明函数问题
fn();
var fn = function(){
console.log('aa');
}
结果:提示报错 —— ”fn is not a function“
解析:该段代码执行之前,会做变量声明提升(var fn),fn在提升之后的值是undefined,而fn调用时在fn被赋值为函数之前,此时fn的值是undefined,所以无法被作为函数调用