总:函数
函数对于编程语⾔来说是⼀个核⼼的概念,通过函数我们可以封装任意多条语句 ,在任何地⽅任何时候调⽤执⾏
在ECMAScript⾥⾯,函数是通过关键字来定义 function
- 任意多条语句
- 任意地⽅任何时候
- 调⽤执⾏
一.函数的定义
1.第⼀种定义⽅式——函数声明
优点:由于函数声明提升的特性,可以在函数声明之前调用函数,这提供了灵活的代码组织方式。
缺点:在严格模式下(“use strict”),某些情况下函数声明提升可能不生效,需要注意放置函数声明的位置。
function 函数名(参数...?){
//函数的代码体
}
// 封装了我们的第⼀个函数
function sayHello() {
console.log("--------------------------------");
console.log("刘坤说他是我们班最帅的男孩⼦...");
console.log("--------------------------------");
}
2.第⼆种定义⽅式——函数表达式
优点:函数表达式可以创建匿名函数并直接赋值给变量或作为其他函数的返回值,提供更大的灵活性。
缺点:与函数声明不同,函数表达式不提升,必须先声明再调用,否则会导致错误。
var 函数名 = function(参数...?){
//函数代码码
}
var sayHello = function(){
console.log("--------------------------------");
console.log("刘坤说他是我们班最帅的男孩⼦...");
console.log("--------------------------------");
}
警告:两种定义⽅式最终得到的效果是⼀样的,唯⼀的区别就在于它们的调⽤⽅式
两种函数的区别:
通过 function 关键字定义的函数是可以在任何地⽅调⽤执⾏的,但是通过 var 定义的函数只能在定义之后再调⽤执⾏
3.第三种定义⽅式——Function构造函数
使用Function构造函数可以创建新的函数对象
优点:动态创建函数,可用于需要根据不同条件创建不同函数逻辑的场景。
缺点:使用Function构造函数创建的函数是全局作用域的,可能会污染全局环境,且性能相对较差。
var sayHi = new Function("alert('Hi')");
sayHi();
4第四种定义⽅式——箭头函数
基本语法:ES6引入了箭头函数,其基本形式为const functionName = (arg0, arg1, …, argN) => { … }。
const sayHi = () => {
alert("Hi");
};
sayHi();
优点:箭头函数比传统函数更简洁,且不绑定自己的this,适合用于回调函数和事件处理程序。
缺点:不能用作构造函数,也不能使用arguments对象,限制了其在某些场景下的应用。
5.第五种定义⽅式——方法定义
基本语法:在对象或类中定义的方法也是一种函数定义方式。
class Greeter {
constructor(message) {
this.message = message;
}
sayHi() {
alert(this.message);
}
}
const greeter = new Greeter("Hi");
greeter.sayHi();
优点:结合面向对象编程范式,通过方法定义可以封装数据和行为,提高代码的模块化和维护性。
缺点:如果过度使用,可能导致类和方法过于庞大,不利于管理。
二.函数的调⽤执⾏
⼀个函数封装完成以后,它不是⾃已执⾏,它只会调⽤以后才执⾏(所以函数也叫调⽤执⾏)
函数的调⽤执⾏⽅式⾮常简单,只需要通过 函数名() 就可以了
//这⾥是函数的封装
function sayHello() {
console.log("--------------------------------");
console.log("hello!world");
console.log("--------------------------------");
}
// 函数名+()来调⽤执⾏
// 上⾯的函数名是sayHello,我们调⽤就可以通过下⾯的⽅式来进⾏
sayHello();//调用方法
三.函数的检测
函数也可以看成是⼀个变量,变量就会有数据类型 ,如果想检测⼀个变量是否是函数,我们可以通过关键字 typeof 来完成
//上面提到的函数的表达式
var aaa = function(){
}
//上面提到的函数声明
function bbb(){
}
console.log(typeof aaa); //"function"
console.log(typeof bbb); //"function"
四.函数作⽤域【重点】
在ECMAScript⾥⾯, 普通的花括号,如 if,else,while,for,do 等⼀系列的,都形成不了作⽤域,所以这些作⽤⾥⾯的变量全都是全局变量—-如下代码:
if(true){
var a = 123;
}
console.log(a); //123
//形成不了作用域,在外面可以访问到
只有通过函数的花括号⾥⾯定义变量才是局部变量
function bgg(){
var b = 456; //这⾥定义的b是局部变量,只能在bgg这个函数⾥⾯使⽤
}
console.log(b); //function形成了作用域在外界就访问不到————报错
总结:函数体的花括号会形成作⽤域
五.函数的参数
函数的参数是⼀种特殊范围的变量,它可以实现函数外部的赋值,同时函数的内部也可以使⽤
function sayHello(stuName) {
// 这个时候的stuName就是⼀个特殊范围的变量
// 这种特殊的变量叫参数
// 参数是可以实现外部赋值,内部使⽤了
console.log("-----------------------------");
console.log(stuName+"说他是我们班最帅的男孩⼦...");
console.log("-----------------------------");
}
//调⽤函数
sayHello("小明");
sayHello("小金");
参数的应⽤点:什么情况下会使⽤参数?
当我们在封装函数的时候,如果发现某些值需要通过外部来设定的时候,我们就会使⽤参数
参数的注意事项:参数⾥⾯的值会随着函数体代码运⾏结束⽽⾃动销毁——如下:
function aaa(userName){
console.log("-----------");
console.log(userName);
console.log("-----------");
}
aaa("张三丰"); //当代码运⾏结束以后,参数就销毁了
aaa(); //这⼀次我们没有传值 ,userName上⼀次的值被销毁,这⼀次没有值,它就是undefined
扩展:形参与实参
形参:形式参数,指函数在定义的时候花括号⾥⾯的参数【说得通俗⼀点就是变量名】
实参:实际参数,指调⽤函数的时候的括号⾥⾯的值(它是⼀个具体的值,可以认为就是变量的值)
实参的值传向形参::实参——————>形参
通俗来说,形参就是变量名,实参就是变量的值
function sayHello(stuName,age) {
console.log("⼤家好,我叫" + stuName+",我今年年龄"+age);
}
sayHello("刘德华",18)
上⾯的 stuName 与 age 代表的就是形参(具体的变量名),下⾯的"刘德华、18"就是实参(变量值)
⼀定⼀定要注意,实参的值传给形参,相当于变量的值赋值给了变量
那么我们思考一下如果形参的个数与实参的个数不一样会怎么样?
function sayHello(stuName,age) {
console.log("⼤家好,我叫" + stuName+",我今年年龄"+age);
}
sayHello("刘德华",18); //var stuName = "刘德华" ;var age = 18;
sayHello("刘亦菲"); //var stuName= "刘亦菲";var age;
// 请思考下⾯的代码是否报错
sayHello("吴亦凡", 29, "活泼开朗");
//var stuName = "吴亦凡";var age = 29;"活泼开朗"
在上⾯的代码⾥⾯,我们可以看到,函数⾥⾯的参数是可以不⼀⼀对应的。在第⼆次调⽤ sayHello的时候,我们的实参只有1个,⽽形参有2个,第三次调⽤的时候,我们有3个实参,这样也不会报错
实参与形参总结
- 实参向形参赋值
- 实参与形参的个数不需要⼀⼀对应
上⾯第2点与其它编程语⾔是完全不同的,其它的编程⾥⾯⾔⾥同,形参与实参必须相⼀⼀对应
六.arguments实参数组
参数不同情况的说明
- 当实参个数与形参个数相同的时候,⼀对⼀赋值
- 当实参个数⼩于形参个数,没有接收到值的形参它就是 undefined
- 当实参个数⼤于形参个数,前⾯的⼀对⼀赋值,多的值会放在⼀个特殊的地⽅,叫实参数组,这个实参数组叫 arguments 【特殊说明,后期我要更正这句话】
function add(a, b) {
console.log(a);//刘德华
console.log(b);//吴彦祖
// 所有的实参都会在arguments⾥⾯
console.log(arguments);
//Arguments(3) ['刘德华', '吴彦祖', '陈冠希', callee: ƒ,Symbol(Symbol.iterator): ƒ]
}
add("刘德华","吴彦祖","陈冠希");
你传递了多少个实参进去,那么 arguments ⾥⾯就会有多少个元素,如果不给实参,它就是空的
扩展:arguments解析
在上⾯的执行结果⾥⾯,我们得到了⼀个点,它有索引,它也有 length 属性,⻓得像⼀个我们学习的数组。所以我们把 arguments 叫实参数组
function add(a, b) {
console.log(arguments);
console.log(arguments[0]); //通过索引来取值
console.log(arguments.length); //也可以获取⻓度
console.log(Array.isArray(arguments)); //false
}
add("刘德华","陈奕迅","林俊杰","周杰伦","张杰");
那么arguments与真正的数组有什么区别呐?
- 两边的对象上⾯都有索引,以及 length 属性,所以它们都可以通过索引来取值,也可以通过legnth 来获取⻓度【相同点】
- arguments 只具备的数组的特征,但是不具备数组的 push/pop/shift/unshift 等⼀系列⽅法【不同点】
总结:在 JavaScript ⾥⾯,如果有⼀个对象具备数组的特征(索引与⻓度),但是⼜不具备数组的那些⽅法,我们就把这些对象叫**类数组(伪数组)**所以上⾯的 arguments 并不是⼀个真正的数组,它是⼀个类数组
arguments的应用点:
function add() {
//所有的参数都会在arguments⾥⾯,所以我们直接从这⾥找就⾏了
// console.log(arguments);
var sum = 0;
for (var i = 0; i < arguments.length; i++) {
sum += arguments[i];
}
return sum;
}
var x = add(11, 12);
var y = add(11, 12, 13);
var z = add(11);
扩展:函数的返回值
返回值与参数其实是⼀个相对的过程
- 参数:函数外边的值赋值给函数⾥⾯使⽤
- 返回值:函数⾥⾯的值拿到外边来使⽤
函数的返回值是通过 return 关键字来实现的,每⼀个函数都可以设置返回值
function aaa(){
var x = "白龙马";
return x;
}
//当aaa()函数运⾏结束以后,最后返回x到外边,将这值在外边赋值给了变量stuName
var stuName = aaa();
console.log(stuName);
什么场景下⾯会使⽤返回值?
当⼀个函数运⾏结束以后,我们希望还要把某个值重新给外边去使⽤,这个时候就可以使⽤return 将这个值返回到外边