本文是前端学习笔记中的第二篇,对应的是渡一教育的web前端开发JavaScript精英课js中的第七课时
JS中的函数给我的感觉还是蛮新奇的,以前只学过C和Java,都是不可以函数嵌套的强类型语言,(尽管JDK10以后,Java引入了局部类型推断(Local-Variable Type Inference),即我们平时说的var),弱类型语言的一些特性,我还是需要些时间慢慢去适应
目录
一. 弱类型语言无法输出地址
弱类型语言的方法虽然可以输出,但是得到的并不是期望的地址值,而是整个函数体,尽管JS也有堆和栈的概念,但是地址不会被输出
<script>
function testMethod() {
alert("我真帅");
}
testMethod();
console.log(testMethod); // 控制台中输出 内容同下
document.write(testMethod); // function testMethod() { alert("我真帅"); }
</script>
输出到浏览器屏幕上的是 function testMethod() { alert("我真帅"); },而在控制台中输出则更规范,保留了代码格式,一般都在控制台中输出
二. 3种函数定义方式
3种定义方式,可以总的被归纳为两种,如下所示
-
函数声明
-
函数表达式
其中,函数表达式还可以再进一步细分为命名函数表达式和匿名函数表达式
(1)函数声明
<script>
function test() {
console.log("函数声明");
}
// test();
</script>
如上所示,一个函数便被声明了,但是不会执行,因为此时没有JS代码调用它,此时只需要把注释放开,便可在控制台看到输出
(2.1)命名函数表达式
<script>
var named = function abc () {
console.log("命名函数表达式");
}
named();
</script>
命名函数表达式的命名二字,表现在function后面的“abc”那,但是实际上若想调用这个函数还是需要named();这样调用,后面的“abc”基本上是没有任何意义的,。因此实际开发一般都是使用匿名函数表达式
(2.2)匿名函数表达式
<script>
var unnamed = function () {
console.log("匿名函数表达式");
}
unnamed();
</script>
匿名函数表达式结构更加清晰,删除了无用的命名
那么,尽管命名函数表达式没用,那么它们的区别在哪里呢?
答案其实就是命名那
<script>
// (1)函数声明
function test() {
console.log("函数声明");
}
// (2)函数表达式
// (2).1 命名函数表达式
var named = function abc () {
console.log("命名函数表达式");
}
// (2).2 匿名函数表达式
var unnamed = function () {
console.log("匿名函数表达式");
}
// (2).1和(2).2的区别在于各自的name属性 2.1为abc 2.2为unnamed 此外,以函数声明方式定义函数同样有name 即它本身
console.log("test.name = " + test.name); // test
console.log("named.name = " + named.name); // abc 唯一一点不同之处 开发一般都用匿名
console.log("unnamed.name = " + unnamed.name); // unnamed
</Script>
如上代码
可以看到,无论是函数声明还是匿名函数表达式,方法的name属性值都是各自的方法名,唯独命名函数表达式是function后面跟着的变量名“abc”,区别仅在于此
三. 形参和实参列表
JavaScript中的形参因为弱类型语言的缘故,不再需要定义变量数据类型,直接写名字即可,且不限制传递参数个数(类似于Java的JDK5引入的可变参数,不过功能更加强大),但这样的同时也会引发一些问题,比如传递的参数个数和定义的形参个数不同,此时需要以arguments对象取出实参。 具体看下面的代码
<script>
function js (a,b) {
// 形参相当于 var a,b;
if (js.length > arguments.length) {
console.log("形参多了");
} else if (js.length < arguments.length) {
console.log("实参多了");
} else {
console.log("相等");
}
for (var i = 0;i<arguments.length;i++){
alert(arguments[i]);
}
js(1,2,3,undefined,null,NaN);
</script>
形参定义了两个,但实际传递的参数有5个,此时如果想使用其余被传递的参数,则需要使用arguments对象获取,注意,arguments是对象而不是数组,arguments[0]只是其一个属性,而这个属性是传递的实参的副本
除此之外,形参还和arguments对象的属性存在映射关系,若修改了形参值会通过映射改变arguments对象属性的值(但二者实际上并非指向同一块内存),即形参值,但是也存在一定的约束关系,形参值只能修改和实参值共有的部分,如下所示
<script>
function testArgs(a,b,c,d) {
// 相当于 var a,b,c,d;
a = 11;
b = 22;
c = 33;
d = 44;
console.log("arguments[0] = " + arguments[0]); // 11
console.log("arguments[1] = " + arguments[1]); // 22
console.log("arguments[2] = " + arguments[2]); // 33
console.log("arguments[3] = " + arguments[3]); // undefined
//由此可见 这种映射关系只存在于实参和形参的共有部分
}
testArgs(1,2,3);
</script>
实际被传递的参数只有3个,但此时形参定义了4个,那么arguments数组的属性也只有arguments[0],arguments[1],arguments[2],此时查看arguments[3]自然是undefined