1.函数
函数: 一个重复执行的代码块。可以用来简化要重复被使用的代码,提高程序执行的效率。
2.函数的三种声明方式
- 使用 function 关键字声明函数
语法:
function 函数名(){
函数体
}
// 声明函数
function fn1(){
console.log("第一种声明方式, 使用function关键字声明的函数");
}
// 调用函数fn1
fn1();
- 使用函数表达式来声明函数
语法:
var 函数名 = function(){
函数体
}
如果一个函数没有名字, 那就是匿名函数
// 声明函数
var fn2 = function(){
console.log("第二种声明方式, 使用函数表达式声明的函数");
};
// 调用函数
fn2();
- 使用构造函数来声明函数
语法:
var 函数名 = new Function();
// 声明函数
var fn3 = new Function("console.log('第三种声明方式, 使用构造函数来声明函数')");
// 调用函数
fn3();
3.函数名提升
就是可以把函数的调用放在函数声明前面。
// 使用function关键字声明函数, 具有函数名提升
fn1()
function fn1(){
console.log("使用function关键字, 声明函数");
}
// 使用函数表达式声明函数, 没有函数名提升
fn2(); // fn2 is not a function
var fn2 = function(){
console.log("使用函数表达式, 声明函数");
}
// 使用构造函数声明函数, 没有函数名提升
fn3(); // fn3 is not a function
var fn3 = new Function("使用构造函数声明函数");
4.函数的参数
- 函数的参数: 从函数外部传递给函数内部使用的值
// 声明一个calc
function calc(v1, v2){
// console.log( 1 + 3 );
console.log(v1 + v2);
}
// 调用函数
calc(4, 8); // 12
calc(2, 5); // 7
// var a = +prompt("输入v1")
// var b = +prompt("输入v2")
// 从函数外部传递变量给函数内部使用
// calc(a, b);
- 函数的参数可以分为 形参 和 实参
// 2. 函数的参数可以分为 形参 和 实参
// 形参: 在函数的声明阶段, 括号中携带的参数就是形参 (形式参数)
// function fn1( 形参列表 ){}
function fn1(a, b, c, d){
console.log(a);
console.log(b);
console.log(c);
console.log(d);
}
// 实参: 在函数的调用阶段, 括号中携带的参数就是实参 (实际参数)
// fn1( 实参列表 )
fn1( "张三", 25, "男", "abcd" );
- 形参和实参的个数可以不一致
// 如果 形参多 而 实参少, 多余的形参时undefined
function fn2( v1, v2, v3, v4, v5 ){
console.log("v1 =>", v1); // 红色
console.log("v2 =>", v2); // 橙色
console.log("v3 =>", v3); // undefined
console.log("v4 =>", v4); // undefined
console.log("v5 =>", v5); // undefined
}
fn2("红色", "橙色");
// 如果实参多 而 形参少, 多余的实参会被忽略
function fn3( color1, color2 ){
console.log("color1 =>", color1); // 红色
console.log("color2 =>", color2); // 橙色
}
fn3( "红色", "橙色", "黄色", "绿色", "青色", "蓝色", "紫色" );
- arguments 是函数内部本身具有的对象, 该值是实参组成的数组列表, 里面包含所有的实参
function fn4( c1, c2 ){
// console.log("c1 =>", c1); // 红色
// console.log("c2 =>", c2); // 橙色
console.log("arguments =>", arguments); // 实参组成的数组列表
console.log(arguments.length); // arguments的长度就是实参的个数
// 获取每一个实际参数
for(var i=0; i<arguments.length; i++){
console.log( "arguments[i] =>", arguments[i] );
}
// arguments.callee 表示函数本身
console.log(arguments.callee);
console.log(fn4);
console.log(arguments.callee === fn4);
}
fn4( "红色", "橙色", "黄色", "绿色", "青色", "蓝色", "紫色" );
控制台输出如下👇👇👇
5.函数的返回值
- return 关键字:可以把某个值, 作为函数执行完以后的返回值进行传递
function fn1(){
var add = 1+3; // 4
// 把gz2149作为函数的返回值, 传递给外面
return "asd16161";
}
// 把函数执行的结果, 返回给一个变量i
var i = fn1();
// 输出函数执行的结果
console.log( "i =>", i ); // asd16161
- 函数如果没有 return 返回值, 则默认返回undefined
function fn2(){
var i = 1+2+3+4+5;
console.log("以下三种情况都是返回undefined");
// 以下三种情况都是返回undefined
// 函数不写返回值
// return;
// return undefined;
}
var j = fn2();
console.log( "j =>", j ); // undefined
- return关键字 可以跳出函数(阻止函数向下执行)
function fn3(){
console.log("语句1");
console.log("语句2");
// 遇到return关键字, 函数不会向下执行
return ;
console.log("你将看不见我");
}
var k = fn3();
console.log( "k =>", k ); // undefined
6.函数的递归调用
- 函数的递归: 就是函数, 在函数的内部自己调用自己
function fn(i){
console.log(i++); // 1
// 每次都会调用自己, 造成死循环
// fn(i)
if(i>10){
return;
}else{
// 在函数内部, 自己调用自己
fn(i)
}
}
// 调用一次 fn
fn(1)
- 声明获取定位的函数
function getLocation(){
var key = confirm("是否给予用户定位权限");
console.log("key =>", key);
// 如果用户没有给予定位权限 (key为false)
if(!key){
alert("获取权限失败, 请重新获取定位权限");
// 在函数内部自己调用自己, 重新获取定位权限
getLocation();
// 阻止函数向下执行
return;
}
alert("进入程序");
}
// 调用获取定位的函数
getLocation();
- 用函数自调用, 实现登陆的功能
var uname = "viewonly"
var upass = "123456"
function login(){
var un = prompt("请输入用户名");
var up = prompt("请输入密码");
if(un == uname && up == upass){
alert("进入程序");
}else{
alert("用户名或密码, 不正确");
// 重新调用, 重新输入, 重新判断
login();
}
}
// 调用函数
login();
7.函数的作用域
概念: 变量生效的范围就是作用域
在es5中, 作用域可以分为 全局作用域 和 函数作用域
根据作用域: 可以将变量分为 全局变量(对标 全局作用域) 和 局部变量(对标 函数作用域)
-
全局作用域: 所有作用域加起来就是全局作用域
-
全局变量: 在全局作用域中声明的变量就是全局变量 (全局作用域都能生效的变量)
-
函数作用域: 整个函数的区域, 就是函数作用域
-
局部变量: 在函数作用域里面声明的变量就是局部变量 (只在函数作用域里面生效),在函数执行完后会立即销毁
var i = 5;
console.log("函数外面输出 i =>", i); //5
var k = "op789";
console.log("函数外面输出 k =>", k); // "op789"
var n = 10;
var h = 10;
function fn(){
console.log("函数里面输出 i =>", i); //5
// var k; 变量名提升, 变量的声明会提升到当前作用域的第一行去声明
console.log(k); // undefined
// 函数作用域: 整个函数的区域, 就是函数作用域
// 局部变量: 在函数作用域里面声明的变量就是局部变量 (只在函数作用域里面生效)
var j = 10;
console.log("函数里面输出 j =>", j); //10
// 变量名提升, 变量的声明会提升到当前作用域的第一行去声明, 值留在本地
var k = "abc";
console.log(k); // abc
// 局部变量可以和全局变量的变量名一样, 但是局部变量不会影响全局变量, (两个变量是无关的)
var n = 20;
console.log("n =>", n); // 20
// 没有使用var关键字, 没有创建局部变量, 直接修改的全局变量
h = 20;
console.log("h =>", h); // 20
// 变量的声明可以不使用var关键字, 直接赋值也可以, 但不建议这么做(1.不利于表达意图, 2.容易创建或污染全局变量)
u = 3000;
console.log("u =>", u); // 3000
}
// 局部变量在函数执行完以后, 会立即销毁
fn();
// console.log("函数外面 j =>", j); // 报错 j is not defined
console.log("n =>", n); // 10
console.log("h =>", h); // 20
console.log("u =>", u); // 3000
8. 函数作用域链
函数的作用域是向内嵌套的, 内层函数的作用域不会向外扩散
内层函数可以访问外层函数的变量, 但外层函数 无法访问 内层函数的变量
当函数访问变量时, 根据就近原则在这个作用域链中从内到外查询变量。
function fn1(){
var j = "乙"
console.log(i, j, k, L); // k is not defined
function fn2(){
var k = "丙"
// console.log(i, j, k, L); // L is not defined
function fn3(){
var L = "丁"
// 当函数访问变量时, 根据就近原则在这个作用域链中从内到外查询变量。
console.log(i, j, k, L); // 甲乙丙丁
}
fn3()
}
fn2()
}
fn1();