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>

浙公网安备 33010602011771号