js函数
1.函数简介
-Function类型,即函数的类型
-一个典型的JavaScript函数定义如下:
function 函数名称(参数表) {
函数执行部分;
}
注意函数的参数表直接写形参名,不用写var类型。
-return语句,return返回函数的返回值并结束函数运行
-函数也可以看做数据来进行传递
<script type="text/javascript" charset="UTF-8"> function test(a, b) { return a + b; } alert(test(10, 20)); // 函数也是一种数据类型 alert(typeOf test); // 返回值为function // 函数可以传递 function test1(func) { func(); } function test2() { alert('aaa'); } test1(test2); // 函数可以嵌套 function test4() { function test5() { alert('inner'); } //只能在函数内部调用,外部不可调用 test5(); } test4(); </script>
2.函数的三种定义方式
-三种定义方式
- function语句式
-函数直接量式
-通过Function构造函数形式定义函数
-比较三种方式定义的区别
function语句 function构造函数 函数直接量
兼容 完全 js1.1以上 js1.2以上
形式 句子 表达式 表达式
名称 有名 匿名 匿名
性质 静态 静态 静态
解析时机 优先解析 顺序解析 顺序解析
作用域 具有函数作用域 顶级函数(顶级作用域) 具有函数作用域
<script type="text/javascript" charset="utf-8" defer="defer" src="../commons/001.js"> // 3种方式定义函数 // 1 function语句函数式 function test1() { // 只会被编译一次,放到内存里,静态,效率高 alert('我是test1'); } // 2.函数的直接量式 ECMAScript推荐使用 // 只会被编译一次,放到内存里,静态,效率高 var test2 = function() { alert('我是test2'); }; // 3.function 构造函数式 // 每次都会被编译,效率低,但不占用内存 var test3 = new Function("a", "b", "return a+ b;"); alert(test3(10,20)); function test1() { alert('111'); } test1(); var test2 = function() { alert('222'); }; test2(); // 执行结果为111 -》222 没问题 // 但是如果将test1和test2分别放到函数体之前执行呢, test3(); function test3() { alert('333'); } test3(); alert(test4); //返回undifind,表示变量声明了,但是没有赋值 test4(); var test4 = function() { alert('444'); }; // 执行结果为333 ,为什么444没有被弹出,因为只有function语句函数式才是优先解析,剩下是顺序解析 // 作用域问题 var k = 1; function t1() { var k = 2; function test() {return k;} // 2 var test = function(){return k;};// 2 var test = new Function("return k;"); // 1 原因是具有顶级作用域,相当于在外面(全局)new 了一个函数,所以结果为1 alert(test()); } t1(); </script>
3.函数的参数(arguments对象)
-arguments是表示函数的实际参数(与形参无关)
-callee函数(回调函数属性)
-arguments对象的秘密属性 callee属性。这个属性比较奇怪,它能够返回arguments对象所属的函数的引用、这相当于
在自己的内部调用自己。用法:检测函数传递的参数正确与否。
<script type="text/javascript" charset="utf-8"> // js中 函数的参数分为:形参和实参 function test(a, b, c, d) { // 形参4个 实参2个,在js中 形参个数和实参个数没有关系 // 如何求形参的个数 test.length; // 函数的实际参数,内部就是用一个数组去接受的 // arguments对象 可以访问函数的实际参数 // arguments对象 只能在函数的内部访问和使用 alert(arguments.length); alert(arguments[0]); alert(arguments[1]); if (test.length === arguments.length) { return a + b; } else { return '参数不正确'; } } alert(test(10, 20)); // arguments对象 用的最多的地方还是做递归操作 // callee方法 指向的是函数本身 arguments.callee.length;// 等价于test.length function fact(num) { if (num <= 1) { return 1; } else { // 这里建议使用arguments.callee,因为可以存在这样的代码,fact = null,这样会出现问题 return num * arguments.callee(num - 1); } } alert(fact(5)); </script>
4.this对象
-this对象是在运行时基于函数的执行环境绑定的。在全局函数中,this等于window,而当函数被作为某个对象
的方法调用时,this等于那个对象。
-也就是说this关键字总是指代调用者。
<script type="text/javascript" charset="utf-8"> // this : this对象是指在运行时期,基于执行环境所绑定的。 // 总是指向调用者 var k = 10; function test() { this.k = 20; } alert(test.k); // 返回undefind,因为函数只是编译了,并没有执行 test(); // 对于test来讲,执行环境是全局作用域 等价于window.test(); alert(test.k);// 还是返回undefined,因为this指的是window alert(window.k); // 20 等价于alert(k); </script>
5.call和apply方法
- 每一个函数都包含两个非继承而来的方法:call和apply。
这两个方法的用途都是在特定的作用域中调用函数,实际上等于设置函数体内this对象的值。
-call,apply的用途之一就是传递参数,但事实上,他们真正强大的地方是能够扩充函数赖以运行的作用域。
-使用call()和apply()来扩充作用域的最大好处就是对象不需要与方法有任何耦合关系。
-call方法简单的实现。
<script type="text/javascript" charset="utf-8"> // call和apply 简单的用法,绑定一些函数,用于传递参数 调用 function sum(x, y) { return x + y; } function call1(num1, num2) { // 将sum绑定到this。也就是call1上。这样就可以使用sum方法了。 return sum.call(this, num1, num2); } function apply1() { // apply1和call类似,只不过后面传递的参数 是一个数组。 return sum.apply(this, [num1, num2]); } // 主要用途 扩充作用域 window.color = 'red'; var obj = {color :'blue'}; function showColor(){ alert(this.color); } showColor.call(this); // 结果为red showColor.call(obj); // 结果为blue 函数showColor的作用域从 全局变为了obj // call方法的简单模拟和实现 function test1(a, b) { return a + b; } // 名字大写,默认为一个对象 ->自定义的对象 function Obj(x, y) { return x * y; } var obj1 = new Obj(10, 20); alert(test1.call(obj1, 10, 20)); // 相当于 自己定义了一个临时的方法,用来接收test1 obj.method = test1(); // 使用完之后删掉 delete obj1.method; </script>
6.执行环境和作用域链概念
-执行环境(execution context)是js中最为重要的一个概念。执行环境定义了变量或函数有权访问的其他数据,决定了他们各自的行为。
每一个执行环境都有一个与之关联的变量对象,环境中定义的所有变量和函数都保存在这个对象中。虽然我们的代码无法访问这个对象,
但是解析器在处理数据的时候会在后台执行它。
-全局执行环境是最外围的一个执行环境。根据ECMAScript实现所在的宿主环境不同,表示执行环境的对象也不一样。
-每一个函数都有自己的执行环境。当执行流进一个函数时,函数的环境就会被推入一个函数栈中、而在函数执行之后,栈将其弹出,
把控制权返还给之间的执行环境。当代码在一个环境中执行时,会创建变量对象的一个作用域链(Scope chain)。作用域链的用途,是保证
对执行环境有权访问的所有变量和函数的有序访问。
<script type="text/javascript" charset="utf-8"> // 1.执行环境,window对象(最上层的执行环境) var color1 = 'blue'; // 每个函数都有一个执行环境,(variable obj) function changeColor() { var color2 = 'red'; // color1 是一级作用域 color2是二级作用域,color3是三级作用域 function swapColor() { // 这个函数也产生了一个执行环境 (variable obj) var color3 = color2; color2 = color1; color1 = color3; // 这里可以访问color1,color2,color3 } // 这里可以访问color1,color2,但是不能访问color3 swapColor(); } // 这里只能访问color1 changgeColor(); //第一个作用环境 window // 环境变量 可以一层一层的向上进行追溯,可以访问它的上层环境 (变量和函数),基于这种机制,全局变量尽量少用。节约效率。 </script>
7.垃圾收集和块级作用域
-js是一门具有自动垃圾收集机制的编程语言。开发人员不必关心内存分配和回收问题。
-离开作用域的值被自动标记为可以回收,因此将在垃圾收集期间被删除。标记清楚是目前主流的垃圾收集算法。这种算法的思想
是想给当前不使用的值加上标记,然后回收其内存。
-js里面没有块级作用域的概念,和C,java等高级语言不同。所以在使用if和for的时候要注意。
-js模拟块级作用域。
<script type="text/javascript" charset="utf-8"> // 垃圾收集 方法1 标记法 function test() { var a = 10; //被使用 var b = 20; // 被使用 } test(); //执行完毕之后,a,b又被标记了一次 , 标记成了没有被使用状态。 // 方法2 引用计数法 function test1() { var a = 10; // aCount = 1 var b = 20; // bCount = 1 var c; c = a; //aCount = 2 a = 20; //aCount = 1; // 当aCount = 0的时候,就会被回收。 } // 块级作用域的概念 function test2 () { for (var i = 1; i <= 5; i++) { alert(i); } alert(i); //在java中,肯定会报错,但是在js中 返回值为6.所以在使用时 需要注意这一点。原因是js没有块级作用域这一说法。 // js默认当函数 执行完之后 这个变量才没有。 } // 模拟块级作用域 function test3() { // 写一个匿名函数,在加上小括号,表示让函数立即执行。 (function () { for (var i = 1; i <= 5; i++) { alert(i); } })(); } // 函数直接执行 参考下面的代码 (function() {alert('i am comming');})(); </script>
8.闭包(Closure)
-闭包与函数之间有着紧密的关系,它是函数的代码在执行过程中的一个动态环境,是一个运行期的,动态的概念。
-所谓闭包,是指词法表示包括不必计算的变量的函数。也就是说,该函数能够使用函数外显示定义的变量。
-在程序语言中,所谓闭包。是指语法域位于某个特定的区域。具有持续参照(读写)位于该区域内自身范围之外的执行域上的非
持久型变量值能力的段落。这些外部执行域的非持久性变量神奇保留他们在闭包最初定义(或创建)时的值。
<script type="text/javascript" charset="utf-8"> var name = 'xiao A'; var obj = { name : 'xiao B', getName : function() { return function(){ return this.name; }; } }; alert(obj.getName()()); // 结果为xiao A var obj1 = { name : 'xiao B', getName : function() { return function(){ var o = this; return o.name; }; } }; alert(obj1.getName()()); // 结果为xiao B // 闭包:一个函数可以访问另外一个函数作用域中的变量。 // 封闭性,类似于java中的private 起一个保护变量的作用。 // 1级作用域 function f(x) { // 2级作用域 var temp = x; // 3级作用域 return function(x){ temp += x; alert(temp); }; } var a = f(50); // 匿名的函数结构体 a(5); // 55 a(10); // 65 a(20); // 85 // 直到temo找不到了之后,才回收。 </script>