js红宝石 第十章-函数

文章详细介绍了JavaScript中定义函数的多种方式,包括函数声明、变量初始化和箭头函数,强调了箭头函数的特性和限制。还讨论了函数参数的灵活性,如默认参数、拓展参数和收集参数。此外,提到了函数的重载、作用域、this的用法,以及闭包的概念,这些都是JavaScript编程中的重要概念。
摘要由CSDN通过智能技术生成

定义函数的几种方式

      // 函数声明
      function sum(num1,num2){
        return num1 + num2
      }

      // 变量初始化
      let sum = function(num1,num2){
        return num1 + num2
      }
      // 箭头函数
      let sum = (num1,num2)=>{
        return num1 + num2
      }

10.1 箭头函数

1.只有一个参数可以省略小括号

2.如果函数体只有一行代码,省略大括号会隐式返回表达式

      let sum = (a,b) => a+b// 省略大括号
      let printX = x => x// 省略小括号

箭头函数没有arguments,super和new.target.不能用作构造函数,也没有prototype属性

10.2 函数名

函数名就是指向函数的指针,所以一个函数可以有多个函数名

ES6中所有函数对象都会暴露一个只读的name属性,其中包含关于函数的信息(一般是标识符)

没有名称的函数会显示成空字符串,用Function创建的会被表示成"anonymous"

10.3 参数

ECMAScript不关心参数的个数,类型等.

传入的参数在解释器中被表现成一个数组arguments,可以在函数内部访问arguments去取得每一个参数值(箭头函数除外)

arguments参数可以和命名参数对象一起使用

    let sum = function(a,b,c){
      for(const i of arguments){
        console.log(i);
      }
     }

     sum(1,2,3,4)

箭头函数中的参数

箭头函数中只能用定义的命名参数访问

10.4 没有重载

ECMAScript没有函数签名,所以没有重载

如果定义了两个相同函数名,则后面的会覆盖前面的

10.5 默认参数值

ES6支持显式定义默认参数

定义的默认参数值不影响arguments参数

     let sayName = function(name = 'zs'){
      console.log( `My Name is ${arguments[0]}, ${name}` )
     }

     sayName()
     sayName('ls')

默认参数作用域与暂时性死区

 默认参数按照顺序进行初始化,因此后面的参数可以调用前面的,但前面的参数不能使用后面的(称为暂时性死区)

    function make(a = 10,b = a){
      //  相当于
      // let a = 10
      // let b = a
    }

10.6 拓展参数与收集

10.6.1 拓展参数

可以使用解构函数把数组拆分,当作传入的参数

      let value = [1,2,3,4]
      function sum(){
        let s = 0
        for(const i of arguments){
          s+=i
        }
        return s
      }
      sum(-1,...value)
      // 等价于
      sum(-1,1,2,3,4)

10.6.2 收集参数

在函数定义时,可以使用拓展操作符接收不同长度的独立参数

收集参数前如果还有命名参数,则会接收剩余参数

收集参数只能单独出现在最后一个参数

      let value = [1,2,3,4]
      function sum(...values){
        return values.reduce( (x,y)=>x+y,0 )
      }
      
      // 可以
      function sum(firstValue,...values){
        // 函数体
      }

      // 不可以
      function sum(...values,lastValue){
        // 函数体
      }

箭头函数虽然不支持arguments,但是支持收集参数的定义方式

10.7 函数声明与函数表达式 

使用function定义函数时,即使函数的定义出现在调用之后,也不会报错.因为函数声明提升,函数声明被提升到了代码顶部

      let value = [1,2,3,4]

      sum(balue)

      function sum(...values){
        return values.reduce( (x,y)=>x+y,0 )
      }

但是如果使用let的声明方式,则不行

10.8 函数作为值

函数名在ECMAScript中就是变量,所以函数可以用在任何可以使用变量的地方

所以函数可以作为参数,也可以作为返回值

10.9 函数内部

函数内部有三个特殊对象

arguments

this

new.target

10.9.1 arguments

arguments是一个类似数组的对象,包含调用函数传入的所有参数

arguments还有callee属性,指向arguments所在函数

      function factorial(num){
        if(num<=1){
          return 1
        } else {
          return num * factorial(n-1)
          // 使用 arguments.callee 可以使函数逻辑与函数名解耦
          return num * arguments.callee(n-1)
        }
      }

10.9.2 this

this在标准函数和箭头函数中的行为不同

在标准函数中,this.引用的是把函数当成方法调用的上下文对象(在网页全局的函数,this指向window)

      window.color = 'red'
      let o = {
        color: 'blue'
      }

      function sayColor(){
        console.log(this.color);
      }

      sayColor()// red

      o.sayColor = sayColor
      o.sayColor()// blue
      

在箭头函数中,this保留定义函数时的上下文

      function sayColor(){
        this.color = 'red'
        // 这里的this指向sayColor
        setTimeout(()=>console.log(this.color),1000)
      }

10.9.3 caller

caller的引用是调用当前函数的函数(在全局调用的则为null)

