预解析、作用域、变量的三个机制、对象、数组
一、预解析
1.预解析概念
+ 不是教给你怎么写代码
+ 告诉你代码的执行机制, 和不要怎么写代码
+ 预: 预先, 在所有代码执行之前
+ 解析: 解释, 对代码进行通读并解释(只是把整体代码当作一个文档)
2.解释了什么东西
+var 关键字
=>会把var关键字定义的变量在代码块执行之前声明
+ 声明式函数
=>会把这个函数名在所有代码块执行之前声明,并且赋值为一个函数
注意:赋值式函数
=>var fn = function () {}
=>按照 var 的规则进行解析
3.扩展
报错信息
+ Uncaught TypeError: fn is not a function
+ 当你看到它的时候
=> 我前面的代码是不是用过 fn 这个变量了
4.预解析的无节操
(1). 不管 if 条件是不是成立,里面的代码都会进行预解析
(2). return 后面的代码虽然不执行,但是会进行预解析
二、作用域
1.作用域的定义
作用域
+ 教给你怎么写代码
+ 作用: 生效, 有用
+ 域: 范围
+ 作用域: 变量(变量名, 函数名)生效的范围
2.分类(2种)及上下级关系
-
全局作用域
+ 打开的一个页面就是一个全局作用域 + 全局作用域,叫做 window
-
私有作用域 (局部作用域)
+ 只有函数生成私有作用域 + 每一个函数就是一个私有作用域
-
作用域的上下级关系
你的函数写在哪个作用域下,你就是谁的子级作用域
-
作用域的上下级关系有什么用
+ 为了确定变量的范围 + 三个机制 + 1.变量定义机制 + + 有 var 关键字 var num + + 声明式函数 function fun() {} + 2.变量使用机制 + + 需要拿到某一个变量来使用 + + n++ + 3.变量赋值机制 + + 一定要有赋值符号 num = 100
function fn() {
// 这个位置就是 fn 私有作用域
function a() {
// 这个位置式 fn 私有作用域下的 a 私有作用域
}
}
function fun() {
// 这个位置就是 fun 私有作用域
}
var f = function () {
// 这个位置就是 f 私有作用域
}
3.作用域里面的预解析
-
作用域里面的预解析
+ 预解析分成几个阶段 1. 全局预解析 2. 私有作用域预解析 + 全局预解析 => 会在页面打开的时候就进行了 => 只解释属于全局的内容 + 私有作用域的预解析 => 当函数执行的时候 => 进行预解析 => 函数内部的预解析, 只属于函数内部
-
问题:
+ 函数执行的时候, 会进行形参赋值 + 函数执行的时候, 会进行预解析 + 一旦函数的形参和定义的私有变量重名 => 先预解析还是先形参赋值 + 结论: => 因为函数是单独进行预解析 => 在函数执行的时候, 先进行形参赋值, 在进行预解析
三、变量的三个机制
-
变量的定义机制
+ 一个变量(函数) + 定义在哪一个作用域里面 + 只能在当前作用域, 或者下级作用域里面使用 + 上一级作用域不能使用
-
变量使用机制
+ 当你需要使用一个变量(函数) + 会首先在自己作用域内查找, 如果有, 直接使用 + 如果没有, 取上一级作用域查找, 如果有, 直接使用 + 如果还没有, 再去上一级查找 + 直到 window 都没有, 那么就 **报错**
-
变量赋值机制
+ 当你需要给一个变量(函数名)赋值的时候 + 会首先在自己作用域内查找, 如果有, 直接赋值 + 如果没有, 取上一级作用域查找, 有就赋值 + 还没有, 再去上一级作用域查找, 有就赋值 + 直到 window 都没有, **把这个变量定义为全局变量, 在进行赋值**
四、对象
1.对象数据类型
-
(1). 对象数据类型
+ JS 数据类型的一种 + 一个复杂数据类型 Object + 函数: 一个盒子, 承载一段代码 + 对象: 一个盒子, 承载一堆数据 + 本质: 抽象的描述一个事物
-
(2).对象的创建方式
1. 字面量创建 => var o = {} 2. 内置构造函数创建 => JS 给我们提供了一个内置构造函数叫做 Object => var o = new Object()
1. 字面量创建对象
var o = {
}
console.log(o)
2. 内置构造函数创建对象
var o2 = new Object()
console.log(o2)
3. 字面量创建直接添加成员
=>表示我的对象里面有两个成员
=>一个是 num 成员, 他的值是 100
=>一个是 str 成员, 他的值是 'hello world'
=>管他们叫做对象的属性
=>一个是 fn 成员, 他的值是一个 函数
=>管他们叫做对象的方法
var o = {
num: 100,
str: 'hello world',
fn: function () {
console.log('我是 o 的 fn 函数') }
}
console.log(o)
-
(3).两种创建方式的区别
1. 字面量创建可以在创建的时候就向直接添加一些数据 => 数据是以键值对的形式出现 => key: value 的形式 => key 就是这个数据的名字 => value 就是这个数据的值 => 多个数据之间使用 逗号(,) 分隔 2. 内置构造函数目前我们不好直接添加成员 => 直接创建一个空对象 => 后期通过对象的操作语法来进行增删改查
2.对象的操作语法及区别
-
对象的操作语法
+ 增: 向对象里面添加一个成员 + 删: 删除对象里面的一个成员 + 改: 修改对象里面的一个成员 + 查: 获取对象里面某一个成员的值 + 怎么创建的对象无所谓, 就是操作对象的
-
语法有两种
1. 点语法 1-1. 添加一个成员 => 语法: 对象名.成员名 = 值 1-2. 删除一个成员 => 语法: delete 对象名.成员名 1-3. 修改一个成员 => 语法: 对象名.成员名 = 值 => 原先有就是修改, 原先没有就是添加 1-4. 查询某一个成员的值 => 语法: 对象名.成员名 => 当你访问一个对象里面没有的成员的时候 => 会给你一个 undefined 2. 数组关联语法 1-1. 添加一个成员 => 语法: 对象名['成员名'] = 值 1-2. 删除一个成员 => 语法: delete 对象名['成员名'] 1-3. 修改一个成员 => 语法: 对象名['成员名'] = 值 => 原先有就是修改, 原先没有就是添加 1-4. 获取一个成员的值 => 语法: 对象名['成员名']
-
注意:
+ 因为对象数据类型是一个复杂数据类型 + 在控制台打印的时候, 会出现两种情况 + 现在的样子和最终的样子 => 在控制台上, 你不展开对象数据类型的时候, 是当前的样子 => 在控制台上, 你展开对象数据类型以后, 就是最终的样子 + 解决问题 1. 你直接打印你想看到的值 2. console.table() (在控制台以表格的形式打印)
点语法
0. 创建一个对象
var o = {
}
console.table( o)
1. 点语法操作对象
1-1. 添加一个成员
向对象里面添加了一个叫做 name 的成员, 值是 'Jack'
o.name = 'Jack'
o.age = 18
console.table( o)
1-2. 删除一个成员
删除对象中的 name 成员
delete o.name
console.table( o)
1-3. 修改一个成员
把 o 对象内的 age 成员修改成 20
o.age = 20
1-4. 访问对象内的某一个成员
我要拿到 o 对象内的 age 成员存储的值, 打印出来
console.log(o.age)
console.table( o)
数组关联语法
var o = new Object()
1. 操作对象
1-1. 添加一个成员
o['name'] = 'Jack'
o['age'] = 18
1-2. 删除一个成员
delete o['name']
1-3. 修改一个成员
o['age'] = 20
1-4. 获取一个成员的值
console.log(o['age'])
console.log(o)
-
对象两种操作语法的区别
+ 点语法 => 不能使用变量 => 不能拼接字符串 => 点的后面是什么, 这个成员名称就是什么 + 数组关联语法 => 可以使用变量 => 可以拼接字符串
var obj = {
}
var name = 'abc'
// 添加一个叫做 name 的成员
// 就是在添加一个叫做 name 的成员, 和 name 变量没有任何关系
obj.name = 'Jack'
// 添加一个叫做 age 的成员
obj['age'] = 18
// 当你中括号里面书写一个变量的时候
// 会把变量的值当作这个成员名称
// 因为 name 变量的值是 'abc'
// 下面代码等价于 obj['abe']
obj[name] = 'hello world'
for (var i = 1; i <= 100; i++) {
obj['o' + i] = i
}
console.log(obj)
// 同样是一个变量使用
// 你是要使用 age 的值, 来当作 obj 的一个成员名称
obj[age] = 20
console.log(obj)
3.循环遍历对象
-
循环遍历对象
+ 遍历过 arguments => 因为 arguments 的排序是一组有规律的数字 => for 循环能给我们提供一组有规律的数字 => arguments 的索引(下标) 可以访问到里面的某一个成员 + 遍历 对象 => 成员名可以访问当对象里面的每一个数据 => 所有的名字不一定存在规律 + 我们有一个叫做 fon in 的循环 => 大部分时候用来遍历对象的 => 语法: for (var 变量 in 对象) { 重复执行的代码 } -> 根据对象内有多少个成员执行多少回 -> 循环的每一次, key 分别是对象的成员名称(字符串类型) -> 就可以利用 key 和 数组关联语法 来获取每一个成员的值
var obj = {
name: 'jack',
age: 18,
gender: '男',
score: 100
}
// for in 遍历对象
for (var key in obj) {
console.log('我执行了')
console.log(key)
// 当 key === 'name' 的时候, 就是 obj['name']
// 当 key === 'age' 的时候, 就是 obj['age']
console.log(obj[key])
}
/*
判断一个成员是不是在这个对象里面
+ 使用 in 语法
+ 成员名 in 对象名(以字符串的形式书写)
+ 对象内的每一个成员名称都必须是字符串
*/
-
扩展
+ window 的 name 属性 => 是一个全局天生自带的属性 => 作用: 在 iframe 标签和 name 属性合作进行跨域的 => 特点: 被固定为字符串类型了 -> 不管你给 name 赋值为什么数据类型 -> 他都会自动转换成 字符串
4.数据类型存储的区别、赋值上的区别
-
数据类型存储的区别
+ 数据类型分成两种 => 基本数据类型 1. Number 数值 2. String 字符串 3. Boolean 布尔 4. Undefined 空 5. Null 空 => 复杂数据类型 1. Function 函数 2. Object 对象 + 存储上是有区别的 + JS 打开的内存空间 => JS 是一个脚本语言, 依赖于浏览器执行 => 本质是依赖浏览器里面的 JS 解析引擎 => JS 本身不打开内存空间 => 因为浏览器在你的电脑上运行的时候, 会占用一段内存空间 => JS 就是在这一段内存空间里面运行的 => 数据类型的存储, 就是存储在浏览器分配给 JS 存储的一段空间 + 浏览器的一段存储空间 1. 栈内存 => 存储机制, 先来进栈底 2. 堆内存 => 存储机制, 随机存储 + 数据类型的存储 1. 基本数据类型 => 直接把值存储在栈内存里面 2. 复杂数据类型(地址数据类型 / 引用数据类型) => 把数据放在了堆内存里面 => 把地址放在栈内存的变量里面 => 我们管这个地址叫做引用 + 代码的执行 => 我们只能直接访问当栈里面的内容 => 你要想访问某一个对象里面的成员 => 因为对象本身在堆内存里面 => 我们就需要利用栈里面的地址, 找到堆里面的空间 => 然后取访问内部的成员
var num = 100
var str = 'hello'
var obj = {
name: 'Jack',
age: 18
}
console.log(obj.name)
console.log(obj.age)
console.log(age)
-
数据类型赋值的区别
1. 基本数据类型 => 就是把变量存储的值直接赋值给另一个变量 => 赋值过后两个变量没有关系了 2. 复杂数据类型 => 因为复杂数据类型中, 变量存储的是地址 => 赋值的时候, 实际上是把一个变量的地址给了另一个变量 => 赋值过后, 两个变量操作一个空间 3. 函数的形参和实参的关系 => 实参就是在函数调用的时候给形参赋值 => 实参和形参的交互, 和变量赋值时一个道理 4. 函数的返回值也是变量赋值的一种 => 返回值是吧函数内部的数据 return 出去 => 在函数外面有一个变量接收
5.函数也是一个对象
-
函数也是一个对象
+ 函数是保存一段代码 + 对象是保存一段数据 + 函数本身也是一个对象, 可以保存一堆数据 + 函数的存储空间
-
函数
+ 当你定义号一个函数以后 + 函数就有两个功能 1. 函数名() => 把函数当作一个函数来执行掉 2. 函数名.成员名 = '值' => 存储一些数据 + 这两个功能互相不干扰 => 也没有关系
五、数组
1.数组数据类型
-
数组数据类型
+ 也是 JS 的一种数据类型 + 也是复杂数据类型 Array + 一个盒子: 存储一堆数据 => 不是按照键值对存储 => 按照索引进行存储的(序号)
-
数组的创建
1. 字面量创建 => var arr = [] 2. 内置构造函数创建 => JS 给我们提供了一个内置构造函数 Array => var arr = new Array()
-
创建的时候直接添加一些成员
1. 字面量 => 直接写在中括号里面, 多个数据使用 逗号(,) 分隔 2. 内置构造函数 2-1. 不传递参数 => 创建一个空数组 2-2. 传递一个正整数 => 这个参数表示数组的长度 2-3. 传递多个数据 => 每一个数据都是放在数组里面的数据 => 没有表示长度的数据了
1. 字面量创建
var arr = []
console.log(arr)
2. 内置构造函数创建
var arr = new Array()
console.log(arr)
3. 字面量创建直接添加数据
var arr = ['hello', 'world', '你好', '世界']
console.log(arr)
4. 内置构造函数直接添加数据
4-1. 不传递参数
var arr = new Array()
console.log(arr)
4-2. 传递一个数字
var arr = new Array(100)
console.log(arr)
4-3. 传递多个数据
var arr = new Array(10, 20, 30)
console.log(arr)
2.数组的操作
-
数组有一个 length 属性
=> 是一个读写的属性 -> 读: 获取数组的长度 -> 写: 设置数组的长度 => 当你设置的比本身长度小, 那么就相当于删除 => 当你设置的比本身长度大, 那么多出来的就是用空补齐
-
数组的排列
=> 按照索引进行排列的 => 索引: -> 从 0 开始, 依次 +1 -> 最后一位的索引, 就是 length - 1 => 索引也是一个读写的属性 => 读: 读取到指定索引位置的数据 -> 如果数组确实有这个索引位置, 那么就是这个位置的数据 -> 如果数组没有这个索引位置, 那么就是 undefined => 写: 设置指定索引位置的数据 -> 如果有这个索引位置, 那么就是给这个索引位置设置 -> 如果没有这个索引位置, 那么就是添加 -> 如果这个数字超出 length 很多, 那么中间的位置用空补齐
-
数组的遍历
=> 因为数组是按照索引进行排列的 => for 循环能给我们提供一组有规律的数字 => 使用 for 循环遍历一个数组
for (var i = 0; i < arr.length; i++) {
console.log(arr[i])
}
-
数组也是一个对象
=> 数组除了可以按照索引排列一些数组 => 还可以当作对象使用, 使用 点语法 存储一些数据 => 你把数组当作对象使用的时候, 他的成员是不占用 length 位置的 => 当你把数组当作对象使用 for in 循环来遍历的时候 -> 里面的每一个索引位置, 每一个 key 都会遍历出来 => 一般不会那他当作对象使用
// 数组当作对象
// 当一个数组创建号以后, 他这个名字就可以当作对象使用存储数据
arr.age = 18
arr['gender'] = '男'
// age 和 gender 成员不影响循环遍历数组
for (var i = 0; i < arr.length; i++) {
console.log(arr[i])
}
// for in 循环
// 是只要有一个 成员名称 在这个 arr 里面就执行一会
for (var key in arr) {
console.log(arr[key])
}
console.log(arr)