JS基础

js介绍

一门脚本语言

组成

  • ECMAScript

    • 简称ES,ES5,ES6
    • js语法规范
  • DOM

    • 文档对象模型
    • 操作页面内容
  • BOM

    • 浏览器对象模型
    • 操作浏览器功能

js基础写法

三种书写方式

  • 内联

    • 写在标签里面
  • 外联

    • js独立出来的文件
    • script配合src属性导入
    • 注意:如果写了外联,那么在 script 标签里,就不要写其他JS代码,因为写了也没用
  • 行内(了解)

    • 点我,告诉你黑马最帅的男人

js注释

  • // 单行

  • /* */ 多行注释

    • vs code 快捷键:shift+alt+a

js输入输出语句

  • alert(’ 提示内容 ')

    • 弹出一个提示框
  • prompt(‘提示用户输入内容’)

    • 弹出一个输入框
  • confrim(’ 提示用户内容’)

    • 弹出一个确认框(使用少)

js结束语

    • 英语分号
  • 什么也不写

    • 需要单独一行

变量声明,赋值,使用

什么是变量?

  • 可以理解为是一个装数据的容器

变量的声明

  • let 变量名
  • eg: let name

变量的赋值

  • 变量名=数据
  • eg:name=‘刘德华’

变量的使用

  • name

    • 直接写变量名
    • eg: alert(name)
    • 注:变量名你要加引号

变量初始化

  • 在声明变量时就赋值

    • let name=‘刘德华’

变量的本质

  • 就是在内存中开辟一个空间,存放数据

变量细节补充

  • 同时声明多个变量并赋值

    • 需要用逗号隔开
    • eg:let name = ‘jack’,age = 16
  • 如果变量只声明不赋值,那么值是undefined

    • 风格let name alert(name) // undefined let name, age = 16 // 相当于写的是 let name let age = 16 alert(name) // undefined alert(age) // 16
  • 如果一个变量不声明,但是直接复制了,可以用,但是不推荐

    • age = 30 alert(age)

变量命名规则跟规范

  • 规则

    • 不能用关键字
    • 变量名只能由字母,数字,下划线,美元符号($)组成,且数字不能开头
  • 规范

    • 起名要有意义,变量的名字跟保存的数据有关

    • 要用驼峰命名法

      • 如果一个变量名由多个单词组成吗,那么第一个单词首字母小写,后面每个单词首字母大写

        • 例:userName、 userLoginName

算术运算符

  • +加

  • -减

  • *乘

  • / 除

  • % 取余

  • 优先级

    • 先乘除后加减,有括号的先算括号里面的,同级就从左往右依次运算

document.write

  • 给 body 内增加内容

  • 如果内容带标签,会解析成html元素

    • document.write(‘

      哈哈哈,我出现了

      ’)

console.log

  • 它主要是给程序员自己调试数据用的
  • 在浏览器的f12 -> Console 来显示
  • 在控制台中,如果数据是字符串,则颜色为黑色

转义符

  • 符号:\ 可以把右边的符号转换原来的意义

    • \n`: 换行
    • \': 输出单引号
    • \": 输出双引号
    • \\ : 输出\

模板字符串

  • 反引号

  • 好处

    • 内容怎么写的,它就是怎么展示的
    • 如果想输出变量的值,不用再拼接字符串,写${变量名} 自动填充在里面

数据类型

数据类型

  • Numbr

    • 数值型
    • 写法:直接写数字,例如:10,11,12, 10.32, 11.2, -0.3
  • String

    • 字符串型,用来表示文字的
    • 写法:用单引号或双引号或反引号包起来的都是字符串
  • Boolean

    • 布尔类型
    • 这种类型只有两个数据: true 与 false
  • undefined

    • 代表未定义
    • 只有一个值就叫undefined

检测数据类型

  • typeof 检测基本数据类型

    • 检测数据类型是什么
    • 用法:typeof 数据 typeof 变量名
  • instanceof检测复杂数据类型

    • 可以判断一个数据是复杂数据类型的哪种

    • 用法:

      数据 instanceof 构造函数

      数据 instanceof Array // 判断是否为数组
      数据 instanceof Function // 判断是否为函数
      数据 instanceof Date // 判断是否为日期对象
      数据 instanceof Object // 判断是否为对象(所有复杂数据类型本质都是对象)

    • 如果是这个类型得到true,不是得到false

数据类型转换

  • 介绍

    • 把一种数据类型转换为另外一种数据类型
  • 隐式转换

    • 不用额外写代码、程序自己根据一定的规则来转换成某种类型
    • + 两边如果有一个是字符串,会把另外一个非字符串的也自动转换成字符串
    • 除了+以外的所有算术运算符(-、*、/、%) 都会把别的类型转换成数字类型,如果无法得到数字的,会得到NaN
    • NaN:Not a Number 代表非数字,它也是number类型(数字类型)
    • 它做任意数学运算得到的结果也一定是NaN-只要是数学运算都是转换成数字,但是如果是拼接就是转换成字符串
    • -缺点:需要记住这些规则,不太明显,增加了代码阅读难度
  • 显示转换

    • 转成字符串

      • String(数据)

      • 数据.toString()

        • toString 最好用在变量,否则会报错
    • 转成数字类型

      • Number(数据)

        • 整数小数都能转换
        • 只要有一个是非数字,得到的都是NaN,两边有空格无所谓,中间有就不行
      • parseInt(数据)

        • 转换成整数,只会的到整数部分
        • 从左往右依次转换,遇到非数字停止,有几个数字转换几个,若一个都没有得到NaN
      • parseFloat(数据)

        • 转换成小数或整数(数字是什么就的到什么)
        • 原理效果跟parseInt一样的,也是从左往右依次转换
      • 快速转

        • 直接在数据前面加+
      • 利用隐式转换

        • 数据 - 0 或 数据 * 1 或者 数据 / 1
      • 补充

        • 0不能作为除数,就是 / 右边的数,其他语言里会报错,JS里得到Infinity
        • Infinity代表无穷大,-Infinity代表无穷小

自增和自减

自增

  • 符号:++

    • 就是让自己+1

自减

  • 符号:–

  • 就是让自己-1

  • 如果不参与运算,++(–)写在前或者写在后都一样

  • 如果参与运算前缀和后缀就有区别:

    -前缀:先自增(自减),再用自增(自减)后的值参与运算

    后缀:先用原值参与运算,再自增(自减)

比较运算符

  • >:判断左边是否大于右边

  • <:判断左边是否小于右边

  • >=:判断左边是否大于或等于右边

  • <=:判断左边是否小于或等于右边

  • ==:判断左右两边是否相等,只判断值相等,不判断类型

  • ===:判断左右两边是否全等,既要值相等,也要类型相等

  • !=:判断左右两边是否不等

  • 得到的结果是布尔类型,也就是得到true或false

  • 一定要区分:赋值就是一个 = 不要写成 == ,除非你要判断值是否相等才写成 ==

逻辑运算符

&&:逻辑与

  • 左右两边都是true,结果才是true,有一个是false结果就是false
  • 口诀:一假则假

| | :逻辑或

  • 左右两边有一个是true结果就是true,两个是false结果才是false
  • -口诀:一真则真

! :逻辑非

  • true变false,false变true
  • 真变假,假变真
  • 它还有把别的数据类型转成布尔类型的特点

转换成布尔类型

  • Boolean(数据)
  • 只有0、空字符串、NaN、undefined、null转成false,其他都是true

转换成布尔类型

  • Boolean(数据)
  • 只有0、空字符串、NaN、undefined、null转成false,其他都是true

逻辑运算中的短路

  • 指的不执行右边的式子
  • 只存在于 && 和 || 中有短路
  • 当左边能确定整个式子结果,就没必要看右边了,所以发生了短路的现象(不执行右边的式子)
  • &&什么时候短路:在左边式子为false时短路
  • | | 什么时候短路: 在左边式子为true的时候短路

逻辑运算中的短路

  • 指的不执行右边的式子
  • 只存在于 && 和 || 中有短路
  • 当左边能确定整个式子结果,就没必要看右边了,所以发生了短路的现象(不执行右边的式子)
  • &&什么时候短路:在左边式子为false时短路
  • | | 什么时候短路: 在左边式子为true的时候短路

赋值运算符

=

  • 代表把右边的值赋值给左边变量
  • 他不是判断相等,判断相等是 ==

+=

  • 在自己值的基础上再+一个值

-=

  • 在自己值的基础上再-一个值

*=

  • 在自己值的基础上再*一个值

/=

  • 在自己值的基础上再/一个值

%=

  • 在自己值的基础上再%一个值

程序结构

顺序结构

  • 程序从上往下依次执行

  • 程序默认就是顺序结构

  • 语法:if(条件) { 代码 }

    • 当 if 小括号里的 条件 为 true 时,则执行大括号里的代码,如果为false就不执行大括号里的代码

分支结构

  • 分支语句之 if-else

    • 语法:if(条件){ 代码 } else { 代码 }

      • 如果条件满足,执行代码1,不满足就执行代码2
      • 所以也就是说代码1和代码2,只会选择一个来执行
    • 这也可以称之为双分支语句

  • 分支语句之if - else if - else

    • 语法:if(条件1){ 代码1 } else if(条件2){ 代码2 } … else { 代码 }
    • 先判断条件1,如果为true则执行代码1,如果为false则继续往下判断条件2,为true则执行代码2,为false则继续往下判断条件3,为true执行代码3,以此类推,如果上面条件都不满足就只执行else里的代码n
  • 分支语句之switch

    • switch也是多分支语句
    • 语法:switch (数据) {case 值1: 代码1 break; case 值2: 代码2 break; … default: 代码n break;

循环结构

  • whilex循环

    • 语法:while(循环条件) { 循环体 }

    • 循环体:就是要重复执行的代码

    • 语义: 判断循环条件是否为true,如果为true则执行循环体,否则跳出while循环

      • 如果为true时执行完循环体,会又回到while的循环条件的位置,继续判断是否为true,以此类推
    • 一般情况下,我们需要一个变量来控制循环的次数,这个变量叫循环增量,所以我们一般会这样写

      • eg:let i = 0 while (i < 次数) { 循环体 i++ }
      • 想执行几次,就在次数那写几
      • 切记:一定要写i++,因为如果不写会导致无限循环,这种我们称之为死循环
  • do-while循环

    • 语法:do { 循环体 }while( 循环条件)
    • 先执行循环体,然后再判断循环条件,如果条件为true,回来继续执行循环体,那么如果为false,就跳出循环
    • do-while循环的循环体至少会执行1次
    • 如果某个循环体,至少要执行1次的,那么就用do-while
  • for循环

    • 语法: for(声明循环变量;循环条件;变量++ ) { 循环体 }

    • 执行过程:

      • 先执行声明循环变量,再判断循环条件是否为true,为true就执行循环体,为false就直接跳出循环
      • 为true执行完循环体,会回到变量++的位置来做变量自增,自增完了再来判断循环条件,依次类推

break和continue

  • break

    • 结束所在的switch语句
    • 结束所在的循环
  • continue:

    • 只能用在循环
    • 结束当次循环,继续下次循环

循环嵌套

  • 循环里面再写一个循环就叫循环的嵌套

相等的一些细节

  • undefined == null 得到true

  • undefined === null 得到false

  • NaN 不等于任何数据,包括它自己

  • NaN == NaN 永远都是false

  • 那么如何判断是否为NaN?

  • isNaN(数据)

  • 如果是NaN得到true

  • 否则得到false

数组

数组语法

  • 数组初始化

    • let 数组名 = [ 数据列表 ]
    • 数据列表可以写任意个数据,如果多个数据用逗号隔开
  • 名词

    • 元素:数组里保存的每个数据都叫数组元素
    • 下标/索引:每个数据的编号,从0开始
    • 长度:数组中数据的个数
  • 单独取出某个数组里的元素,该怎么取?

    • 用下标取

    • 语法: 数组名[下标]

      • eg: nums[0]
  • 数组赋值

    • 也是通过下标重新赋值

    • 语法是:数组名[下标] = 数据

      • eg : nums[0] = 999
  • 数组的最大下标 = 长度 - 1

数组长度

  • 数组名.length

  • 数组长度可以更改

    • 数组名.length++

      • 则是在最后多一个元素,用undefined补齐
    • 数组名.length–

      • 则相当于删除数组最后一个元素
    • 数组长度直接赋值,如果赋的值比原来的长度要大,相当于增加,增加部分用undefined补齐

    • 数组长度直接复制,如果赋的值比原来的长度要小,相当于删除,减少几个就删除后面几个

遍历数组

  • 把数组中每个元素给取出来
  • 语法:for (let i = 0; i < 数组.length; i++){ 数组[i] }

动态添加数组

  • 可以对数组原本不存在的下标进行赋值,那么它就会在这个下标位置增加数据
  • 如果这个下标跟原本存数据的下标存在一段距离,那么这段距离会用undefined补齐
  • eg: 例:一个数组为let nums = [10,20, 30] 下标只到2, 但是如果我 nums[8] = 999 ,那么在下标8会多一个999,然后在下标3到下标7用undefined补齐

按顺序添加数组语法

  • 数组名[数组名.length] = 数据
  • 例:nums[nums.length] = 60

数组内置的方法

  • 数组本质也是一种对象,所以也有属性和方法

  • 属性

    • 数组.length
  • 方法:

    • reverse() :反转数组

    • push()

      • 在数组末尾添加元素
      • 也可以一次性添加多个,用逗号隔开
      • 返回值是新长度
    • pop()

      • 删除数组末尾的元素(一次只能删一个)
      • 返回值就是被删除的元素
    • unshift()

      • 在数组第一个位置添加元素
      • 也可以一次性添加多个,用逗号隔开
      • 返回值是新长度
    • shift()

      • 删除数组的第一个元素
      • 返回值是被删除的元素
    • join()

      • 把数组中每个元素用一个符号连接起来
      • 如果什么都不传,默认是逗号隔开
      • 如果传空字符串,那么每月任何隔开符,如果传空格字符串,用空格隔开
      • 返回一个字符串

数组方法

  • 数组排序方法

    • sort()

      • 默认是先比较第一位,再比较第二位,依次类推

      • 如果比较字符,它会先把字符根据ASCII码转整数,小的前面,大的后面

      • 按数字大小从小到大排列就传入函数

        • eg:

          数组.sort( function (a,b) {

          return a - b
          } )

      • 按数字大小从大到小排列就传入函数

        • eg:

          数组.sort( function (a,b) {

          return b - a
          } )

  • 数组splice

    • 删除

      • splice(从哪个下标开始,删除几个)

        • splice(2,3) 代表下标2开始删,删除3个
    • 替换

      • splice(从哪个下标开始,找几个,替换成什么)

        • splice(2,3,300) 下标2开始一共找3个,都只替换成一个300
        • 替换多个,也是逗号隔开
    • 新增

      • splice(新增到哪个下标,0, 新增的内容)
      • 如果要新增多个,则逗号隔开
  • 数组indexOf和lastIndexOf

    • indexOf(数据)

      • 从前往后找匹配的数据,返回找到的第一个的下标
      • 如果不存在得到-1
    • lastIndexOf(数据)

      • 从后往前找匹配的数据
      • 不存在也得到-1
    • 主要作用:判断数据在不在数组里面,如果不在得到-1,在就不等于-1

字符串的内置方法

  • indexOf和lastIndexOf

    • 跟数组一样的效果
    • 但是如果传入空字符串,则永远得到0
  • 字符串不可改!

    • 所谓的不可改是它的内容不可改,不是说变量不能重新赋值
    • eg:
  • spilt 切割

    • 把字符串按照某个符号切割为数组

    • eg:

      let str = ‘刘德华|张学友|郭富城|黎明’
      // 我需要把字符串转成数组
      let arr = str.split(’|’) // 按竖线分割字符串变成数组
      console.log(arr)

    • 如果传入不存在的字符,或者没传任何参数,那么会把字符串当做一个整体元素

    • 如果传入空字符串,会把字符串的每个内容都当做一个元素

  • replace 替换

    • 替换字符串
    • 参数1:被替换的内容
    • 参数2:替换的新内容
    • 只会得到新的结果,不会直接改变原来的值
  • toUpperCase() 转大写

  • toLowerCase() 转小写

  • trim () 取出两边的空格

arguments伪数组

  • arguments是一个伪数组,它里面保存了调用函数时传递过来的所有实参
  • arguments只能用在函数里
  • arguments作用:就是可以让函数的扩展性更强,因为可以让它传入任意个参数,我们都可以拿到并处理

函数

函数介绍

  • 就是一种把代码封装起来的语法
  • 作用:提高代码复用,便于维护与解决代码冗余

函数的基本使用

  • 声明

    • function声明

      • 语法1: function 函数名 () { 函数体 }
      • function 声明的函数,可以在声明之前调用
    • 表达式声明

      • 语法:let 变量名 = function 函数名 () { 函数体 }

      • 如果用这种方式声明的函数,不能再它声明之前调用

  • 调用语法:函数名 ()

  • 注:函数只声明不调用,里面代码不会被执行

有参数的函数

  • 有的时候函数完成某个功能需要外界传入数据,这时候就需要有参数的函数
  • 语法:function 函数名 ( 参数列表) { 函数体 }
  • 参数列表,就是定义需要几个数据,多个之间用逗号隔开

形参和实参

  • 形参

    • 声明函数时写在小括号里的叫形参
  • 实参

    • 调用函数时写在小括号里的叫实参

有返回值的函数

  • 为什么需要?

    • 因为调用函数时会得到一个结果,而这个结果是调用者想要得到的,所以应该把这个结果返回给调用者
  • 语法: function 函数名 ( 参数列表) { 函数体 return 数据 }

  • 可以返回变量值,也可以直接返回数据

return关键词

  • return后面可以直接写数据,也可以写变量(代表取出变量的值返回),也可以写表达式(算出表达式的结果再返回)
  • return有立即结束函数的作用,所以return后面的代码不会被执行
  • return后面也可以不加任何数据,也有返回值,只不过返回值是undefined
  • 函数内也可以不写return,只不过这个时候函数的返回值就是undefined

注意

  • 函数要想被调用,必须加小括号:函数名()
  • 换句话说,只要加了小括号,都代表调用了这个函数
  • 如果只是写函数名,不加小括号,不是调用,它只是代表找到这个函数里保存的代码

构造函数

工厂函数

  • 本质就是封装代码

  • 封装创建对象的代码,因为我们发现每次创建同一种类型的对象代码都一样,所以可以封装成函数

  • 那么这种函数就叫工厂函数

  • 特点:函数内自己创建对象,自己返回对象,调用时不用写new

  • eg:

    function factory(name, age, sex) {

    // 现在我要创建2个对象,都有name、age、sex属性,和吃饭的行为
    let p = {}

    // 左边是属性名,右边是变量名,到时候会取出变量名的值作为属性值
    p.name = name
    p.age = age
    p.sex = sex
    p.eat = function () {
    console.log(‘吃啊吃啊,我骄傲放纵’)
    }
    return p
    }

构造函数

  • 实际上构造函数就是对工厂函数的升级

  • 升级在:不用我们自己创建对象,也不用我们自己返回对象,利用this关键字访问到构造函数帮我们创建的对

  • 注意:构造函数的函数名首字母大写,尽量用名词

  • eg:

    function Person(name, age, sex) {

    // 现在这个对象是构造函数帮我们创建的,所以不叫p了
    // 访问创建的对象用this
    this.name = name
    this.age = age
    this.sex = sex
    this.eat = function () {
    console.log(‘吃啊吃啊,我骄傲放纵’)
    }
    }

    let p1 = new Person(‘jack’,16,‘男’)

  • new关键字做的三件事:

      1. 创建了一个新的空对象
      1. 把函数内的this指向到这个新的空对象
      1. 在函数结束的时候返回这个新的对象
  • 构造函数创建数组和对象

    • eg

      let arr = new Array()
      let obj = new Object()
      let f1 = new Function() //函数

字面量概念

  • 就是通过字面意思就能知道是什么数据类型
  • {}字面就是对象,[]字面就是数组

对象

什么是对象?

  • 对象这种数据类型一般是用来用代码描述现实中的某个具体事物
  • 对象也可以保存多个数据,并且取值时能很清晰的知道取的是什么
  • 对象跟顺序无关,所以我们一般称对象是无序存储,而数组叫有序存储

对象初始化

  • let 对象名 = { 属性名1 :属性值1 … }

  • 属性:对象拥有的特征

  • 方法:对象

  • 例:要用代码去表示一只猫

    • 特征:昵称、年龄、品种…
    • 行为:抓沙发

方法的说明

  • 方法就是对象的行为
  • 在代码中其本质是函数
  • 所以函数可以有参数和返回值,方法也有,写法跟函数一样

对象赋值的细节

  • 对象如果对一个已经存在的属性进行就是修改
  • 如果对一个不存在的属性进行赋值,就是增加

对象取值

  • 对象如果访问一个不存在的属性,得到undefined

  • 对象.属性名 这种形式叫点语法

    • 永远不会找变量,就是找这个对象里的这个属性
  • 对象[字符串]

    • 代表找字符串对应的整个属性
  • 对象[变量名]

    • 代表先取出变量的值,变量值是什么就找什么属性

遍历对象

  • 语法:for (let key in 对象名) { // 不一定要叫key,也可以叫别的,但是建议叫key或者 // key就是属性名,所以可以通过属性名访问属性值 对象名[key] }

内置对象

JS已经提供好的对象

Math对象

  • 这个对象表示的是一个数学高手

  • 它可以做一些数学运算

  • Math.pow(): 幂运算(基本不用,了解)

  • Math.abs():取绝对值

  • 取整的方法:

    • Math.round():- 四舍五入取整,本质是找离自己最近的整数,.5会取更大的整数
    • Math.floor(): 向下取整,得到的整数一定比原来的值要小
    • Math.ceil():向上取整,得到的整数一定比原来的值要大
    • parseInt() : 直接取整
  • 生成随机数

    • Math.random() : 生成 0 - 1之间的随机数,包括0,不包括1

    • 如果要生成任意整数之间的随机数,

      • 公式:Math.floor(Math.random() * ( 大 - 小 + 1)) + 小

日期对象

表示日期的对象

方法

  • getFullYear()

    • 获得年
  • getMonth()

    • 获得月,月从0开始
  • getDate()

    • 获得日期的日
  • getDay()

    • 获得星期几,星期天获得0,其他都是获得对应的数字
  • getHours()

    • 获得时
  • getMinutes()

    • 获得分
  • getSeconds()

    • 获得秒

日期对象创建时指定时间

  • 默认 new Date()什么都不传就获取当前时间

  • new Date() 如果依次传入年、月、日、时、分、秒,就是得到对应的时间,但是要注意传入0得到1月,传入1得到2月

    let time1 = new Date(1990, 0, 1, 12, 32, 45)
    console.log(time1.getFullYear(), time1.getMonth(), time1.getDate())
    console.log(time1)

  • new Date() 如果传入一个字符串时间,就是得到字符串内容的时间,更加精确

    let time2 = new Date(‘1990-2-1 12:32:45’)
    console.log(time2)

时间戳

  • 时间戳获取的是自 1970年1月1日0点0分0秒 到现在过了多少毫秒

  • 怎么获取?

    • 构造函数获取

      • Date.now()
    • 使用实例化的日期对象来获取

      • 对象.getTime()
      • 把 对象 转成 number 类型就能获取
    • 通过时间戳可以得到指定日期

  • 利用时间戳计算

    • 公式总结如下

      天 = parseInt (总毫秒 / (1000 * 60 * 60 * 24))

      总毫秒 = 总毫秒 % (1000 * 60 * 60 * 24) // 得到剩余毫秒

      时 = parseInt ( 总毫秒 / (1000 * 60 * 60) )

      总毫秒 = 总毫秒 % (1000 * 60 * 60 ) // 得到剩余毫秒

      分 = parseInt ( 总毫秒 / (1000 * 60) )

      总毫秒 = 总毫秒 % (1000 * 60 * 60 ) // 得到剩余毫秒

      秒 = parseInt ( 总毫秒 / 1000 )

    • 代码如下:

作用域

全局作用域

  • script 开头到 script 结尾的区域

局部作用域

  • 只有函数会开辟局部作用域,函数内的就叫局部作用域

块作用域

  • 任何 {} 都叫 块作用域,但是只有 let 声明的变量才区分块作用域,var只有全局和局部

全局变量

  • 在全局作用域里声明的变量叫全局变量
  • 任意范围可以访问

局部变量

  • 在函数内声明的变量叫局部变量
  • 只有这个函数内可以访问

块变量

  • {}里用let声明的变量叫块变量,只能在这个大括号里访问
  • var不存在块作用域,只有let存在
  • eg:

作用域链

  • 只有函数可以开辟作用域
  • 默认也有作用域叫 全局作用域 ,我们也称之为0级作用域
  • 如果在0级作用域里声明一个函数,那么这个函数开辟的作用域就叫1级作用域
  • 如果在1级作用域里又声明一个函数,那么这个函数开辟的作用域就叫2级作用域
  • 练习:

预解析以及声明的提升

  • 练习

let 和 var 的异同总结

  • 相同点:都是声明变量

  • 不同点

    • let只认块,也就是 {} ,在哪个大括号里声明,就只能在这个大括号里使用
    • var认作用域,所以var里会分0级、1级、2级,还要注意,只有function才可以开辟作用域
    • var参与预解析时的变量提升,而let不参与
    • let在同一个块里不能声明同名变量,var无所谓

回调函数,自执行函数