JavaScript函数
一、函数的定义
JS中函数的特点:
- 一处定义,处处使用
- 将函数定义在对象内部称为对象的方法
- 函数也是一个对象,可以设置属性与方法
- 每一次调用函数会产生一个this,谁调用的函数this就指向谁
- 在函数中创建的变量为局部变量,在函数调用时声明,在函数调用结束后删除,且只能在函数内部使用,因此可以在不同函数中定义相同名称的变量
- 可以多次调用一个函数为其传递不同参数获得不同结果
- 函数可以用在表达式、计算式等其他地方,都可以得到相应的结果
1、函数声明语句
function name (参数){要执行的代码}
function fun1(){}//用函数声明语句定义一个函数
- 函数不会执行,只有当调用时才会执行
- 可以不带参数也可以没有返回值,甚至可以没有函数名
function fun1(){
console.log('a');//未调用,不执行
}
function fun2(){
console.log('b');
return 'c';
}
console.log(fun2());//b c
//箭头函数或匿名函数可以不用写函数名
(()=>{
console.log('d');
})()//d
2、函数表达式
var fun = function(参数){要执行的代码};
var fun = function(){}
- 通过函数表达式创建的函数是一个匿名函数,存储于变量中
- 函数调用时用:变量名()
var fun = function(){
console.log(1);
};
fun();//1
3、Function构造函数
var name = new Function(“参数”, “要执行的代码”);
var fun = new Function("name","return 3");
console.log(fun());//3
- 参数和代码的引号不能掉
- 调用时通过:name()
- 通过构造函数方式定义的函数不能进行递归
4、函数提升
当函数调用在前,定义在后时,就会产生函数提升,函数的提升是直接将整个函数整体提升到作用域的最开始位置,提升后的位置是在变量提升后的位置之后的,
类型 | 提升情况 |
---|---|
变量 | 将变量定义提升到作用域最开始位置,并且只提升定义不提升赋值,但是变量先提升 |
函数 | 将函数整个提升到作用域最开始位置,相当于剪切复制 |
function foo() {
console.log(a);//a(){}
var a = 1;
console.log(a);//1
function a() {}
console.log(a);//1
}
foo();
注意:
- 只有使用关键字声明的函数和变量才会提升
- 局部变量中的隐式全局变量不会提升
5、自调用函数
在声明函数的同时,对函数进行调用:
( function(){} )()
必须将函数用括号包裹再跟一个调用的括号才可以进行自调用
(function fun(){
console.log(2);
})()//2
当将一个函数赋值给一个变量,可以不用括号包裹函数,直接加一个执行的括号即可:
var fun = function(){
console.log(1);
}();//1
二、函数调用
一般来说,声明的函数是不会执行的,当调用后才会执行,调用函数通常使用的方法是:functionName(),一般有以下几种调用方式:
1、函数调用模式
函数调用模式即普通调用模式:
functionName()
function fun(){
console.log(1);
}
fun();//1
2、方法调用模式
方法调用模式即将函数定义为对象的方法,调用时:
ObjectName.functionName()
var obj = {
name:'Tom',
age:20,
sleep:function(){
console.log('sleep');
}
}
obj.sleep();//sleep
注意:
- 在使用方法调用模式时一定要保证this的指向正确,否则将会出现错误
3、构造函数调用模式
使用new关键字创建函数对象对构造函数进行调用:
function Person(name,age){
this.name = name;
this.age = age;
this.say = function(){
console.log(`${this.name}saying`);
}
console.log(this);
}
var person = new Person('Tom',30);//Person {name: "Tom", age: 30, say: ƒ}
console.log(person);//Person {name: "Tom", age: 30, say: ƒ}
person.say();//Tomsaying
注意:
- new时this指向构造函数的实例化对象
- 构造函数没有设置return时,或者return基本数值类型时,都默认返回this
- 可以更改返回的this的指向,比如函数、对象、数组等
4、间接调用模式
使用call、apply、bind去间接调用定义的函数,返回值依然由函数return决定:
var obj = {};
var getSum = function(a, b) {
console.log(this);
return a + b;
}
// 间接调用模式: call/apply/bind
// call 和 apply可以改变函数内部this的指向,并且执行函数
console.log(getSum.call(obj, 10, 20));
console.log(getSum.apply(obj, [10, 20, 30]));
// bind改变了函数内部的this指向,但是没有执行函数
console.log(getSum.bind(obj, 10, 20)());
三、函数参数
名称 | 作用 |
---|---|
形参 | 定义函数时设置的参数,用于接收实参的值,可以为形参设置初始值 |
实参 | 调用函数时传递的参数,用于赋值给形参,实参个数可以为任何个 |
注意:
- 实参个数比形参多时,函数仅接收形参个数的实参,所有的实参将会保存在arguments类数组中
- 实参个数比形参少时,未接收到实参的形参其值为undefined
- 设置同名形参时,函数内部只能使用最后一个
function fun1(a,b,c){
console.log(a,b,c);
}
fun1(1,2,3);//1,2,3
fun1(1,2);//1,2,undefined
fun1(1,2,3,4);//1,2,3
function fun2(a,a,a){
console.log(a);
}
fun2(1,2,3);//3
1、arguments
函数的实参将会保存在arguments中,arguments是一个类数组,可以获取参数个数
function fun1(a,b,c){
console.log(arguments);
console.log(arguments.length);
}
fun1(1,2,3);//1,2,3 3
fun1(1,2,3,4,5);//1,2,3,4,5 5
fun1();// 0个 0
可以将arguments转换为数组:
function show(a,b,c) {
console.log(arguments);
for(var i = 0; i < arguments.length; i++) {
console.log(arguments[i]);
}
// ES5将arguments转为数组
var x = Array.prototype.slice.call(arguments);
console.log(x);
// ES6中方法
var y = Array.from(arguments);
console.log(y);
}
show("apple", "banana", "lemon");
显式参数就是在定义函数时列出的形参,函数可以不定义形参,没有形参的函数也可以传递实参,实参会存放在arguments中,这时就相当于函数有隐式参数
function fun4(){
console.log(arguments);
}
fun4(1,2,3,4);//1,2,3,4
2、参数默认值
在定义函数参数时,给参数赋值
function fun(a,b = 1){
console.log(a,b);
for(var i = b; i < 10; i++){
;
}
console.log(i);
}
fun(2);//a = 2,b = 1,i = 10
四、call、apply、bind
call、apply、bind都可以改变函数内部this的指向,
方法 | 特点 |
---|---|
call | 参数散列、更改后直接执行 |
apply | 参数是数组、更改后直接执行 |
bind | 参数散列,更改后不会直接执行,返回函数本身,需要再次调用 |
var Person = {
username:'mei',
say:function(){
console.log(this.username);
}
}
var p1 = {username:'wang'};
var pp1 =Person.say.call(p1);//wang
console.log(pp1);//undefined
var pp2 = Person.say.apply(p1);//wang
console.log(pp2); //undefined
var pp4 = Person.say.bind(p1);
console.log(pp4);//say方法
Person.say.bind(p1)();//wang
1、call的使用例子:functionname.call(this的指向对象)
var Person = {
username:'mei',
say:function(){
console.log(this.username);
}
}
Person.say();//mei
var p1 = {username:'wang'};
Person.say.call(p1);//wang
//将say劫持过来,用在call的第一个实参上
var p2 = {username:'zhang'};
Person.say.call(p2);//zhang
2、apply的使用例子:functionname.apply(this的指向对象)
var Person = {
username:'mei',
say:function(){
console.log(this.username);
}
}
Person.say();//mei
var p3 = {username:'Tom'};
Person.say.apply(p3);
3、bind使用例子:bind:functionname.bind(this的指向对象),
var Person = {
username:'mei',
say:function(){
console.log(this.username);
}
}
Person.say();//mei
var p4 = {username:'Tony'};
Person.say.bind(p4);//没有输出
var pp4 = Person.say.bind(p4);//返回的是say方法
console.log(pp4);
Person.say.bind(p4)(); //调用后输出Tony