函数,一段JS代码,它只定义一次,但可以被执行或调用任意次。
JS函数是参数化的。
函数定义构成:函数名称标识符,一对圆括号,一对花括号。
function 函数名 ( ) {
}
function add(num1,num2){
return num1+num2;
}
let sub=function(num1,num2){
return num1-num2;
}
区别:函数声明语法会提升,且可以在函数声明之前调用;而函数表达式只能在声明之后被调用,函数名是可写可不写,且在函数之外无法用函数名调用。
let mul =(num1,num2) => num1*num2;
//函数内没有多余语句,即函数内只有return语句,才能省略写法。
let mul =(num1,num2) => {
return num1*num2;
}
//没有参数,不能省略圆括号
let f1 = () => {
console.log("abc");
}
//若只有一个参数,则可以省略圆括号
let f2 = x => x**2;
//立即执行函数,xx值为函数值传进去执行的结果
let xx=((x)=>x**x)(4) //256
let sum = new Function(
"num1",
"num2",
"let result=num1+num2;return result;"
);
//接受任意多个字符串参数,最后一个参数始终会被当成函数体,而之前的参数都是新函数的参数。
不推荐使用,会被解释两次,影响性能。
函数名称建议使用以动词或以动词为前缀的词组。
通常函数名第一个字符为小写
若函数名包含多个单词时:
有一些函数作为内部函数或私有函数,这种函数名通常以一条下划线为前缀。
eg. _ _ proto_ _
特征:ECMAScript函数不关心传入参数个数,也不关心传入参数的数据类型。
因为ECMAScript函数的参数在内部表现为一个数组。
一个类数组对象,可以使用中括号访问其中元素。
arguments[0]、arguments[1]
要确定接收到的参数个数,可以访问arguments.length属性。
ES5
function f1(name, age) {
name = name ? name : "User"; //可以简化为 name || "User"
age = age ? age : 0; // age || 0
console.log ( name, age);
}
f1();
f1("Tom");
ES6
function f2(name = "User",age = 0){
console.log (name,age) ;
}
f2( );
f2( "Tom" ) ;
function sum(){
};
let nums=[ ];
//将nums传入sum()
sum.apply(null,nums); //ES5
sum(...nums); //ES6
剩余参数语法允许我们将一个不定数量的参数表示为一个数组。
function sum2(name,...scores){
let r=scores.reduce((x,y)=>x+y,0);
console.log(`${
name}总分为:${
r}。`);
}
sum2("Tom",80,90,100);
构成函数主体的JavaScript 代码在定义之时并不会执行,只有调用该函数时,它们才会执行。
调用函数的方法:作为函数、作为方法、作为构造函数、通过它们的call( )和apply( )方法间接调用。
sum( 1,2,3+6,getNum( ) );
对于普通的函数调用,瓯数的返回值成为调用表达式的值。
函数调用上下文(this的值)是全局变量。
方法调用和函数调用有一个重要的区别,即 调用上下文。
let calculator={
oper1:10,
oper2:20,
add:function(){
this.result=this.oper1+this.oper2;
},
};
calculator.add();
console.log(calculator.result); //30
调用上下文
关键字this没有作用域的限制,嵌套的函数不会从调用它的函数中继承this。
先设置一个变量保存当前this的值,以免下文this再次被使用而改变。
如果函数或方法调用之前带有关键字new ,它就是构造函数调用。
构造函数通常不使用return关键字,它们通常初始化新对象,当构造函数的函数体执行完毕时,它会显式返回该对象。
使用函数对象的call( )和apply( )方法可以间接调用函数。
第一个参数指定调用上下文(函数内部的this ) ,第二个参数给函数传递参数。
let obj1={
x:100,
y:200,
show:function(n=1,m=1){
return `(${
this.x*n},${
this.y*m})`;
},
concat:function(){
let r=[this.x,this.y];
for(let a of arguments){
r.push(a);
}
return r;
},
};
let obj2={
x:111,y:222};
let r1=obj1.show.call(obj2);
let r2=obj1.show.call(obj2,10,100);
console.log(r1,r2);
//(111,222) (1110,22200)
let r3=obj1.concat.call(obj2,11,22,33);
let r4=obj1.concat.apply(obj2,[2,3,4,5]);
console.log(r3,r4);
//[ 111, 222, 11, 22, 33 ] [ 111, 222, 2, 3, 4, 5 ]
被作为实参传入另一函数,并在该外部函数内被调用,用以来完成某些任务的函数,称为回调函数。
在函数体里,arguments.length 表示传入函数的实参的个数。
而函数本身的length属性是只读的,它代表函数声明的实际参数的数量。
function fn(a,b){
console.log(arguments.length);
console.log(fn.length);
console.log(arguments.callee.length);
};
fn(1,2,3,4,5);
//5
//2
//2
每一个函数都包含一个prototype属性,这个属性是指向一个对象的引用,这个对象称做“原型对象”(prototype object) 。
每一个函数都包含不同的原型对象。当将函数用做构造函数的时候,新创建的对象会从原型对象上继承属性。
函数是一种特殊的对象,可以拥有属性。
function fx(a,b){
if(fx.count) fx.count++;
else fx.count=1;
return a+b;
}
fx(1,2);fx(2,3);fx(3,4);fx(4,5);
console.log(fx.count);
//4
call( )和apply( )方法
bind( )方法
let obj={
x:10,
show:function(y){
let r='';
for(let i=0;i<y;i++){
r+=this.x+'';
}
console.log(r);
},
};
obj.show(4);
let ss=obj.show.bind({
x:1000});
ss(3);
//10101010
//100010001000
ss.call({
x:2000},3);
//100010001000
let sum=function(x,y){
return x+y;
};
let succ=sum.bind(null,1);
console.log(succ(2));
let obj={
};
obj.supAdd=sum.bind(obj,10);
//偏函数,固定一个函数的一个或者多个参数,返回一个新的函数,这个函数用于接受剩余的参数。
console.log(obj.supAdd(20));
//3
//30
偏函数的好处:
- 通过创建一个名称易懂的独立函数,调用是无需每次传入第一个参数,因为第一个参数通过bind提供了固定值。
- 当我们有一个很通用的函数,为了方便提供一个较常用的变体。