JavaScript:函数

    xiaoxiao2025-02-04  47

    函数概念

    什么是函数

    使用关键字"function", 定义的一段具有独立作用域,能被反复执行的语句块。

    函数有什么作用

    函数一般用于描述某种功能,实现某种功能

    函数的声明方式

    主要有三种声明方式

    1,利用关键字“function”声明

    基本格式为:

    function(){ }

    2,利用函数表达式进行赋值声明

    var fn=function(){ }

    注意:这样声明的函数,必须先声明后调用,否则会报错

    3,利用构造函数“ Function”声明

    基本语法格式为:

    var fnName = new Function(paramList , 函数体字符串);

    var newFn=new Function("a","b",return a*b); newFn(3.3) 输出:9

    注意:

    这种写法是将参数列表和函数体放置在了一起同样作为了参数。如果只有一个参数,那这个参数就是函数体。(就是花括号里面的代码)构造函数内的参数无论有多少个,始终会将其最后一个参数作为函数体去执行参数和函数体的语句要作为字符串去呈现

    提示:重复声明会覆盖

    和声明和一个变量一样,在同一个作用域内,若函数重复声明,后声明的函数会覆盖之前声明过的函数,使之前声明过的任何同名函数无效

    函数的返回值

    每一个函数都会有一个返回值,这个返回值可以通过关键字“return”进行设置若未显示地设置函数的返回值,那函数会默认返回一个undefined但若手动地设置了函数的返回值return后,函数将返回开发者手动设置的那个值在函数中,一旦执行完成“return”语句,那么整个函数就结束了,后续语句将不再执行;就是“return”之后的值只能有一个。如果尝试返回多个值,那得到的结果始终是最后一个值如果真的需要函数返回多个值,那就只有将值组合成一个对象或数组进行返回 function testFn(){ if(typeof testFn=="function"){ console.log("函数数据类型") } } testFn() 输出:函数数据类型

    函数的参数

    基本概念

    函数的参数称为形参与实参

    形参:定义函数时写的参数是形参(形式上的参数)

    实参:调用函数时传递的参数是实参(实际参数)

    函数最终的运算结果由实参所解决定

    不论形参还是实参,都不需要关键字“var”的显示声明,函数的参数只能在函数内部访问

    对位传参法

    形参与实参是通过位置而不是参数名来匹配对应的形参与实参的个数可以不一致如果一个形参没有接收到实参传递来的值,那么他为undefined

    对象传参法

    当参数非常多时,想要不在通过位置来匹配参数,想让参数具有确定性,那么可以使用对象作为参数,然后就可以通过属性名key来匹配。 function fn1(obj){ return { name:obj.name, genger:obj.genger, age:obj.age } } console.log(fn1({name:"yb",genger:"男",age:"20"}))

    参数默认值

    可以给参数设置默认值(es5写法两种,es6一种)   1. es5: 三目运算符,逻辑或运算符

    三目运算符

    function fn1(){ a=(a==undefined?0:a); b=(b==undefined?1:b); console.log(a,b) }

    逻辑或运算符

    function fn1(){ a=a丨丨"aaa"; b=b丨丨"bbb"; console.log(a,b) } fn1(5,6) 输出:5,6 fn1() 输出:aaa bbb

     

     

       2. es6:  在参数列表里面赋值

    function fn1(a=10,b=20){ return a+b } fn1() 输出:30

    Arguments

    arguments代表了一个函数的所有参数,他是一个类似数组的东西,可通过下标取值。在我们不知道具体的参数个数时,他尤为有用。

    将arguments转换为一个数组

    方式一:Array.prototype.slice.call(arguments)

    可通过数组原型上得slice方法截取arguments中所有的内容,然后作为一个数组返回,如下

    function newsunm(param){ arguments=Array.prototype.slce.call(arguments); arguments.push(4); return arguments } newsunm(1,2,3) 输出:[1,2,3,4]

    方式二:Array.from(arguments)

    function test(a,b,c,d){ console.log(Array.from(arguments))} test(1,2,3,4) 输出:[1,2,3,4]

    5 .函数的作用域

     

    全局作用域 & 函数作用域 & 块级作用域

    全局作用域在当前文件中的所有函数,块中都有效,不管let还是

    函数作用域只在某个函数里面有效,不管是var或者let都可以

    块级作用域只在if判断、for循环等语句里面有效,需要使用let关键字声明

    规定了我们定义的变量在某些范围下才能生效的规则

    有哪些作用域? 什么是作用域?

     

    全局作用域在当前文件中的所有函数,块中都有效

    var test=666 function fn(){ console.log(test) } fn() 输出:666

    函数作用域只在某个函数里面有效,不管是var或者let都可以

    function fn(){ var test1="hello" console.log(test1) } fn() 输出:"hello"

    块级作用域只在if判断、for循环等语句里面有效,需要使用let关键字声明

    for(var i=0;i<10;i++){ } i 输出:10 for(let i=0;i<10;i++){ } i 报错

     

     关于let的一些说明

    let声明的变量具有块级作用域let声明的变量不允许重复声明let声明的变量不会进行变量提升

     

    注意

    es5里面,变量作用域只分为两种类型,一种是“全局作用域”,一种是“函数作用域”。在es6里面才拥有块级作用域,即if判断、for循环等语句都有自己独立的作用域

    不同作用域变量的访问问题

    访问优先级:里层能访问外层,外层不能访问里层 块级能访问局部,局部能访问全局,全局不能访问局部,局部不能访问块级

     

    函数递归

    函数在执行的时候调用自身,称之为递归。通过递归可以同更少的代码完成很多需要大量代码来实现的功能。递归和循环十分的相似。在使用递归时同样要注意一个问题,就是要防止结束条件的不明确导致出现“死循环”,导致浏览器崩溃。建议使用循环来替代“递归”,防止有些浏览器对递归迭代周期过长而产生的报错和递归等效的循环 function arr(n){ if (n>1){ return n+arr(n-1) } else return n } arr(100) 输出:5050 自执行匿名函数   匿名函数

    匿名函数就是没有函数名的函数。

    匿名函数的一些使用:

    绑定事件:Btn.οnclick=function(){}  

    排序传参:[].sort(function(){})

    声明字面量函数:var fn=funtion(){}

      自执行匿名函数

    这种函数不需要任何调用,即可立即执行。

    也叫做 立即调用的函数表达式(标准叫法),或者一些其他的不怎么标准但常用的叫法【自调用函数、立即执行函数等】

    基本语法

    ( function(param) {...} ) (param );

    //不带参数 (function(){ return "我是自己调用的函数" })(); 输出:"我是自己调用的函数" //带有参数 (function(a,b){ return a*b })(3,4); 输出:12 回调函数

    回调函数的概念

    作为参数的函数就是回调函数!

    例如有两个函数A,B

    如果函数A作为B的一个参数传入B的话,

    那么我们就称A是回调函数。

    例如:

    function a(){ var i=2 function b(){ return "这是函数a的值"+i } return b } a()() 输出:这是函数a的值2" 闭包 【返回一个函数的函数】 为什么会产生闭包

    由于作用域的原因,我们无法在函数外访问函数里面定义的变量,但有时候我们又会有这样的需求,这个时候我们就需要使用闭包了。

     

    所以:

    当我们想要从外部读取函数里面定义的局部变量时,我们可以定义一个闭包实现。

     

    什么是闭包? 在函数A内部再定义一个函数a,然后子函数a控制父函数中的变量v1,然后在父函数A中把这个子函数a返回给调用方,这个就叫做闭包。 闭包的用途

    闭包从编码角度上讲,主要有两种用途

    可以读取整个父级作用域函数内部的变量,让这些变量的值始终保持在内存中。 function tex(){ var j=10 function tex1(){ return "这是j的值:"+j; }function tee2(index){ j = index return j } return { get:tex1, mot:tee2 } } var pow=tex() undefined pow Object {get: function, mot: function} pow.get() "这是j的值:10" pow.mot(100) 100 pow.get() "这是j的值:100"

     

    ES5模拟后端继承实现(不继承原型)

    前言

    在现实生活中,如果你是一个富二代,或者只要你的父母有房有车,那么你就不要这么辛苦的奋斗了,因为大家可能要为之奋斗一生的东西,如房子,车子等只需要从父辈继承即可。

    当然,如果你不是,那么恭喜你,也许你会成为富一代。

    正所谓穷则变,变则通,贫穷往往能激发一个人最大的潜力。

    相信自己,万事皆有可能!

    JavaScript中的继承

        在现实生活中存在着继承,那么在js中页是也在着继承的。

     

    例如:

     

    对于人而言,可以直接简单的归类为people类,也可以稍微细致点归为男人类,女人类,又或者更多的类。

     

       那么现在如果已经有了人这个类了,这个类具有人的基本属性:姓名,性别,年龄等。

     

    然后,现在你需要在定义一个女人这个类,具有:【姓名,性别,年龄等属性 + 独属于女人类的属性】。

     

    这时对于姓名,性别,年龄等属性你需要在重新定义一次吗?不需要的,我们可以直接继承people类即可,这样既能简化代码,又能提高扩展性,还更清晰明了。

     

    这就是js中的继承这个概念,然后继承可以分为单继承和多继承,分别如下所示。

    修改this指向:单继承基本实现

    这里我们就需要使用call或者apply来实现

    function aaa(name,sex,age){ this.name=name; this.sex=sex; this.age=age } function bbb(name,sex,age,identity){ aaa.call(this,name,sex,age); this.identity=identity } var wu=new bbb("zs", "女", 20,"学生") undefined wu bbb {name: "zs", sex: "女", age: 20, identity: "学生"}

     

    修改多个对象的this指向:多继承的基本实现

    如果要继承多个类,我们只需要进行多次call或者apply调用即可

     

    function Person(name){ this.name=name; } function man(age){ this.age=age; } function identity(name,age,id){ Person.call(this,name); man.call(this,age); this.id=id } var jj=new identity("zs","20","学生") undefined jj identity {name: "zs", age: "20", id: "学生"} 构造函数与es6中的class

    前言

    Es6中的class语法就是Es5中构造函数的另一种写法,一种更高级的写法,

     

    class语法的底层还是es5中的构造函数,只是把构造函数进行了一次封装而已。

     

    Es6出现的目的为了让我们的让对象原型的写法更加清晰、更像面向对象编程让JavaScript更加的符合通用编程规范,即大部分语言对于类和实例的写法。

    Es5中的构造函数写法

    function Person(x,y){ this.x=x; this.y=y; } Person.prototype.toString=function(){ return "这是"+this.x+"岁"+"这是"+this.y+"岁" } var tex=new Person(22,33) function (){ return "这是"+this.x+"岁"+"这是"+this.y+"岁" } tex.to undefined tex.tos undefined tex.toString() "这是22岁这是33岁"

     

    Es6中的class写法

    Es6的class基本写法

    基本上,ES6的class可以看作只是一个语法糖,它的绝大部分功能,ES5都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。

    class text{ constructor(x,y){ this.x=x; this.y=y; } toString(){ return "这是"+this.x+"岁"+"这是"+this.y+"岁" } } var tex=new text(22,33) class text{ constructor(x,y){ this.x=x; this.y=y; } toString(){ return "这是"+this.x+"岁"+"这是"+this.y+"岁" } } tex.toString() "这是22岁这是33岁" class注意事项

     (1)和let一样,ES6的class不允许重复定义

    (2)和let一样,ES6的class不存在变量提升,需要先定义在使用

     

     

    ES5与Es6的对应关系

    ES5的构造函数Person,对应ES6的Person类的构造方法constructorES5的Person原型上的方法对应Es6的除了constructor以外的其他方法。

     

    es6的继承

    继承原理

    子类的原型对象的__proto__就是一个父类的实例对象,这样子类实例就能访问父类原型上的方法与属性,父类的原型对象还是Object的一个实例,,所以最终会找到Object的原型对象上去。

    如果用es5的语法来实现的话,就可以这样来处理,将父构造函数的实例赋值给子构造函数的原型属性

     

     

    关于constructor方法

    constructor方法是类的构造函数是默认方法,通过new命令生成对象实例时,自动调用该方法一个类必须有constructor方法,如果没有显式定义,一个默认的constructor方法会被添加。所以即使你没有添加构造函数,也是有默认的构造函数的。但是默认的constructor方法只会返回一个空对象

    最新回复(0)