11、Function 对象
函数在程序中代表的就是一段具有功能性的代码,可以让我们的程序编程更加具有结构性和提升程序的复用性,也能让代码变得更加灵活强大
11.1、声明函数
// 函数的定义方式1 function 函数名 (参数){ 函数体; return 返回值; } 功能说明: 可以使用变量、常量或表达式作为函数调用的参数 函数由关键字function定义 函数名的定义规则与标识符一致,大小写是敏感的 返回值必须使用return // 函数的定义方式2 用 Function 类直接创建函数的语法如下: var 函数名 = new Function("参数1","参数n","function_body"); 虽然由于字符串的关系,第二种形式写起来有些困难,但有助于理解函数只不过是一种引用类型,它们的行为与用 Function 类明确创建的函数行为是相同的。
11.2、函数调用
//f(); --->OK function f(){ console.log("hello") } f() //----->OK
不同于python,js代码在运行时,会分为两大部分———预编译 和 执行阶段。
预编译:会先检测代码的语法错误,进行变量、函数的声明。
执行阶段:变量的赋值、函数的调用等,都属于执行阶段。
11.3、函数参数
(1) 参数基本使用
// 位置参数 function add(a,b){ console.log(a); console.log(b); } add(1,2) add(1,2,3) add(1) // 默认参数 function stu_info(name,gender="male"){ console.log("姓名:"+name+" 性别:"+gender) } stu_info("yuan")
(2)函数中的arguments对象
function add(a,b){ console.log(a+b);//3 console.log(arguments.length);//2 console.log(arguments);//[1,2] } add(1,2) // arguments的应用1 function add2(){ var result=0; for (var num in arguments){ result+=arguments[num] } console.log(result) } add2(1,2,3,4,5) // arguments的应用2 function f(a,b,c){ if (arguments.length!=3){ throw new Error("function f called with "+arguments.length+" arguments,but it just need 3 arguments") } else { alert("success!") } } f(1,2,3,4,5)
11.4、函数返回值
在函数体内,使用 return 语句可以设置函数的返回值。一旦执行 return 语句,将停止函数的运行,并运算和返回 return 后面的表达式的值。如果函数不包含 return 语句,则执行完函数体内每条语句后,返回 undefined 值。
function add(x,y) { return x+y } var ret = add(2,5); console.log(ret)
1、在函数体内可以包含多条 return 语句,但是仅能执行一条 return 语句
2、函数的参数没有限制,但是返回值只能是一个;如果要输出多个值,可以通过数组或对象进行设计。
11.5、匿名函数
匿名函数,即没有变量名的函数。在实际开发中使用的频率非常高!也是学好JS的重点。
// 匿名函数赋值变量 var foo = function () { console.log("这是一个匿名函数!") }; // 匿名函数的自执行 (function (x,y) { console.log(x+y); })(2,3) // 匿名函数作为一个高阶函数使用 function bar() { return function () { console.log("inner函数!") } } bar()()
使用匿名函数表达式时,函数的调用语句,必须放在函数声明语句之后!
11.6、函数作用域
作用域是JavaScript最重要的概念之一,想要学好JavaScript就需要理解JavaScript作用域和作用域链的工作原理。
任何程序设计语言都有作用域的概念,简单的说,作用域就是变量可访问范围,即作用域控制着变量与函数的可见性和生命周期。在JavaScript中,变量的作用域有全局作用域和局部作用域两种。
// 局部变量,是在函数内部声明,它的生命周期在当前函数被调用的时候, 当函数调用完毕以后,则内存中自动销毁当前变量 // 全局变量,是在函数外部声明,它的生命周期在当前文件中被声明以后就保存在内存中,直到当前文件执行完毕以后,才会被内存销毁掉
首先熟悉下var
var name = "yuan"; // 声明一个全局变量 name并赋值”yuan“ name = "张三"; // 对已经存在的变量name重新赋值 ”张三“ console.log(name); age = 18 // 之前不存在age变量,这里等同于var age = 19 即声明全局变量age并赋值为18 var gender = "male" var gender = "female" // 原内存释放与新内存开辟,指针指向新开辟的内存 console.log(gender)
作用域案例:
var num = 10; // 在函数外部声明的变量, 全局变量 function func(){ // num = 20; // 函数内部直接使用变量,则默认调用了全局的变量, //var num = 20; // 函数内部使用var 或者 let声明的变量则是局部变量 // 函数内部直接使用变量,则默认调用了全局的变量, // 使用变量的时候,解释器会在当前花括号范围值搜索是否有关键字var 或者 let 声明了变量,如果没有,则一层一层往外查找最近的声明 // 如果最终查找不到,则直接报错! 变量名 is not define! console.log("函数内部num:",num) } func(); console.log("全局num:",num);
11.7、JS的预编译
js运行三个阶段:
-
语法分析
-
预编译
-
解释执行
语法分析就是JS引擎去检查你的代码是否有语法错误,解释执行就是执行你的代码。最重要最需要理解的就是第二个环节预编译,简单理解就是在内存中开辟一些空间,存放一些变量与函数 。
预编译可分为全局预编译和局部预编译。
在js脚本加载之后,会先通篇检查是否存在低级错误;
在语法检测完之后,便进行全局预编译;
在全局预编译之后,就解释一行,执行一行;
当执行到函数调用那一行前一刻,会先进行函数预编译,再往下执行。
全局预编译的3个步骤:
-
创建GO对象(Global Object)全局对象,即window对象。
-
找变量声明,将变量名作为GO属性名,值为undefined
-
查找函数声明,作为GO属性,值赋予函数体
局部预编译的4个步骤:
-
创建AO对象(Activation Object)执行期上下文。
-
找形参和变量声明,将变量和形参名作为AO属性名,值为undefined
-
将实参值和形参统一。
-
在函数体里面找函数声明,值赋予函数体。
GO对象是全局预编译,所以它优先于AO对象所创建和执行
案例分析:
<script> var a = 10; console.log(a); function foo(a) { console.log(a); var a = 100; console.log(a); function a() {} console.log(a); var b = function(){}; console.log(b); function d() {} } var c = function (){ console.log("匿名函数C"); }; console.log(c); foo(20); </script>
全局预编译
GO/window = { a: undefined, c: undefined, foo: function(a) { console.log(a); var a = 123; console.log(a); function a() {} console.log(a); var b = function() {} console.log(b); function d() {} } }
解释执行代码(直到执行调用函数foo(20)语句)
GO/window = { a: 10, c: function (){ console.log("I at C function"); } test: function(a) { console.log(a); var a = 123; console.log(a); function a() {} console.log(a); var b = function() {} console.log(b); function d() {} } }
调用函数foo(20)前发生布局预编译
// 局部预编译前两步: AO = { a:undefined, b:undefined, } // 局部预编译第三步: AO = { a:20, b:undefined, } // 局部预编译第四步: AO = { a:function a() {}, b:undefined d:function d() {} }
预编译总结:
-
函数声明整体提升-(具体点说,无论函数调用和声明的位置是前是后,系统总会把函数声明移到调用前面)
-
变量 声明提升-(具体点说,无论变量调用和声明的位置是前是后,系统总会把声明移到调用前,注意仅仅只是声明,所以值是undefined)
面试题:
var num3 = 10; function func3(){ console.log(num3); var num3 = 20; } func3(); console.log(num3); /* // 全局编译 GO{ num3:undefined, func3: function (){ console.log(num3); var num3 = 20; } // 全局执行 var num3 = 10; GO{ num3:10, func3: function (){ console.log(num3); var num3 = 20; } // 局部编译 func3.AO{ num3:undefined, } // 局部执行 func3.AO{ num3:20, } // 全局执行 GO.num3 = 10 } */