★文章内容学习来源:拉勾教育大前端就业集训营
本篇学习目标:
1.更深层次理解函数;
2.掌握函数表达式——另一种定义函数的方式;
3.清除函数数据类型;
4.理解arguments对象;
5.熟练运用函数递归;
6.理解变量类型(全局/局部)、作用域及作用域链;
7.理解预解析的好处;
8.掌握IIEF自调用函数实现方法。
变量名()
执行,不能使用函数名()
执行。//函数表达式——定义函数的另一种方式
//将函数fun1赋值给一个变量foo1
var foo1 = function fun1 ( ){
console.log("今天天气很好");
}; //注意结束处有分号
//调用——用变量名加括号,不用函数名了
foo1();
//因为调用也用不到函数名,所以其实函数名可以省略
//将匿名函数赋值给一个变量foo1
var foo1 = function (){
console.log("今天天气很好");
}; //注意结束处有分号
//调用——用变量名加括号,不用函数名了
foo1();
Function
(object
的一种)。//检测函数数据类型
function fun1 () {
console.log(1);
}
console.log(typeof(fun1)); //function
//检测函数数据类型
var foo1 = function (a,b){
console.log(a + b);
};
console.log(typeof(foo1));//function
//将函数作为另一个函数的参数
setInterval(function () {
console.log(1);
},1000);
//以上setInterval函数有两个参数,第一参数位置是匿名函数(实现控制台输出1),第二个参数位置是1000;
//其中1000表示1000毫秒也就是1秒
//总体的实现的功能:每隔1秒执行一下第一个参数位置的值。
JavaScript 中,arguments
对象是比较特别的一个对象,实际上是当前函数的一个内置属性; 也就是说所有函数都内置了一个 arguments
对象;
有了arguments对象,形参不会成为实参的限制。
arguments
对象中存储了传递的所有的实参。
arguments
是一个伪数组,因此及可以进行遍历。
总结一句是: 函数的实参个数和形参个数可以不一致,所有的实参都会存储在函数内部的 arguments
类数组对象中。
//arguments对象
function sum1(a,b) {
console.log(a+b);
}
sum1(1,5);//6
sum1(2,6,6,7); //8,只加前两个
sum1(2); //2+undefined=NaN
function fun(a) {
console.log(a);
}
fun (1,2,3,4,5,6,7); //实参的个数是远远大于形参的
function fun() {
console.log(arguments);
console.log(arguments.length);//7
//使用数组遍历可以把所有实参都遍历一遍
for (var i = 0 ; i < arguments.length ; i++) {
console.log(arguments[i]);
}
}
定义一个求和函数,如果传入 1 个参数,返回它自己;如果传入两个参数,返回他们的和;如果传入三个参数,先比较前两个的大小,大的与第三个参数求和返回;如果传入 4 个及以上,输出错误提示。
//方法1:用多分支if语句
function sum(a,b,c) {
if (arguments.length == 1) {
return a
} else if (arguments.length == 2) {
return a + b;
} else if (arguments.length == 3) {
return a > b ? a+c : b+c ;
} else {
throw new Error("您输入的参数超过3个!");
}
}
console.log(sum(1)); //1
console.log(sum(1,2)); //3
console.log(sum(1,2,3)); //5
//console.log(sum(1,2,3,4)); //您输入的参数超过3个!
//方法2,用switch语句
function sum(a,b,c) {
switch (arguments.length) {
case 1:
return a;
break;
case 2:
return a+b;
break;
case 3:
return a>b?a+b:b+c;
break;
default:
throw new Error("您输入的参数超过3个!");
}
}
console.log(sum(1));
console.log(sum(1,2));
console.log(sum(1,2,3));
console.log(sum(1,2,3,4));
RangeError
):超出计算机的计算最大能力。举例:如果实参为1,返回1;如果实参是大于1的数,返回实参+函数调用上一项。
//函数递归举例
function fun (a) {
if (a == 1) {
return 1;
} else if (a > 1) {
return a + fun(a-1);
}
};
console.log(fun(1)); //1
console.log(fun(2));// 2+ fun(1)=2+1=3
console.log(fun(3));//3+fun(2)=3+2+1=6
输出斐波那契数列的某一项的值。
斐波那契数列:后面的一项数据是前两项数据之和。1,1,2,3,5,8,13,21,34,55……
//其中参数a代表斐波那契数列的第a项,feibo(a)输出的结果是第a项的值
function feibo(a) {
if (a == 1) {
return 1;
} else if (a == 2) {
return 1;
} else if (a > 2) {
return feibo(a-1)+feibo(a-2);
}
}
console.log(feibo(1)); //1
console.log(feibo(2)); //1
console.log(feibo(3)); //2
console.log(feibo(4)); //3
console.log(feibo(5)); //5
console.log(feibo(6)); //8
console.log(feibo(7)); //13
console.log(feibo(8)); //21
console.log(feibo(9)); //34
console.log(feibo(10)); //44
//函数部分,也可以稍微省略一点写成这样
function feibo(a) {
if (a == 1 || a ==2) {
return 1;
} else if (a > 2) {
return feibo(a-1)+feibo(a-2);
}
}
//定义一个函数
function fun() {
//其中有个局部变量a
var a = 2;
//在函数内部是可以调用的
console.log(a);
}
//调用函数
fun ();//2
//外部调用函数内部的局部变量a,会出现错误!
console.log(a); //错误
{ }
中的结构体都属于一个块,在这之中定义的所有变量在代码块外都是不可见的,我们称之为块级作用域。但是现阶段(目前在学js5),可以认为 JavaScript 没有块级作用域。
因为在es6之前没有块级作用域的的概念,只有函数作用域。
//目前没有块级作用域,只有函数作用域概念。
//函数定义在另一个函数内部,如果外部函数没有执行时,相当于内部代码没写。
function outer() {
var a = 1;
console.log(a);
function inner() {
var b = 2;
console.log(b);
}
//因为inner函数定义在outer函数内部,所以也要在outer函数内部调用
inner(); //但是只有外部的outer函数调用后,这个才会起作用
}
outer(); //outer函数是可以在外部调用的
inner();//inner函数就不可以在外部调用了,会出现错误
函数内部的变量
,只能在函数作用域内部被访问到,在外面没有定义的。定义在全局的变量
,作用域范围是全局,在整个 js 程序任意位置都能够被访问到。function f1() {
function f2() {
}
}
var num = 456;
function f3() {
function f4() {
}
}
function f1() {
var num = 123;
function f2() {
console.log(num);
}
f2();
}
var num = 456;
f1();
//遮蔽效应
var a = 1;//全局作用域中的变量a
function outer() {
var a = 2; //外部函数作用域中的变量a
console.log(a); //2//从本层往外依次查找,直到找到第一个变量定义
function inner() {
var a =3; //内部函数作用域内的a
console.log(a); //3//从本层往外依次查找,直到找到第一个变量定义
}
console.log("再执行内部函数");
inner(); //3
}
console.log("先执行外部函数");
outer(); //2
1. 把变量的声明提升到当前作用域的最前面,只会提升声明,不会提升赋值。
2. 把函数的声明提升到当前作用域的最前面,只会提升声明,不会提升调用。
3. 先提升 var,再提升 function。
//变量声明提升
console.log(a); //虽然先调用,但是没有报错,而是输出了undefined值
var a = 1;
//相当于是如下执行的
var a;
console.log(a); //undefined
a = 1;
//函数声明提升
fun (); //先调用也可以正常执行,输出12
function fun () {
console.log(12);
}
//举例:先变量声明提升,再函数声明提升
console.log(fun);
var fun = "haha";
fun();
function fun () {
console.log(12);
}
//相当于下面这个过程
var fun;
function fun () {
console.log(12);
}
console.log(fun);//输出函数
fun = "haha";
fun();//错误类型提示,这时候fun已经被赋值成"haha",不是个函数了
//函数表达式进行的是变量声明提升
foo1(1,5);
var foo1 = function (a,b) {
console.log(a+b);
}
//举例
fun1 ();
fun2 ();
fun3 ();
fun4 ();
function fun1 () {
console.log(1);
console.log(3);
console.log(5);
}
function fun2() {
console.log(2);
console.log(4);
console.log(6);
}
function fun3() {
console.log(-1);
console.log(-3);
console.log(-5);
}
function fun4() {
console.log(-2);
console.log(-4);
console.log(-6);
}
IIFE
:immediately-invoked function expression,叫做即时调用的函数表达式,也叫做自调用函数;()
运算符。//函数名定义的不可以实现自调用,比如这样的函数
function fun1 () {
console.log(1);
}
()
运算符就可以立即执行。//函数表达式的形式定义的函数可以实现自调用,方法是后面直接加()运算符
var foo1 = function() {
console.log("函数表达式的形式定义的函数可以实现自调用,方法是后面直接加()运算符");
} ();
IIFE
,可以想办法将函数矮化成表达式。+
-
()
//将函数矮化成表达式,以实现自调用
//算术运算符 + - ()
+ function fun1() {
console.log("1前面加算术运算符+");
}();
- function fun2 () {
console.log("2前面加算术运算符-");
}();
( function fun3 () {
console.log("3整体加算术运算符()");
})();
!
非运算
//将函数矮化成表达式,以实现自调用
//逻辑运算符!
! function fun4 () {
console.log("4前面加逻辑运算符!")
}();
IIFE
结构可以关住函数的作用域,在结构外面是不能调用函数的。IIFE
最常用的是 () 运算符,而且函数可以不写函数名,使用匿名函数。下篇继续:【74】JS(7)——对象①基本介绍