JavaScript:函数

JavaScript函数与继承知识详解

函数概念

什么是函数

使用关键字"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

注意:

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

提示:重复声明会覆盖

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

函数的返回值

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

函数的参数

基本概念

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

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

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

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

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

对位传参法

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

对象传参法

  1. 当参数非常多时,想要不在通过位置来匹配参数,想让参数具有确定性,那么可以使用对象作为参数,然后就可以通过属性名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的一些说明

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

 

注意

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

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

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

 

函数递归

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

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

匿名函数的一些使用:

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

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

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

  自执行匿名函数

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

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

基本语法

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

//不带参数
  (function(){
      return "我是自己调用的函数"
  })();
  输出:"我是自己调用的函数"

//带有参数
  (function(a,b){
      return a*b
  })(3,4);
  输出:12
  1. 回调函数

回调函数的概念

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

例如有两个函数A,B

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

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

例如:

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

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

 

所以:

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

 

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

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

  1. 可以读取整个父级作用域函数内部的变量,
  2. 让这些变量的值始终保持在内存中。
  3.     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"

 

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

前言

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

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

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

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

JavaScript中的继承

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

 

例如:

 

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

 

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

 

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

 

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

 

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

  1. 修改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: "学生"}
  1. 构造函数与es6中的class

前言

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

 

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

 

  1. 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类的构造方法constructor
  • ES5的Person原型上的方法对应Es6的除了constructor以外的其他方法。

 

es6的继承

继承原理

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

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

 

 

关于constructor方法

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值