基本概念
1、 一段js代码,只定义一次,但是可以执行或调用无数次。
2、 js函数是参数化的:函数定义时会包含一个形参,是局部变量;
3、 函数调用会为形参提供实参的值;
4、 函数使用实参的值来计算返回值,成为该函数调用表达式的值;
5、 除实参外,每次调用还会拥有另一个值(本次调用的上下文),这就是this关键字的值
6、 方法:如果函数挂载在一个对象上,作为对象的属性,称其为对象的方法。
当通过这个对象来调用函数时,该对象就是此次调用的上下文,也就是该函数的this 值
7、 构造函数:用于初始化一个新创建的对象的函数
8、 函数就是对象。Js可以把函数赋值给变量,或者作为参数传递给其他函数。
因为函数就是对象,所以可以把它们设置属性,甚至调用它们的方法
9、 闭包:函数可以嵌套在其他函数中定义。从而访问他们被定义时所处的作用域中的任何变量。
函数定义
定义的构成
1、名称标识符。名称:函数声明语句必须部分。
对函数定义表达式来说,这个名字是可选的。但若存在,则只存在于函数体中,并指代该函数对象本身。
2、()。包括0或多个用逗号隔开的标识符组成的列表。这些标识符是函数的参数名称,就像函数中的局部变量一样
3、{ }。包括0或多条js语句。调用及执行。
函数的定义方法
1、
// 函数声明语法
function add(num1,num2){
return num1+num2;
}
2、
// 函数表达式
let sub=function (num1,num2){
return num1-num2;
}
区别:以函数声明的方法定义的函数,函数可以在函数声明之前调用(变量提升);
而函数表达式的函数只能在声明之后调用
以函数声明的方法定义的函数,函数名是必须的。
如果函数表达式声明的函数有函数名,那么这个函数名就相当于这个函数的一个局部变量,只能在函数内部调用。(递归时最好加个名字)
let su=function s(num1,num2){
return num1-num2;
}
s(1,2);
//s is not defined
3、
// 箭头函数(es6新加)
let mu1=(mun1,mun2) => num1*num2;
let mu2=(mun1,mun2) => {
return mun1*mun2;
}
// 相当于mu1的定义
let f1 = () => {
console.log("arrow function");
}
// 若无参数,括号必须加
let f2 = x => x**2;
// 若有参数,可以无括号
let xx=((x) => x**x)(4);
//立即执行函数。
// (x) => x**x是参数为x的函数,后面(4)相当于为函数传入参数4,最后计算出来的结果赋值给xx
4、
// function构造函数
let sum=new Function(
"num1",
"num2",
"let return=num1+num2; return result;"
);
构造函数接收任意多个字符串参数,最后一个参数始终会被当成函数体,而之前的参数都是新函数的参数;
不推荐使用,影响性能。
函数命名
函数名称:合法标识符。通常是动词或以动词为前缀的词组
第一个字符为小写
当包含多个单词时:用下划线分隔:like_this()
除第一个单词外的单词首字母大写:likeThis()
有一些函数是用做内部函数或私有函数:以_为前缀
函数参数
特征:既不关心传入的参数个数,也不关心这些函数的数据类型 -> 定义时接受两个并不意味着调用时就要传两个。
原因:因为ES函数的参数在内部表现为一个类数组,函数被调用时总会接收一个数组,但函数并不关心这个数组中包含什么,传递函数的每个参数都被包含在arguments对象(类数组)中
// 参数默认值
function f1(name,age){
name=name?name:"User";
// 等价于name=name||"User"
age=age?age:0;
console.log(name,age);
}
f1();
f1("Tom",12);
function f2(name="User",age=0){
console.log(name,age);
}
f2();
f2("Tom",21);
// 扩展参数
function sum(){
let r=0;
for(let i=0; i<arguments.length;i++){
r+=arguments[i];
}
return r;
}
let nums=[1,2,3,4,5];
console.log(sum.apply(null,nums));
// 15
console.log(sum(...nums));
// 15
// 剩余参数
function sum1(name){
let r=0;
for(let i=1;i<arguments.length;i++){
r+=arguments[i];
}
console.log(`${name}总分为:${r}。`);
}
sum1("Tom",80,90,100);
// Tom总分为:270。
// reduce计算数组元素相加后的总和
function sum2(name,...scors){
let r=scors.reduce((x,y) => x+y,0);
console.log(`${name}总分为:${r}。`);
}
sum2("Tom",80,90,100);
// Tom总分为:270。
function sum3(name,...scors){
let r=scors.reduce((x,y) => x+y);
console.log(`${name}总分为:${r}。`);
}
sum3("Tom",80,90,100);
// Tom总分为:270。x+y后面的0是传入的初始值
arguments对象
一个类数组对象(不是Array的实例),因此可以使用中括号语法访问其中元素.
要确定接收到的参数个数,可以访问arguments.length
function sum1(){
return Array.from(arguments).reduce((x,y) => x+y,0);
}
// let sum2=() => {
// return Array.from(arguments).reduce((x,y) => x+y,0);
// };
// 报错。箭头函数内部不支持arguments对象(也没有this)
console.log(sum1(1,2,3),sum2(1,2,3));
let sum3=(...nums) => {
return nums.reduce((x,y) => x+y,0);
};
console.log(sum3(1,2,3));
function f1(x,y,...nums){
console.log(arguments);
console.log(nums);
}
f1(1,2,3,4,5,6);
// [3,4,5,6]
// arguments对象包含剩余参数值,剩余参数没有接收到实参时为空数组
f1(1,2);
// []
// function f2(x,...nums,y){
// console.log(nums);
// console.log(y);
// }
// f2(1,2,3,4,5);
//报错。 剩余参数要位于参数列表末尾
函数调用
1、 作为函数
使用调用表达式可以进行蹼泳的函数调用,也可以进行方法调用
Eg:sum(1,2,3+6,getNum())
非严格的es5中,this的值是全局对象,严格模式下是Undefinde
2、 作为方法
一般情况下和普通函数的使用方式一致
方法调用和函数调用的区别:调用上下文
调用上下文:关键字this没有作用域限制,嵌套的函数不会从调用它的函数中继承this
3、 作为构造函数
调用前带new就是构造函数调用。
构造函数调用创建一个新的空对象,这个对象继承自构造函数的prototype属性。
构造函数可以使用this来引用这个新创建的对象。
不使用return,当前构造函数的函数体执行完毕时,会显式返回该对象
4、 函数对象的Call()、apply()方法间接调用
第一个参数指定调用上下文(函数内部的this);第二个参数给函数传递参数
Call()的第一个必备第二个可以没有
call() 方法分别接受参数。
apply() 方法接受数组形式的参数。
5、 回调函数
被作为参数传入另一函数,并在该外部函数内被调用,用以来完成某些任务的函数。
函数对象
函数属性
1、 length:只读属性。代表函数声明的实际参数数量
(arguments.length表示传入函数的实参个数)
2、prototype:
每一个函数都包含一个prototype属性,这个属性是指向一-个对象的引用,
这个对象称做“原型对象”(prototype object) 。
每一个函数都包含不同的原型对象。当将函数用做构造函数的时候,新创建
的对象会从原型对象上继承属性
3、 自定义属性
函数方法
1、call()
2、apply()
通过调用方法的形式来间接调用函数
cal1()和apply()的第一个实参是要调用函数的主体对象,它是调用上下文
在函数体内通过this来获得对它的引用。
3、bind():
将函数绑定至某个对象
当在函数f().上调用bind()方法并传入-一个对象。作为参数,这个方法将返回一个新的函数
(以函数调用的方式)调用新的函数将会把原始的函数f()当做o的方法来调用。
传入新函数的任何实参都将传入原始函数。
第一个参数是指定上下文,第二个参数才开始传参
尾调用
// 尾调用.函数的最后是不是一个函数.可以提升性能,比递归好
function tail(x){
console.log('tail',x);
}
function fx(x){
return tail(x);
}
fx(123);// tail 123