JavaScript函数
1.函数格式
-
function 函数名称(形参列表){
return x;
}
形参需要和C语言区别,js里边只要==写个形参名字就行==
2.函数的注意点
一个函数可以有形参也可以没有形参(零个或多个)
一个函数可以有返回值也可以没有返回值,没有返回值默认是undefined
-
JavaScript中的函数和数组一样, 都是引用数据类型(对象类型)
-
既然是一种数据类型, 所以也可以保存到一个变量中,将来可以==通过变量名称找到函数并执行函数==
let say = function () { console.log("hello world"); } say();
-
-
调用函数时实参的个数和形参的个数可以不相同 ==我只能说一giao我里giao==
function getSum(a, b) { console.log(a, b); return a + b; } let res = getSum(10); //b就是undefined let res = getSum(10, 20, 30); //多余的收不到
3.函数arguments
因为console.log();也是通过()来调用的, 所以log也是一个函数,为什么log函数可以接收1个或多个参数,内部的实现原理就用到了arguments
-
arguments的作用:保存所有传递给函数的实参,我们写一个求和函数
function getSum() { // 注意点: 每个函数中都有一个叫做arguments的东西 // arguments其实是一个伪数组,可以通过下标索引取值 // console.log(arguments); let sum = 0; for (let i = 0; i < arguments.length; i++){ let num = arguments[i]; // console.log(num); // 10 20 30 sum += num; } return sum; } //这样我们就实现了不管有多少参数都能求和 let res = getSum(10, 20, 30, 40); console.log(res);
4.函数扩展运算符
-
扩展运算符在函数的形参列表中的作用
-
将传递给函数的所有实参打包到一个数组中,注意和在等号左边一样, ==也只能写在形参列表的最后==
function getSum(...values) { //也可以实现不管多少个实参都能相加 // console.log(values); let sum = 0; for (let i = 0; i < values.length; i++){ let num = values[i]; sum += num; } return sum; } let res = getSum(10, 20, 30, 40); console.log(res); //只能写在最后面哦 function getSum(a, ...values)
-
5.函数形参默认值
在ES6之前可以通过逻辑运算符来给形参指定默认值
-
格式: 条件A || 条件B,如果条件A成立, 那么就返回条件A,如果条件A不成立, 无论条件B是否成立, 都会返回条件B
function getSum(a,b){ a=a||"tanwenchao"; b=b||"lalala"; console.log(a,b); } getSum(); //就算没传也有值
从ES6开始, 可以直接在形参后面通过=指定默认值
-
注意点: ES6开始的默认值还可以从其它的函数中获取
//function getSum(a = "tan", b = "xu") { //下面试通过函数获取 function getSum(a = "指趣学院", b = getDefault()) { console.log(a, b); } getSum(); // getSum(123, "abc"); function getDefault() { return "tan" }
6.函数作为参数和返回值
注意点:在进行参数传递操作时,要想一想带括号和不带括号,比如下面
let say = function () { console.log("hello world"); } let fn = say; fn(); //我也可以这样写 let fn = say(); fn;
-
作为返回值
在其它编程语言中函数是不可以嵌套定义的
function test() { let say = function () { console.log("hello world"); } return say; } let fn = test(); // 相当于let fn = say; fn();
-
作为参数
let say = function () { console.log("hello world"); } function test(fn) { // let fn = say; fn(); } test(say);
7.匿名函数
匿名函数就是没有名称的函数
-
作为其他函数的返回值
function test() { return function () { console.log("hello lnj"); }; } let fn = test(); // let fn = say; fn();
-
作为一个立即执行的函数
注意点: 如果想让匿名函数立即执行, 那么必须使用()将函数的定义包裹起来才可以
(function () { console.log("hello it666"); })();
-
作为其他函数的参数
function test(fn) { // let fn = say; fn(); } test(function () { console.log("hello world"); });
8.箭头函数
箭头函数是ES6中新增的一种定义函数的格式,目的就是为了简化定义函数的代码,相比之前有什么区别呢.如下
let 函数名称 = (形参列表) =>{}
let say = (name) => { console.log("hello"+name); } say(); //上面看起来并不简化 //在箭头函数中如果只有一个形参, 那么()可以省略 let say = name => { console.log("hello"+name); } //在箭头函数中如果{}中只有一句代码, 那么{}也可以省略 let say = name => console.log("hello"+name); say("谭文超");
9.递归函数
和以前学的一样
10.函数中变量作用域
我们知道,js中有两种定义变量的方法,var 和 let ,区别之前已经学过,他们在变量作用域中也有区别
- 首先了解一下块级作用域和局部作用域
- 在ES6中只要{}没有和函数结合在一起, 那么应该"块级作用域",例如写在while循环,for循环等
- 函数后面{}中的的作用域, 我们称之为"局部作用域
- 在了解一下var和let的作用域的区别
- 在块级作用域中通过var定义的变量是全局变量,在局部作用域中通过var定义的变量是局部变量==感觉很沙雕的样子==
- 通过let定义的变量,只要是在{}里就是局部变量,==var和C语言的变量差不多==
- 无论是在块级作用域还是在局部作用域, 省略变量前面的let或者var就会变成一个全局变量 如 {num = 1;}
11.作用域链基本了解
正是因为有了函数的嵌套,所以才有了作用域链的概念,同时由与ES6之前和之后的语法差异,研究"作用域链"的时候最好将ES6之前和ES6分开研究.
-
ES6之前没有块级作用域, 只有全局作用域和局部作用域
- 全局作用域我们又称之为0级作用域
- 定义函数开启的作用域就是1级/2级/3级/...作用域,JavaScript会将这些作用域链接在一起形成一个链条, 这个链条就是作用域链,0 ---> 1 ----> 2 ----> 3 ----> 4
-
变量在作用域链查找规则
1先在当前找, 找到就使用当前作用域找到的,当前作用域中没有找到, 就去上一级作用域中查找,以此类推直到0级为止, 如果0级作用域还没找到, 就报错
var num = 123; function demo() { // 1级作用域 var num = 456; function test() { // 2级作用域 // var num = 789; console.log(num); } test(); } demo(); //当前代码会输出456
ES6就是多加了一个块级作用域,但是通过let 定义变量不管是新开了一个函数还是一个代码块,都会开启作用域的.
12.函数预解析
什么是预解析:浏览器在执行JS代码的时候会分成两部分操作:预解析以及逐行执行代码,也就是说浏览器不会直接执行代码, 而是加工处理之后再执行, 这个加工处理的过程, 我们就称之为预解析.
预解析规则:将变量声明和函数声明提升到当前作用域最前面,将剩余代码按照书写顺序依次放到后面
-
三种预解析
//下为第一种,会预解析,可以先调用在创建, say(); function say(){ console.log("谭文超"); }
//下为第二种 say(); // 如果将函数赋值给一个var定义的变量, 那么函数不会被预解析, 只有变量会被预解析 var say = function() { console.log("hello itzb"); } //预解析完是这样的 /* var say; //undefined say();// say is not a function say = function() { console.log("hello itzb"); } */
//下为第三种 // ES6定义函数的格式不会预解析 say(); // say is not defined let say = () => { console.log("hello itzb"); }
我们来看几个练习
var num = 123; fun(); function fun() { console.log(num); var num = 666; } //输出什么东西 /* var num; function fun() { var num; console.log(num); // undefined num = 666; } num = 123; fun(); */
var a = 666; test(); function test() { var b = 777; console.log(a); console.log(b); console.log(c); var a = 888; let c = 999; } /* var a; function test() { var b; var a; b = 777; console.log(a); // undefined console.log(b); // 777 console.log(c); // 报错 a = 888; let c = 999; } a = 666; test(); */
-
下面这个了解一下(面试或笔试)
// 在ES6之前没有块级作用域, 并且没有将这两个函数定义到其它的函数中, // 所以这两个函数应该属于全局作用域 // 注意点: // 1.在高级浏览器中, 不会对{}中定义的函数进行提升 // 2.只有在低级浏览器中, 才会按照正常的方式解析 if(true){ function demo() { console.log("1"); } }else{ function demo() { console.log("2"); } } demo();//高级浏览器输出1 /* 低级浏览器如下解析,输出2 function demo() { console.log("1"); } function demo() { console.log("2"); } if(true){}else{} demo(); */
// 注意点: 如果变量名称和函数名称同名, 那么函数的优先级高于变量 // 一定要记住, 在企业开发中千万不要让变量名称和函数名称重名 console.log(value); // 会输出函数的定义 var value = 123; function value() { console.log("fn value"); } console.log(value); // 会输出123 /* function value() { console.log("fn value"); } console.log(value); var value; value = 123; console.log(value); */