JS函数基础

函数的定义

函数其实就是一个封装一段代码段的对象,那函数名其实仅是用来引用函数对象的一个普通变量

写代码的时候我们避免不了要重复用一些代码,一直重复写很耗时,而且不美观也不利于维护,因此函数的出现就是来让代码重用,便于维护。

一段代码,可能被反复使用,可以定义为函数,然后调用函数来使用这段代码。

在JavaScript中函数就是对象。函数不同于其他对象的决定性特点是,函数存在一个被称为[[Call]]的内部属性。内部属性无法通过代码访问而是

定义了代码执行时的行为。ECMAScript为JavaScript的对象定义了多种内部属性,这些内部属性都用双重中括号来标注。

[[Call]]属性是函数独有的,表明该对象可以被执行。由于仅函数拥有该属性,ECMAScript定义了typeof操作符对任何具有[[Call]]属性的对

象返回 [object Function]


函数的创建

function 声明

1
2
3
4
function 函数名(参数列表) {
函数体;
return 返回值;
}

直接量声明

1
2
3
4
var 函数名 = function(参数列表) {
函数体;
return 返回值;
};

用 new 创建

因为 Function 是内置类型,本身有一个 Function 的构造函数,是内置类型,所以是可以 new

1
var 函数名 = new Function("参数名1","参数名2",...,"函数体; return 返回值")

这里注意,参数和函数体都要用引号引起来,但是一般函数的创建都不会这样创建,以前面的两种为主,那其实前面两种的创建是存在一定的差别的,下面会提到

函数的参数

函数执行时必须的数据变量,它分为显示参数(Parameters)与隐式参数(Arguments)

显示参数

1
2
3
function fun(name, id) {
//函数体
}

这种直接传进来的 name 和 id 就是显示参数,也就是你能看到的。其实函数传递的参数就相当于在函数体内又声明了一个局部变量

1
2
3
4
5
6
var i = 10;
function fun(i) {
i++;
console.log(i);
}
fun(i);

上面的代码,就相当于下面的代码

1
2
3
4
5
6
7
var i = 10;
function fun() {
var i = 10; //这个值就是传进来的参数的值
i++;
console.log(i);
}
fun(i); //在此处传入i

隐式参数

每个 JavaScript 函数内部都有一个对象 arguments 对象,其实是一个类数组的对象,它会自动接受所有传入函数的参数值。

1
2
3
function func(          ) {
//arguments[ ]
}

值得一说的是,arguments 有下标有长度,可以通过下标来获得传入的参数,比如 arguments[0] 就是第一个参数, length 就可以遍历这个类数组对象,但是,毕竟它不是数组,所以不能进行一些数组特有的操作,比如 sort

函数重载

强类型语言对重载的定义:函数名相同,参数不同,或者是参数类型不同都可以叫做函数的重载。

但在js中因为 arguments 的存在,JavaScript的函数根本就不存在所谓的签名,所以重载在JavaScript中实际是不存在的。

1
2
3
4
5
6
7
8
9
10
11
12
// 模拟函数重载
function abc(){
if (arguments.length ===1){
//A
}
if(arguments.length ===2){
//B
}
}

abc(11);
abc(11,22);

声明提前

在开始执行程序前,js 引擎会首先查找 var 声明的变量和 function 声明的函数,将其提前到当前作用域的顶部集中创建,而将赋值操作保留在原地,这里特别说一下,未用 var 声明的变量不会声明提前.

1
2
console.log(a); //a is not defined
a = 10;
1
2
3
console.log(a); //undefined
var a = 10;
console.log(a); //10

但是在它下面声明并赋值变量 a,因为声明提前,其实代码会变成下面这个样子

1
2
3
4
var a;
console.log(a);
a = 10;
console.log(a);

这样看,一切都变得很合理。。

函数也是一样

1
2
3
4
5
6
7
8
function fun() {
console.log(1);
}
fun(); //2
function fun() {
console.log(2);
}
fun(); //2

控制台会输出两个 2,因为 function 声明的函数也会声明提前,代码其实是下面这个样子

1
2
3
4
5
6
7
8
function fun() {
console.log(1);
}
function fun() {
console.log(2);
}
fun(); //2
fun(); //2

第二次声明因为方法名字一样,后者覆盖了前者,所以再调用的时候就会调用最后这个

但是声明提前会增加程序解读的难度,因此我们在写程序时,尽量避免声明提前所带来的危害

声明提前的解决方法

那我们既然知道会有声明提前这种操作,就在变量和函数的声明时都放在当前作用域的顶部。

在 ES6 中 可以用 let 代替 var,不过要求在当前作用域中 let 变量之前不允许出现声明的变量

也可以用直接量声明变量的方法

1
2
3
4
5
6
7
8
var fun = function() {
console.log(1);
};
fun(); //1
var fun = function() {
console.log(2);
};
fun(); //2

这种当然也会声明提前,那我们看一下声明提前后的代码

1
2
3
4
5
6
7
8
9
10
var fun;
var fun;
fun = function() {
console.log(1);
};
fun(); //1
fun = function() {
console.log(2);
};
fun(); //2

声明提前但是赋值还是留在原地,所以虽然有声明提前,但是并不会改变我们原本想要的结果。也就解决了声明提前带来的危害。

匿名函数

函数创建时没有指定函数名

匿名函数使用后自动释放,会节约内存,它会划分临时作用域,避免全局变量污染全局。

用处

callback

将一个函数作为参数传入另一个函数内,被其它函数调用

举个栗子🌰

1
2
3
arr.sort(function(a, b) {
return a - b;
});
1
str.replace(/reg/g, function(kw,$1,$2,...){return 替换值})

自调

定义函数后自己调用自己,调用结束后,立刻释放,不占内存

举个例子🌰

1
2
3
4
(function(参数列表) {
函数体;
return 返回值;
})(参数值列表);

会定义一个临时的作用域,减少使用全局变量,避免全局污染。

重载

相同函数名,不同参数列表的多个函数。在调用时,根据传入参数的不同,自动选择匹配的函数执行。

这样可以减少 api 的数量,减轻调用者的负担。

听起来很诱人,但是 js 语法不支持重载,原因是 js 不允许多个同名函数同时存在,后声明的函数会覆盖前面声明的。(哇。js 不支持你在这里说什么,神经病啊).

既然说重载,那肯定是可以通过某些方法实现的。这个方法就是利用 arguments

1
2
3
4
5
6
7
8
9
function test() {
if (arguments.length == 0) {
//不传值的操作
} else if (arguments.length == 1) {
//传一个值进行的操作
} else {
//....等等
}
}

其实也不是真正意义上的重载,因为并没有创建同名的多个函数,但是实现的效果是和重载差不多的。

常规函数

  • 命名函数
  • 匿名函数
  • 对象方法
  • 对象方法简写(ES 6)
  • IIFE(立即执行函数)

箭头函数

  • 命名箭头函数
  • 匿名箭头函数
  • 对象方法
  • IIFE 箭头函数
本文结束感谢您的阅读

本文标题:JS函数基础

文章作者:陈宇(cosyer)

发布时间:2019年03月27日 - 00:03

最后更新:2020年03月29日 - 00:03

原始链接:http://mydearest.cn/2019/JS%E5%87%BD%E6%95%B0%E5%9F%BA%E7%A1%80.html

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

坚持原创技术分享,您的支持将鼓励我继续创作!