一、为什么要使用函数?
- 提高代码复用
- 便于阅读和交流
二、函数介绍
说起 ECMAScript 中什么最有意思,那莫过于函数了。函数在ECMAScript中属于一等公民。本质上每个函数都是 Function 类型的实例,与其他引用类型一样都可以具有方法和属性.
1、基本介绍
函数是一个可以被其他代码或其自身调用的代码片段,或者是一个指该函数的变量variable 。 .当函数被调用时, 参数arguments 作为输入传递到函数,并且函数可以可选地返回输出。 在 JavaScript JavaScript中函数也是一个对象object.
函数名是作为函数声明或函数表达式的一部分声明的标识符 identifier。函数的作用域scope取决于函数名是一个声明还是表达式。
2、 如何定义函数?
1)声明式定义命名函数
命名函数是具有函数名称的函数:
function fun() {
console.log(this.username);
}
2)表达式: 定义匿名函数 。
匿名函数是一个没有函数名的函数
var foo = function (a, b) {
console.log(a, b);
}
3)构造函数模式
var foo = new Function();
三、函数内部属性
在函数内部,有两个特殊的对象: arguments 和 this。
1、arguments
arguments是一个类数组,包含着传入函数中的所有参数。虽然 arguments 的主要用途是保存函数参数,但这个对象还有一个名叫 callee 的属性,该属性是一个指针,指向拥有这个 arguments 对象的函数。
callee实例:
//求一个数的阶乘
function factorial(num){
if (num <=1) {
return 1;
} else {
return num * factorial(num-1)
}
}
问题:这个函数的执行与函数名 factorial 紧紧耦合在了一起。
为了消除这种紧密耦合的现象,可以像下面这样使用 arguments.callee、
function factorial(num){
if (num <=1) {
return 1;
} else {
return num * arguments.callee(num-1)
}
}
2、this
1)理解this
1. 一个关键字, 一个内置的引用变量
2. 在函数中都可以直接使用this
3. this代表调用函数的当前对象
4. 在定义函数时, this还没有确定,只有在执行时才动态确定(绑定)的
2) 如何确定this的值
1. test()
2. obj.test()
3. new test()
4. test.call(obj) test.apply(obj)
3)代码实例:
<script type="text/javascript">
// 构造函数
/*
new做了什么?
1. 返回一个新的对象
2. 相对于给构造函数最后一行加了 return this
*/
function People() {
this.age = 20;
this.name = "张三";
console.log(this)
}
var p = new People();
console.log(p);
//普通调用
// this ---> window
Person();
// this普通调用(全局)指向窗口window
console.log(this);
</script>
四、函数的属性和方法
1、属性
ECMAScript 中的函数是对象,因此函数也有属性和方法。每个函数都包含两个
属性: length 和 prototype 。其中, length 属性表示函数希望接收的命名参数的个数。至于prototype则代表函数的显示原型!
2、方法
每个函数都包含两个非继承而来的方法: apply() 和 call() 。这两个方法的用途都是在特定的作用域中调用函数,实际上等于设置函数体内 this 对象的值。
1)apply
语法:
func.apply(thisArg, [argsArray])
参数:
thisArg
在fun函数运行时指定的this值。
argsArray
可选的。
一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 func 函数。
返回值:
调用的方法的返回值,若该方法没有返回值,则返回undefined
2)call
语法:
fun.call(thisArg, arg1, arg2, ...)
参数:
thisArg
在fun函数运行时指定的this值。
arg1, arg2, ...
指定的参数列表
返回值:
调用的方法的返回值,若该方法没有返回值,则返回undefined
代码实例:
<script type="text/javascript">
function fun(a,b) {
console.log("a = "+a);
console.log("b = "+b);
//alert(this);
}
var obj = {
name: "obj",
sayName:function(){
alert(this.name);
}
};
/*
* call()和apply()
* - 这两个方法都是函数对象的方法,需要通过函数对象来调用
* - 当对函数调用call()和apply()都会调用函数执行
* - 在调用call()和apply()可以将一个对象指定为第一个参数
* 此时这个对象将会成为函数执行时的this
* - call()方法可以将实参在对象之后依次传递
* - apply()方法需要将实参封装到一个数组中统一传递
*
* - this的情况:
* 1.以函数形式调用时,this永远都是window
* 2.以方法的形式调用时,this是调用方法的对象
* 3.以构造函数的形式调用时,this是新创建的那个对象
* 4.使用call和apply调用时,this是指定的那个对象
*/
//fun.call(obj,2,3);
fun.apply(obj,[2,3]);
var obj2 = {
name: "obj2"
};
/*fun.apply();
fun.call();
fun();*/
//fun.call(obj);
//fun.apply(obj);
//fun();
//obj.sayName.apply(obj2);
</script>
五、new关键字解析
new 运算符创建一个用户自定义的类型实例或内置类型的实例。
1、语法:
new constructor[([arguments])]
参数:
constructor:
一个指定对象实例的类或函数。
arguments:
一个用来被constructor 调用的参数列表
2、构造函数实例化
var aar= new Object(); 构造函数实例化
实例化构造函数new的操作:
- 返回一个新的对象
- 将构造函数中的this指向实例化对象。
- 在构造函数的最后一行会 return this;
3、创建一个用户自定义的对象需要两步:
- 通过编写函数来定义对象类型。
- 通过new来创建对象实例。
4、当执行一个new关键字时都会发生些什么?
当代码 new Foo(…) 执行时,会发生以下事情:
- 一个继承自 Foo.prototype 的新对象被创建。
- 使用指定的参数调用构造函数 Foo ,并将 this 绑定到新创建的对象。new Foo 等同于 new Foo(),也就是没有指定参数列表,Foo 不带任何参数调用的情况。
- 由构造函数返回的对象就是 new 表达式的结果。如果构造函数没有显式返回一个对象,则使用步骤1创建的对象。(一般情况下,构造函数不返回值,但是用户可以选择主动返回对象,来覆盖正常的对象创建步骤)
即判断构造函数返回值的类型:
1)如果是值类型,就丢弃它,还是返回instance。
2)如果是引用类型,就返回这个引用类型的对象,替换掉instance。
3)如果没有写return,相当于return undefined
六、函数调用总结:
1、普通调用:() call() appiy()
2、 构造调用:new fn()
3、立即调用函数表达式(IIFE):(function(){})()
立即调用函数表达式(IIFE)是一个函数, 其在函数被加载到浏览器的编译器之后直接调用的。. 识别IIFE的方法是通过在函数声明的末尾定位额外的左和右括号。
(function foo1() {
console.log("立即调用函数表达式");
}());
//道格拉斯·克罗克福德的风格
(function foo2() {
console.log("立即调用函数表达式");
})();
4、回调: 不需要开发者调用
七、函数与数组的结合使用:
1、介绍
Arguments(函数内部属性):不是数组;可实现函数不传参数获取数值。
arguments.callee():获取当前函数的引用。
2、代码实例:
<script type="text/javascript">
/*
* 在调用函数时,浏览器每次都会传递进两个隐含的参数:
* 1.函数的上下文对象 this
* 2.封装实参的对象 arguments
* - arguments是一个类数组对象,它也可以通过索引来操作数据,也可以获取长度
* - 在调用函数时,我们所传递的实参都会在arguments中保存
* - arguments.length可以用来获取实参的长度
* - 我们即使不定义形参,也可以通过arguments来使用实参,
* 只不过比较麻烦
* arguments[0] 表示第一个实参
* arguments[1] 表示第二个实参 。。。
* - 它里边有一个属性叫做callee,
* 这个属性对应一个函数对象,就是当前正在指向的函数的对象
*
*/
function fun(a,b){
console.log(arguments instanceof Array);
console.log(Array.isArray(arguments));
console.log(arguments[1]);
console.log(arguments.length);
console.log(arguments.callee == fun);
console.log(arguments);
}
fun("hello",true);
</script>
八、使用细节和注意事项:
1、函数没有重载
实参列表可以不用和形参列表进行一对一的匹配,同名函数之间就是后面的覆盖前面的 ,函数没有重载没有重载。
<script type="text/javascript">
function fn(){
console.log('a');
}
//形式参数
function fn(a){
console.log(a);
}
//实参列表可以不用和形参列表进行一对一的匹配
//同名函数之间就是后面的覆盖前面的 没有重载
fn(1,2,3,4,5,6);
</script>