1. 函数的定义
函数体里面是普通的操作语句,不是对象的键值对。
1) 构造函数
(完整详细的写法,但很少用)
sum = new Function('a', 'b', 'console.log(a); return a + b');
2) 具名函数
(有名字的函数)
function sum(a,b){
return a + b;
}
3) 匿名函数
(没有名字的函数,也叫函数表达式)该方法创建的函数sum不会挂到window上
let sum = function(a,b){
return a + b;
}
let sum = function fn(a,b){
return a + b;
}
fn(1,2) //语法错误,fn不存在,如果函数定义在等号右边则是以表达式的方式存在,其fn函数的作用域也
只在表达式右边,不是全局,出了表达式就不存在。
4) 箭头函数(没有this和arguments属性)
ES6函数定义新语法,sum不会挂到window上,且箭头函数没有this和arguments属性
let sum = (a,b)=> a+b //函数体只有一个语句时,箭头左边是传入参数,右边是函数体
//返回对象
(错误) let fn = (x)=> {name: x} //若返回值为一个对象时,花括号默认是代码块标志,
(正确) let fn = (x)=> ({name: x}) //给对象加个括号,标明里面是一个整体,即可正确返回对象
let sum = (a,b)=> {
return a + b;
} //函数体有多个语句
每个函数都有返回值,若不写return,默认返回一个undefined,并且返回值是在函数执行时返回
任何函数的构造函数都是自己
箭头函数全都是匿名函数: 普通函数可以有匿名函数,也可以有具名函数
2. 函数自身属性
1) name
name这个属性值就是函数本身的名字
2) length
该属性是用来记录函数的形参个数,其属性值是number类型的数字,是形参个数。
3) arguments (箭头函数没有该属性)
在JS中,函数的定义中形参可以不写,在函数调用时可以直接传参,函数会接收传进来的参数,并且依次放进函数的arguments属性里,arguments属性值看似是个数组,但其实说对象更准确,里面数据是按照数组存储的方式存储的,所以arguments就是数组里提到的伪数组。当调用函数test(1,2)时,输出arguments组成如下所示:
arguments(2){
0: 1
1: 2
length: 2
callee: ƒ test()
Symbol(Symbol.iterator): ƒ values()
__proto__: Object
}
总结一句就是:arguments是包含了函数所有参数的伪数组
伪数组不具有数组的公共属性,如push、pop等,如想把伪数组变成数组,可通过Array.from实现。
4) caller
3. 函数公共属性
4. 闭包
4.1 什么是闭包
如果一个函数用到了外部的变量,那么这个函数加这个变量就叫做闭包
未完待续。。。。
5. 调用栈
5.1 什么是调用栈
JS引擎在执行一个函数前,需要把函数所在的环境push到一个数组里,这个数组叫做调用栈。等函数执行完了,就会把环境pop弹出来,然后return到之前的环境,继续执行后续代码
6. 立即执行函数
6.1 来源
在JS中,在函数外用var声明一个变量得到的是一个全局变量,那么如何得到一个局部变量呢?只用通过在函数内声明一个变量才能得到一个局部变量,如下所示:
function fn(){
var a;
}
fn();
那么a就是局部变量了,可是函数fn却是局部变量啊,为了得到一个局部变量,定义了一个全局函数变量,得不偿失,因此,就想到声明一个匿名函数如下:
function(){
var a;
}
那么问题来了,匿名函数咋执行啊,没有名字怎么执行函数得到局部变量啊,因此就将函数的声明和执行统一在一起了,形成:
function(){
var a;
}();
但是这样一来,JS认为该语法是错误的,是不对的,经过实验发现,如果在该函数面前加一个操作符,如+、-等就可成功运行了,也可以用括号将其括起来,如下:
(function(){
var a;
}());
!function(){
var a;
}();
6.2 结论
但经过测试,最保险的方法有如下两种:
- 在前面加感叹号
- 用括号将其包起来,但是要保证前一个语句后有分号隔开。
这样的函数就是立即执行函数。