作用域的特点
作用域的机制
作用域是针对变量的起作用的范围。
而变量又分为“使用变量”和“赋值变量”
使用变量
又叫做访问变量 出现在表达式中,赋值语句右侧。
访问变量规则: 当访问变量的时候,会先查看当前作用域中是否存在该变量,如果有,就使用。如果没有,就将会向上一层级作用域中寻找。依次向上,直到找到,或者到了全局作用域中还没有找到,就会报错。
赋值变量
也叫做修改变量 只出现在赋值语句左侧
赋值变量规则: 当对一个变量进行赋值的时候,会先查看当前作用域中是否存在该变量,如果有,就赋值。如果没有,就向上一层级查找。依次向上,直到找到,或者到了全局作用域中还没有找到,就会在全局作用域中悄悄的声明这个变量并赋值。
千万不要使用这个特点 切记,切忌。
// 当前是全局作用域
var num = 100; // 在全局作用域中定义的变量
// 定义一个函数
function demo() {
var num = 101; // 这是在函数作用域中定义的变量
console.log(num);
}
// 执行函数
demo(); // 101
解释: 当在函数内部访问变量num的时候,先看当前作用域中是否有num 找到了! 于是就用101 输出101
var num = 100; // 在全局作用域中定义的变量
// 定义一个函数
function demo() {
// var num; // 因为声明提升的原因 代码其实是这样子的
console.log(num);
var num = 101; // 这是在函数作用域中定义的变量 虽然定义是在这里 但是提升了
}
// 执行函数
demo(); // undefined
解释:依旧是作用域的问题,但是因为输出代码在前面,所以有些同学可能会误以为会输出100 但是不要忘记 预解析!!!
var num = 100; // 在全局作用域中定义的变量
// 定义一个函数
function demo() {
var num = 101;
}
// 执行函数
demo();
// num是多少
console.log(num); // 100
解释:作用域的问题,因为输出的代码是在全局作用域中的,所以会直接访问全局中的变量 而与函数内部的变量无关
var num = 100; // 在全局作用域中定义的变量
// 定义一个函数
function demo() {
num = 101;
}
console.log(num); // 100
// 执行函数
demo();
// num是多少
console.log(num); // 101
读代码:
1 提升
2 给全局中的num赋值为100
3 执行函数
将num赋值为101 问题的关键在于:修改的这个num到底是不是全局中的?
答案就是: 依旧是就近原则, 如果当前作用域中有声明的num变量
就修改它 如果没有向上寻找 如果找到了就修改它 如果没有找到再向上寻找
重复这个过程 直到找到或者到了全局作用域中还没有找到,就会在全局中声明这个变量并赋值
// 如果一个变量没有通过var声明 就直接赋值
num = 101; // 这种不经过声明就直接赋值而且还不会报错的特点属于JS的糟粕之一。一定不要使用。
console.log(num); // 101
console.log(num);
num = 101;
这种情况下会报错 因为num没有经过定义就被使用
疑问:
console.log(num)在使用num
难道 num = 101 不是在使用num吗?
为什么第一条console代码就会报错 而num = 101就不会报错?
因为第一个是在“访问”变量 而第二个是在“赋值”
function demo() {
num = 11;
}
demo();
console.log(num);
解读: 当demo执行的时候, num = 11 被执行 先查找num是否在当前作用域中存在,结果发现没有
没有就向上寻找,到达了全局作用域,也没有,于是就在全局作用域中,声明了该变量 所以当输出num时候,已经声明并赋值完毕
所以可以输出
当demo不执行的时候,就不会执行到这条代码 于是整个过程不会发生, num就没有被声明在全局 此时输出num时,是在访问num变量
会报错
数组
装载一组数据的容器
数组的定义
方式1 字面量
语法:
var arr = [];
方式2 构造函数
语法:
var arr = new Array();
传参规则:
当没有参数时, 定义的是一个空数组
当参数只有一个并且是数字时,该数字表示数组的长度
当参数有多个时,参数就是每一个成员
方式3 构造函数
语法:
var arr = Array();
传参规则: 同上
数组的使用
存储数据
数据是以一定的顺序存储在内部 每一个数据都有一个"序号" 这个序号叫做下标 或者 索引
语法:
数组[下标] = xxx
访问数据
语法:
数组[下标]
当该代码单独出现,就表示要使用数据。就会去读取对应的数据。如果数据存在,就得到,如果不存在就读取不到,返回undefined。
数组的length属性
数组是一组数据,length属性表示这个数组中的内容的个数。简称数组的长度。
该长度是可读可写的属性。
当设置属性的时候,表示要修改该数组的长度。数组的长度会发生变化。
当读取时,会实时返回数组的当时的长度。
var arr = [1, 2, 3, 4];
console.log(arr.length); // 4
arr.length = 1; // 修改数组的长度
console.log(arr); // 输出 [1]
console.log(arr.length); // 1
对象的使用
对象: 是一组k:v对的集合。
每一组k:v对,叫做对象的属性。 k表示对象的属性名,v表示对象的属性值。
每一组k:v对,使用逗号隔开,推荐一行一个k:v对
k最好符合变量的命名规范。 v是具体的数据。可以是任何数据。
定义对象:
方式一: 字面量 (推荐方式)
var obj = {}; // 字面量定义对象
// 定义对象并设置属性
var obj1 = {
name: "张三"
};
方式二: 构造函数
var obj = new Object();
var obj = Object();
对象的属性操作:
读取属性:
方式1 点语法
var obj = {
name: "张三"
}
console.log(obj.name); // 张三
方式2 方括号语法
var obj = {
name: "张三"
}
console.log(obj["name"]); // 张三
设置属性:
方式1 点语法
var obj = {
name: "张三"
}
obj.age = 12;
console.log(obj); // {name: "张三", age: 12}
方式二 方括号语法
var obj = {
name: "张三"
}
obj["age"] = 13;
console.log(obj); // {name: "张三", age: 13}
删除属性
delete关键字 该关键字只能用来删除对象的属性 而不可以用来删除变量
var obj = {
name: "张三"
}
delete obj["name"];
console.log(obj); // {}
对象的循环遍历
语法: for (var i in obj) {}
var obj = {
name: "小明",
age: 13,
sex: "男",
married: false
}
// 使用for in 循环遍历对象
for (var i in obj) {
// i 是对象的属性名
// obj[i] 是对象的属性值
}
注:obj.i 这种方式,真的是在读取 obj的 i 属性 而不是读取i变量所保存的值属性
方括号语法与点语法的区别
方括号语法:
[] 之间是一个JS的执行环境,可以使用变量,表达式
[] 最终需要的是一个字符串,如果不是字符串,会转为字符串(调用toString方法)
[] 只要内容是字符串,不论是否符合变量命名规范,都可以成功
点语法:
点语法,点什么,就是什么
点语法,必须符合变量的命名规范
var a = "name";
var obj = {
name: "张三",
a: 123
}
点语法:
console.log(obj.name); // "张三"
console.log(obj.a); // 123
方括号语法:
console.log(obj[a]); // "张三"
console.log(obj["a"]); // 123