函数介绍(2)

79 阅读7分钟

作用域

  • 什么是作用域?
    • 简答一句话概述:变量生效的范围
    • 变量不是在所有地方都是可以使用的,而这个变量的可使用范围 就是作用域

全局作用域 && 局部作用域

  • 全局作用域
    • 最大的作用域
    • 在全局作用域中定义的变量可以在任何地方使用
    • 页面打开时浏览器给我们生成了一个全局作用域 window 这个作用域一直存在,直到浏览器页面关闭时才会销毁
    • var num = 100; var num2 = 200 这两行代码的变量就是存储在全局作用域下面,可以在任意地方去使用
  • 局部作用域
    • 局部作用域就是在全局作用域内的某一个地方,相对比较小的一些作用域
    • 在局部作用域中定义的变量只能在当前的这个作用域内部使用,在全局作用域或者其他的局部作用域中不可以使用
    • 在 JS 中,只有函数能生成一个局部作用域,别的都不行
    • 简单来说,每一个函数内部都是一个局部作用域
    var num = 100 // 全局作用域下声明的变量,在任何地方都可以使用
    
    function fn() {
        var num2 = 200  // 在当前位置声明的变量都是 fn 函数局部作用域内的变量, 也就是说只能在当前 fn 函数内部使用
    }
    

作用域链

  • 作用域链是一个纯概念性的东西
  • 当我们在某一个作用域内获取某一个变量时
  • 会先在当前作用域查找,找到直接拿来用,没找到会向上层查找
  • 如果上层作用域也没找到,那么会继续向上层作用域的上层作用域查找,一直到查找到全局作用域
  • 这样一层一层向上查找构成一个链条(假设有,不是真的有链条),我们叫做作用域链
var num = 100;

function fn() {
    var num2 = 200

    function fun() {
        var num3 = 300

        console.log(num3)   // 自己当前作用域就有(fun函数内部),拿过来直接用
        console.log(num2)   // 自己当前作用域没有(fun函数内部),去上一层查找,发现有(fn函数内部),拿过来用
        console.log(num)    // 自己当前作用域没有(fun函数内部),去上一层查找,发现没有(fn函数内部),继续向上一层作用域查找,发现有(全局作用域),直接用
        console.log(a)  // 自己没有,一级一级向上查找,到全局作用域还是没有,报错
    }
}

变量使用规则

访问规则

  • 变量的访问规则 也叫做 作用域的查找机制
    • 作用域的查找机制只能向上查找,不能向下查找
function fn() {
    var num = 100
}
fn()

console.log(num)    // 全局作用域没有,相当于已经查找到顶层,所以报错找不到,不会向 fn的局部作用域查找

赋值规则

  • 当我们想要给某一个变量赋值的时候,就要先找到这个变量,然后再给它赋值
  • 赋值规则
    • 先在自己作用域内部查找,有就直接赋值
    • 没有就去上一级作用域内部查找,有就直接赋值,如果没有继续向上一级作用域内部查找
    • 如果一直找到全局作用域都没有,那么就把这个变量定义为全局变量,再给他赋值
function fn() {
    num = 100
}
fn()

/**
 * fn 调用以后,要给 num 赋值
 * 查找自己作用域没有后,向上层查找
 * 上层就是全局作用域,发现还是没有
 * 那么会把 num 定义为全局的变量,并给它赋值
 * 所以 fn 函数调用后,全局就有了一个变量叫做 num 并且值是 100
*/

递归函数

  • 什么是递归?
    • 在编程世界中,递归就是一个自己调用自己的手段
    • 递归函数:在一个函数内部,调用了自己,循环往复
/**
 * 这就是一段简单的递归,在函数内部调用了自己,函数一执行,就调用自己一次,在调用在执行,循环往复没有尽头
*/
function fn() {
    fn()
}
fn()
  • 其实递归函数和循环很类似
    • 需要有初始化,自增,执行代码,条件判断
    • 如果没有就会是一个没有尽头的递归函数,我们通常叫这种为 死递归

写一个简单的递归

/**
 * 求 1 至 5 的和
 * 
 *  1 + 2 = 3
 *  3 + 3 = 6
 *  6 + 4 = 10
 *  10 + 5 = 15
*/

// 写递归的第一步,先写结束条件(为了避免出现死递归)
function add (n) {
    if (n == 5) {
        return 5
    }
}
add(1)

// 第二步,写不满足结束条件时的递归操作
function add (n) {
    if (n == 5) {
        return 5
    } else {
        return n + add(n + 1)
    }
}
add(1)

简单了解对象

  • 什么是对象
    • 首先排除男女朋友, 我们这是正经编程
    • 对象是一个 复杂数据类型, 也叫做 引用数据类型
    • 虽然我们说是复杂类型, 但是也没有很复杂, 只不过是存储了一些基本数据类型的集合
var obj = {
    num: 100,
    str: 'hello',
    boo: true
}
  • 这里的 {} 和函数中的 {} 不一样, 函数内部书写代码, 对象里面是书写数据的
  • 对象就是一个键值对的集合
    • 什么是键值对?
      • 对象 obj 中, num 是键, 100 是值
      • 对象 obj 中, str 是键, 'hello' 是值
      • 对象 obj 中, boo 是键, true 是值
    • 其实就是我们准备一个房子, 把我们想要的数据放进去, 然后把房子的地址给到变量名, 当我们需要某一个数据的时候, 就可以根据变量名里面存储的地址找到对应的房子, 然后去房子里面找到对应的数据

创建对象* 字面量方式创建对象

* 语法:`var obj = {}   var obj1 = {键值对, 键值对}`
  • 内置构造函数创建
    • 语法:var obj = new Object()
  • 对象内对于 键(key) 的要求
    • 推荐使用符合变量命名规则和规范的名字
    • 可以使用纯数字当作 键名
      • 这种情况下该属性会排列在最前面
    • 可以使用任何特殊符号
      • 使用特殊符号的时候,在书写时需要被引号包裹

对象数据类型的操作(增删改查)两种语法

  • 点语法
    var obj = {}
    obj.name = 'qianfeng'   // 增
    obj.name = 'qianfeng123'    // 改
    console.log(obj.name)       // 查
    delete obj.name         // 删
    
  • 中括号语法(数组语法)
    var obj = {}
    obj['name'] = 'qianfeng'        // 增
    obj['name'] = 'qianfeng123123'  // 改
    console.log(obj['name'])        // 查
    delete obj['name']              // 删
    
  • 两者的差异
    • 符合变量命名规范与规则的情况,两者并无差异
    • 操作不符合变量命名规范与规则的名称时,比如纯数字或者带有特殊符号的,就只能用中括号语法
    • 涉及变量相关的时候,只能使用中括号语法

for in 循环遍历对象

for (var k in obj) {
    console.log('我执行了', k, obj[k])
}

数据类型赋值的区别

  • 基本数据类型:赋值以后,两个变量没有关系了
  • 复杂数据类型:赋值以后,两个变量操作一个存储空间
  • 两种数据类型存储的区别
    • 基础数据类型直接存储在 栈内存中
    • 复杂数据类型会将数据本体存在堆内存中,变量名存储在堆内存中,变量名内部存储着指向堆内存的地址

数组数据类型

创建数组数据类型

数组的 length 属性

数组的 索引 概念

for 循环遍历数组

不同数据类型的存储(堆和栈)