可以使用arguments.callee.caller来降低耦合度

      function outer(){
        inner()
      }

      function inner(){
        console.log(inner.caller);
        // 可以使用arguments.callee.caller来降低耦合度
        console.log(arguments.callee.caller);
      }

      outer()

在严格模式下,不能给caller赋值

10.9.4 new.target 

new.target 检测函数是否是用new调用的,如果是普通调用,显示undefined;如果使用了new,则音乐被调用的构造函数

10.10 函数属性与方法

每个函数都有两个属性:

length:保存函数定义时命名参数的个数

prototype:保存引用类型所有实例方法的地方.不可枚举

两个方法:

apply() 第一个参数接受this,第二个参数接受数组,也可以是arguments对象

会返回调用函数的值

      function sum(a,b){
        return  a+b
      }

      console.log(sum.apply(this,[10,10]));// 20

call()和apply()一样,但是第二个参数必须一个一个列出来

      function sum(a,b){
        return  a+b
      }

      console.log(sum.call(this,10,10));// 20

call()和apply()可以指定this值,可以把任何对象设置成函数作用域

      window.color = 'red'
      let o = {
        color: 'blue'
      }

      function sayColor(){
        console.log(this.color);
      }

      sayColor.call(window)
      sayColor.call(this)
      sayColor.call(o)

bind()函数会创建一个新的函数实例,并把其this值绑定到bind()对象

      window.color = 'red'
      let o = {
        color: 'blue'
      }

      function sayColor(){
        console.log(this.color);
      }

      sayColor.call(o)// blue

      let objectSayColor = sayColor.bind(o)
      objectSayColor()// blue

 

10.11 函数表达式

函数声明:函数声明提升

会在代码执行之前获得定义

函数表达式就像字面量赋值,又叫匿名函数(lambda函数)

匿名函数没有函数声明提升

      // 出错
      if(condition){
        function sayHi(){
          console.log('hi');
        }
      } else {
        function sayHi(){
          console.log('hello');
        }
      }

编译器会跳过if判断,直接把第二个函数声明覆盖第一个

换成函数表达式就可以了

      // 正确
      let sayHi
        // 出错
        if(condition){
         sayHi = function(){
          console.log('hi');
        }
      } else {
         sayHi = function(){
          console.log('hello');
        }
      }

10.12 递归

自己调用自己

      function factorial(num){
        if(num<=1){
          return 1
        } else {
          return num * factorial(n-1)
          // 使用 arguments.callee 可以使函数逻辑与函数名解耦
          return num * arguments.callee(n-1)
        }
      }

在严格模式下不能使用arguments.callee,但是可以使用命名函数表达式实现

     const factorial = (function f(num){
        if(num<=1){
          return 1
        } else {
          return num * factorial(n-1)
          // 使用 arguments.callee 可以使函数逻辑与函数名解耦
          return num * f(n-1)
        }
      })

10.13 尾调用优化

目前没有办法测试尾调用优化是否有用,但是浏览器都能保证实现

10.14 闭包

闭包指的是另一个函数作用域中变量的函数,通常是在嵌套代码中实现的

如下代码展示了闭包

      function createComparisonFunction(propertyName){
        return function(object1,object2){
          let value1 = object1[propertyName]
          let value2 = object2[propertyName]

          //比较代码
        }
      }

10.15 立即调用的函数表达式

(function(){
    块级作用域
})()

比如for中使用let初始化变量

for(let i=0;i<10;i++)

10.16 私有变量

任何定义在函数内部的变量和函数都可以认为是私有的,因为在块外无法访问

      function MyObject(){
        // 私有变量和私有函数
        let privateValue = 10

        function privateFunction(){
          return false
        }

        // 特权方法
        this.publicMethof = function(){
          privateValue++
          return privateFunction()
        }
      }

静态私有变量

特权方法可以通过私有作用域定义私有变量和函数实现

      (function(){
        // 私有变量和私有函数
        let privateValue = 10

        function privateFunction(){
          return false
        }

        // 构造函数
        MyObject = function(){}

        // 公有和特权方法
        MyObject.protorype.publicMethod = function(){
          privateValue++
          return privateFunction()
        }
      })()

使用闭包和私有变量会使作用域链边长,但是每个实例会有自己的私有属性

模块模式

单例对象需要进行某种初始化,且需要访问私有变量时使用

      let singleton = (function(){
        // 私有变量和私有函数
        let privateValue = 10

        function privateFunction(){
          return false
        }

        // 公有/特权方法/属性
        return {
          publicPrototype: true,

          publicMethod(){
            privateValue++
            return privateFunction()
          }
        }
      })()

模块加强模式

      let singleton = (function(){
        // 私有变量和私有函数
        let privateValue = 10

        function privateFunction(){
          return false
        }
        
        // 创建对象
        let object = new CustomType()

        // 公有/特权方法/属性
        
          object.publicPrototype= true

          object.publicMethod = function(){
            privateValue++
            return privateFunction()
          }
        
        //返回对象
        return object 
      })()

可以创建变量object保存CustomType的实例

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值