函数
一、定义方式
//方式1:函数声明
function func () {}
//方式2:函数表达式
var func = function () {}
//方式3
var func=new Function()//es6
var sum = new Function(‘a‘, ‘b‘, ‘return a + b‘);
console.log( sum(1, 2) ); // 3
二、函数调用
2.1.调用方式
- 普通函数
// 带参数的函数声明
function 函数名(形参1, 形参2, 形参...){
// 函数体
}
// 带参数的函数调用
函数名(实参1, 实参2, 实参3);
- 构造函数
- 对象方法
2.2.this 指向
函数的调用方式决定了 this
指向的不同:
调用方式 | 指向(非严格模式) | 备注 |
---|---|---|
普通函数调用 | window | 严格模式下是 undefined |
构造函数调用 | 实例对象 | 原型方法中 this 也是实例对象 |
对象方法调用 | 该方法所属对象 | |
事件绑定方法 | 绑定事件对象 | |
定时器函数 | window |
2.3.call、apply、bind
var name=‘HJJ‘;
var obj = {
name:"HHH",
age:18,
myFun:function(){
console.log(this)
console.log(this.name+‘年龄‘+this.age);
}
}
obj.myFun(); //HHH年龄18 ===>对象方法调用,this是该对象obj
var result=obj.myFun; //==>result=myFun,普通函数,this是window
result();//HJJ年龄undefined
result.call(obj);//HHH年龄18 ===>call改变this指向,this是obj....即result添加到obj环境中
result.apply(obj);//HHH年龄18 ===>apply改变this指向,this是obj
result.bind(obj)();//HHH年龄18 ===>bind改变this指向,this是obj
call()、apply()、bind() 方法的共同点和区别:
作用:都是用来改变函数的this对象的指向的。
语法:
call(thisObj,value1,value2...)
apply(thisObj,[value1,value2...])
bind(thisObj,value1,value2...)
参数:
- 第一个参数都是this要指向的对象。(添加到哪个环境中)
- apply第二个参数是个数组。call和bind都是以逗号分隔。
返回值:
- bind是返回函数,需要调用。apply、call是立即调用 。
var person1 = {
name:"HHH",
age:18,
myFun:function(height,job){
this.height=height
console.log(this)
console.log(this.name+‘年龄‘+this.age+‘身高‘+height+‘工作‘+job);
}
}
var person2 = {
name:"LLL",
age:99
}
person1.myFun.call(person2,168,‘actor‘)//LLL年龄99身高168工作actor。。。即person1.myFun添加到person2环境中
person1.myFun.apply(person2,[168,‘actor‘])//LLL年龄99身高168工作actor
person1.myFun.bind(person2,[168,‘actor‘])()//LLL年龄99身高168工作actor
三、函数成员
-
arguments对象:存储了传递的所有的实参。不是一个数组,arguments是一个伪数组,可以进行遍历。
-
arguments[index]:实参集合
-
length:实参的个数
-
callee:引用函数本身
-
-
caller:函数的调用者
-
length:形参的个数
-
name:函数的名称
function func1(x, y) {
console.log(func1.length) // 2 ==>形参的个数
console.log(func1.name) //func1 ==> 函数的名字
console.log(func1.caller) //函数的调用者
console.log(arguments) // [10, 20, 30, callee: ?, Symbol(Symbol.iterator): ?]
//不需要明确指出参数名,就能访问每个实参。
console.log(arguments[0]);//10
console.log(arguments[1]);//20
console.log(arguments[2]);//30
console.log(arguments.length);//2==>实参的个数
console.log(arguments.callee)//函数本身
console.log(arguments.callee === func1) //true==>函数本身
console.log(arguments instanceof Array) //false==>w
}
function func2() {
func1(10, 20, 30)
console.log(func2.caller) //null
}
func2()
- arguments.callee,实现匿名递归函数
var sum = function (n) {
if (1 == n) {
return 1;
} else {
return n + arguments.callee(n - 1);
}
}
console.log(sum(6));//21
四、函数闭包
4.1.匿名函数
- 匿名函数自调用:匿名函数不能通过直接调用来执行,因此可以通过匿名函数的自调用的方式来执行
(function (str) {
console.log(123+str);
})(‘hello‘);
//123hello
var func = (function () {
var init = function () {
...
};
return {
init: init
}
}());
func.init();
- 作用:(1) 防止全局变量污染。(2) 通过匿名函数实现闭包。
4.2.概念
- 有权访问另一个函数作用域内变量的函数都是闭包
- 简单理解:定义在一个函数内部的函数。
4.3.特点
1、外部访问函数内部变量
function func1(){
var n=999;
function func2(){
console.log(n)
}
return func2;
}
var result=func1();//===>转换: result=func2
result(); // 999 ===>转换:func2()
/**
func2可以访问func1内部的所有局部变量。
想要外部读取func1的内部变量,可把func2作为返回值。
**/
2、局部变量会常驻在内存中
function func1(){
var n=1;
var b=2;
function func2(){
n++
console.log(n,b)
}
function func3(){
b+=2;
}
}
func3();
return func2,
}
var result=func1();//===>转换: result=func2
result(); // 2,4 ===>转换:func2()
result(); // 3,4 ===>转换:func2()
3、可以避免使用全局变量,防止全局变量污染
4、缺点:会造成内存泄漏(有一块内存空间被长期占用,而不被释放)
- 解决方法在退出函数之前,将不使用的局部变量全部删除。
- 删除闭包,result= null
var result=(function(){
var n=1;
function func2(){
n++
console.log(n)
}
function clearVariable(){
n=null
}
return {
func2:func2,
clearVariable:clearVariable
}
})()
result.func2(); // 2
result.func2(); // 3
result.clearVariable();//清除变量
result.func2(); // 1
五、函数递归
//阶乘的递归
function factorial (num) {
if (num <= 1) {
return 1
} else {
//return num * arguments.callee(num - 1)
return num * factorial(num - 1)
}
}
factorial(5)//120
//注:必须要有结束递归的条件
应用场景:深拷贝、菜单树、遍历 DOM 树
六、重载和重写
重载:相同的函数名,但是函数的参数个数或类型不同。调用的时候根据函数的参数来区别不同的函数。
覆盖(也叫重写):函数名和参数都一样,只是函数的实现体不一样。一般用于子类覆盖父类的方法。
- js同一个作用域,出现两个名字一样的函数,后面的会覆盖前面的。js只有重写(覆盖),没有重载
//重载
function add(n1,n2){
console.log(n1+n2);
}
function add(n1,n2,n3){
console.log(n1+n2+n3);
}
add(1,2);//NaN
add(1,2,3);//6
//重写
function text(){
console.log(‘text1‘);
}
function text(){
console.log(‘text2‘);
}
text();//"text2"
- 可以根据arguments个数实现重载
function fn(){
switch(arguments.length){
case 0:
addFn(arguments.length)
break
case 1:
deleteFn(arguments.length)
}
}
function addFn(n){
console.log(n++)
}
function deleteFn(n){
console.log(n--)
}
fn() // 1
fn(1) // 0