一个前端开发者的救赎之路-JS基础回顾(三)-Function函数

函数的声明

  1. 赋值式:var func = function() {}
  2. 声明式:function func() {}
  3. 箭头函数:()=>{}
  4. 三者之间的区别
    a. 声明式可以在函数声明前去调用,赋值式不可以
    b. 箭头函数,他们从定义自己的环境继承上下文,而不是像以其他方式定义的函数那样定义自己的调用上下文
    c. 箭头函数没有prototype属性,所以它不能作为新类的构造函数

函数的调用

  • 作为函数:

    • 注意这个说法,函数是通过调用表达式被作为函数或方法调用的。

    • 调用表达式包括求值为函数对象的函数表达式,后跟一对圆括号,圆括号中是逗号分隔的零或多个参数表达式列表

    • this指向:

      • 非严格模式:全局对象
      • 严格模式:undefined
      • 注意 箭头函数又有不同:它们总是继承自身定义所在环境的this值(这里即使是严格模式,this也是window
      • 下面的代码可以用来判断是不是处于严格模式
      js 复制代码
      // 定义并调用函数,以确定当前是不是严格模式
      const strict = (function() { return !this }())
      • 我有一篇文章,这里面有一个自己遇到的有趣的this指向和作用域的问题
    • 这里注意条件式调用:

      • 在Es2020中,可以在函数表达式的后面、圆括号的前面插入?.,从而只在函数不是null或undefined的情况下调用。
      • 在没有副作用的前提下:f?.()<=>(f !== null && f !== undefined ? f() : undefined)
  • 作为方法:

    • this关键字不具有变量那样的作用域机制,除了箭头函数,嵌套函数不会继承包含函数的this值。如果嵌套函数被当做方法来用,那它的this就是调用它的对象。如果嵌套函数(不是箭头函数)被当做函数来调用,则它的this值要么是全局对象(非严格模式),要么是undefined(严格模式)

      js 复制代码
      let o = {
          a: 1,
          b: 2,
          m: function() {
              let self = this;
              console.log(this.a); // 1
              f();
      
              function f() {
                  console.log(this.b); // undefined
              }
              
              const g = () => {
                  console.log(this.b); // 2
              }
              
              g();
          }
      }
      o.m();
  • 作为构造函数

    • 作为构造函数调用,上下文是这个新对象
  • 通过call()或apply()方法调用

    • 这两个方法允许我们指定调用时的this,这意味着可以将任意函数作为任意对象的方法来调用
    • call()方法使用自己的参数列表作为函数的参数,而apply()方法则期待数组值作为参数。
  • 通过JavaScript语言隐式调用

    • 这里在开发中排查bug,很关键,就是我们引用了别人的三方库或者代码都有可能出现这个隐式调用

函数的参数

  • 函数调用时所传的实参,可以少于形参也可以多于形参,
  • 函数定义式可以用一个剩余参数来接收多余的参数,剩余参数必须作为最后一个参数,并且是...args这种形式,剩余参数永远是一个数组,不会是undefined,即使没有传对应的实参,因此不需要给剩余参数默认值

JS预解析

  • 对于函数的预解析和普通变量不一样,函数预解析是直接把整个函数提到顶部作用域,在预解析时会提前定义,只是不会立即执行。
  • 普通变量只是把声明提升到所在作用域顶部,而不进行初始化。
  • 因此,上面函数定义的时候,如果使用字面量的方式会把这个变量提升到顶部作用域并赋值undefined,所以在定义前调用的时候会报错。
  • letconst 会提升(hoisting),但由于 TDZ(暂时性死区) 机制,在声明前访问会报错

作用域

  • 作用域在定义的时候,一个函数就会形成一个内部作用域,后来引入了let/const又有了块级作用域
  • 作用域在访问的时候,是从下往上找,一直找到顶级作用域,找不到就报is not defined
  • 在赋值的时候不是,如果一直到最顶级都未声明,那他就是直接在全局定义域声明并且赋值这个变量。所以即使在vue中的某一个函数直接使用xxx=来进行操作,也是会在全局作用域添加一个全局变量xxx(内存泄漏),千万不要这样干

函数的方法和属性

  1. length属性:返回声明时声明的形参的个数(不包含剩余参数),只读
  2. name属性:表示定义函数时使用的名字(如果是用名字定义的),如果是未命名的函数,表示在第一次创建这个函数时赋给该函数的变量名或属性名。这个属性主要用于记录调试或排错信息。只读
  3. prototype属性:箭头函数没有
  4. call()和apply()方法:箭头函数的this不会被修改
  5. bind()方法:bind()方法主要目的是把函数绑定到对象,箭头函数不起作用
  6. toString()方法:
    • ECMAScript规定返回一个符合函数声明语句的字符串
    • 实际上,多数(不是全部),都是返回函数完整源代码
    • 内置函数返回的字符串中通常包含"native code",表示函数体
  7. Function()构造函数:
    • Function()构造函数可以接收任意多个字符串参数 ,其中最后一个参数函数体的文本。这个函数体 文本中可以包含任意JavaScript语句,相互以分号分隔。传给构造函数的其他字符串参数,将作为这个新函数的的形参。
    • 注意:Function()构造函数不接受任何指定新函数名字的参数。与函数字面量一样,Function()构造函数创建的也是匿名函数
相关推荐
郭老二1 天前
【前端】vue3:编译步骤
前端
不在逃避q1 天前
Trae/Vs Code/Cursor命令行无法跑npm命令
前端·arcgis·npm
夏殇之殁1 天前
包中创建自定义列表项。 . 使用自定义列表项进行数据绑定 . 将天气预报数据保存到本地内存表,通过LiveBindings进行显示。 ...
服务器·前端·javascript
The Chosen One9851 天前
高进度算法模板速记(待完善)
java·前端·算法
陈随易1 天前
MoonBit抓包模块pcap,查看电脑的每一次联网通信
前端·后端·程序员
新中地GIS开发老师1 天前
WebGIS开发学生作品|低空航天管理与航线规划系统
前端·javascript·webgis·三维gis开发
用户059540174461 天前
大模型对话记忆持久化踩坑实录:用 Playwright 自动化测了 300 次,终于揪出会话丢失的真凶
前端·css
丙氨酸長鏈1 天前
Web前端入门第 问:JavaScript 一个简单的 IndexedDB 数据库入门示例
前端·javascript·数据库
kyriewen1 天前
面试官让我手写虚拟列表——AI生成的版本,快速滚动几下就白屏了
前端·javascript·面试
IT_陈寒1 天前
Vite热更新失效?你可能漏了这个配置
前端·人工智能·后端