学习完了函数,现在对 作用域、预解析、对象这三个重要内容加以学习。
一、作用域
1.1 作用域定义
什么是作用域?一段程序代码中所用到的名字并不总是有效和可用的,而限定这个名字的可用性的代码范围就是这个名字 的作用域。作用域的使用提高了程序逻辑的局部性,增强了程序的可靠性,减少了名字冲突。
一般分为两类:全局作用域、局部作用域。
1.2 全局作用域
定义:作用于所有代码执行的环境(整个 script 标签内部)或者一个独立的 js 文件。
1.3 局部作用域
定义:作用于函数内的代码环境,就是局部作用域。 因为跟函数有关系,所以也称为函数作用域。
1.4 JS没有块级作用域
在其他编程语言中(如 java、c#等),在 if 语句、循环语句中创建的变量,仅仅只能在本 if 语句、本循环语 句中使用。但是JS(ES6之前)没有这一要求。
二、变量的作用域
在JavaScript中,根据作用域的不同,变量可以分为两种:全局变量、局部变量。
2.1 全局变量
定义:在全局作用域下声明的变量叫做全局变量(在函数外部定义的变量)。
1、全局变量在代码的任何位置都可以使用
2、在全局作用域下 var 声明的变量 是全局变量
3、特殊情况下,在函数内不使用 var 声明的变量也是全局变量(不建议使用)
2.2 局部变量
定义:在局部作用域下声明的变量叫做局部变量(在函数内部定义的变量)
1、局部变量只能在该函数内部使用
2、在函数内部 var 声明的变量是局部变量
3、函数的形参实际上就是局部变量
2.3 全局变量和局部变量的区别
全局变量:在任何一个地方都可以使用,只有在浏览器关闭时才会被销毁,因此比较占内存 局部变量:只在函数内部使用,当其所在的代码块被执行时,会被初始化;当代码块运行结束后,就会被销毁,因此更节省内存空间
三、作用域链
只要是代码都一个作用域中,写在函数内部的局部作用域,未写在任何函数内部即在全局作用域中;如果函数中还有 函数,那么在这个作用域中就又可以诞生一个作用域;根据在**[内部函数可以访问外部函数变量]**的这种机制,用 链式查找决定哪些数据能被内部函数访问,就称作作用域链。
注意:内部函数访问外部函数的变量,采取的是链式查找的方式来决定取那个值 这种结构我们称为作用域链 就近原则!!!
我们可以借助这里例子学习作用域链:
1、num =10、 fun1 在全局下使用属于0级
2、num=20、fun2 在fun1定义域下的定义的属于1级
3、console.log(num); 在fun2义域下的定义的属于2级
<script>
// 作用域链 : 内部函数访问外部函数的变量,采取的是链式查找的方式来决定取那个值 这种结构我们称为作用域链 就近原则
//全局0级
var num = 10;
function fun1() { // 外部函数定义
//局部1级
var num = 20;
function fun2() { // 内部函数定义
//局部2级
console.log(num);
}
//局部1级
fun2();
}
//全局0级
fun1();
// 内部函数访问外部函数的变量,采取的是链式查找的方式来决定取那个值 这种结构我们称为作用域链 就近原则!!
</script>
可能理解的还不是很透彻,现在用下面的例子进一步学习:
<script>
// 案例1 : 结果是几?0
function f1() {
var num = 123;
function f2() {
var num = 0;
console.log(num); // 站在目标出发,一层一层的往外查找
}
f2();
}
var num = 456;
f1();
// 案例2 :结果是几?
var a = 1;
function fn1() {
var a = 2;
var b = '22';
fn2();
function fn2() {
var a = 3;
fn3();
function fn3() {
var a = 4;
console.log(a); //a的值 ? 4
console.log(b); //b的值 ? '22'
}
}
}
fn1();
</script>
四、 预解析
4.1 预解析定义
为什么学习预解析?因为JavaScript 代码是由浏览器中的 JavaScript 解析器来执行的。JavaScript 解析器在运行 JavaScript 代码的时候分为两 步:预解析和代码执行。
预解析:在当前作用域下, JS 代码执行之前,浏览器会默认把带有 var 和 function 声明的变量在内存中进行提 前声明或者定义。预解析也叫做变量、函数提升。
代码执行: 从上到下执行JS语句。
预解析会把变量和函数的声明在代码执行之前执行完成。
4.2 变量预解析
变量提升(变量预解析): 变量的声明会被提升到当前作用域的最上面,变量的赋值不会提升。
注意:变量提升只提升声明,不提升赋值!!!
console.log(num); // 结果是多少?
//这里不会报错,因为存在var
//但是结果显示未定义,因为赋值不提高
var num = 10;
4.3 函数预解析
函数提升: 函数的声明会被提升到当前作用域的最上面,但是不会调用函数。
注意:函数声明代表函数整体,所以函数提升后,函数名代表整个函数,但是函数并没有被调用!!!
//会正常打印
fn();
function fn() {
console.log('打印');
}
4.4 函数表达式声明函数(匿名函数)问题
函数表达式创建函数,会执行变量提升,此时接收函数的变量名无法正确的调用。
//不能正常输出,显示函数未定义
fn();
var fn = function() {
console.log('打印');
}
// 相当于执行了以下代码
// var fn;
// 调用 fn();
// 还没有这个函数所以报错
//现在才执行
fn = function() {
console.log('打印');
}
解释:该段代码执行之前,会做变量声明提升,fn在提升之后的值是undefined;而fn调用是在fn被赋值为函数体之 前,此时fn的值是undefined,所以无法正确调用。
4.5 预解析案例(面试的考核点之一,考察对JS的理解)
案例1:
<script>
// 案例1
// 执行结果:Undefined
var num = 10;
fun();
function fun() {
console.log(num);
var num = 20;
}
// // 相当于执行了以下操作
// // var num;
// // function fun() {
// // var num;
// // console.log(num);
// // num = 20;
// // }
// // num = 10;
// // fun();
</script>
案例2:
<script>
// 案例2
//输出结果 Undefined 20
var num = 10;
function fn() {
console.log(num);
var num = 20;
console.log(num);
}
fn();
// // 相当于以下代码
// // var num;
// // function fn() {
// // var num;
// // console.log(num);
// // num = 20;
// // console.log(num);
// // }
// // num = 10;
// // fn();
</script>
案例3:
<script>
// 案例3
//输出结果: Undefined 9
var a = 18;
f1();
function f1() {
var b = 9;
console.log(a);
console.log(b);
var a = '123';
}
// 相当于以下代码
// var a;
// function f1() {
// var b;
// var a;
// b = 9;
// console.log(a);
// console.log(b);
// a = '123';
// }
// a = 18;
// f1();
</script>
案例4:
<script>
// 案例4
//输出结果:99999 报错
f1();
console.log(c);
console.log(b);
console.log(a);
function f1() {
var a = b = c = 9;
console.log(a);
console.log(b);
console.log(c);
}
// 以下代码
// function f1() {
// var a;
// a = b = c = 9;
// // 相当于 var a = 9; b = 9; c = 9; b 和 c 直接赋值 没有var 声明 当 全局变量看
// // 集体声明 var a = 9, b = 9, c = 9;
// console.log(a);
// console.log(b);
// console.log(c);
// }
// f1();
// console.log(c);
// console.log(b);
// console.log(a);
</script>
五、对象
5.1 对象的相关概念
什么是对象?在 JavaScript 中,对象是一组无序的相关属性和方法的集合,所有的事物都是对象,例如字符串、数值、数组、函数等。 对象是由属性和方法组成的。
属性:事物的特征,在对象中用属性来表示(常用名词)
方法:事物的行为,在对象中用方法来表示(常用动词)
为什么使用对象:为了让更好地存储一组数据,对象应运而生:对象中为每项数据设置了属性名称,可以访问数据更语义化,数据结 构清晰,表意明显,方便开发者使用。
var ZXY = {
"name":"张学友",
"sex":"男",
"age":128,
"height":154
}
变量、属性、函数、方法总结:
属性是对象的一部分,而变量不是对象的一部分,变量是单独存储数据的容器。变量:单独声明赋值,单独存在。属性:对象里面的变量称为属性,不需要声明,用来描述该对象的特征。
方法是对象的一部分,函数不是对象的一部分,函数是单独封装操作的容器。函数:单独存在的,通过“函数名()”的方式就可以调用。 方法:对象里面的函数称为方法,方法不需要声明,使用“对象.方法名()”的方式就可以调用,方法用来描述该对象的 行为和功能。
5.2 创建对象的三种方式
5.2.1 利用字面量创建对象
创建方法:
1、 里面的属性或者方法我们采取键值对的形式 键 属性名 : 值 属性值
2、 多个属性或者方法中间用逗号隔开的
3、 方法冒号后面跟的是一个匿名函数
调用属性和方法的规范:
1、调用对象的属性 : 对象名.属性名 。
2、调用属性还有一种方法 : 对象名['属性名']。
3、 调用对象的方法 : 对象名.方法名() 。 千万别忘记添加小括号!!!
<script>
// 1.利用对象字面量创建对象 {}
// var obj = {}; // 创建了一个空的对象
var obj = {
uname: '张学友',
age: 18,
sex: '男',
sing: function() {
console.log('lalala~');
}
}
// (1). 调用对象的属性
console.log(obj.uname);
// (2). 调用属性
console.log(obj['age']);
// (3) 调用对象的方法
obj.sing();
</script>
5.2.2 利用 new Object 创建对象
创建空对象:
var Zxy = new Object();
填充空对象:
<script>
// 利用 new Object 创建对象
var ZXY = new Object();
ZXY.username = '张学友';
ZXY.sex = 'nan';
ZXY.age = 23;
ZXY.sing = function () {
alert('lalal~');
}
console.log(ZXY.sex);
ZXY.sing();
</script>
注意:
1、 Object() :第一个字母大写
2、 new Object() :需要 new 关键字
3、 使用的格式:对象.属性 = 值; 对象.方法名 = 函数体;
5.2.3 利用构造函数创建对象
为什么使用构造函数?因我们前面两种创建对象的方式一次只能创建一个对象。
构造函数 就是把我们对象里面一些相同的属性和方法抽象出来封装到函数里面。
使用的注意点:
1. 构造函数名字首字母要大写
2. 我们构造函数不需要return 就可以返回结果
3. 我们调用构造函数 必须使用 new
4. 我们只要调用函数就创建一个对象
5. 我们的属性和方法前面必须添加 this
<script>
function Star(name, age, sex) {
this.username = name;
this.userage = age;
this.usersex = sex;
this.sing = function (singname) {
alert(singname);
}
}
var xzq = new Star('薛之谦', 18, '男');
xzq.sing("绅士");
</script>
5.2.4 new 关键字的执行过程
1. new 构造函数可以在内存中创建了一个空的对象
2. this 就会指向刚才创建的空对象
3. 执行构造函数里面的代码 给这个空对象添加属性和方法
4. 返回这个对象
5.3 对象的遍历
for...in 语句用于对数组或者对象的属性进行循环操作。
for (变量 in 对象名字) {
// 在此执行代码
}
使用方法:
for (var k in obj) {
console.log(k); // 这里的 k 是属性名
console.log(obj[k]); // 这里的 obj[k] 是属性值
使用举例:(但是不常用于遍历方法)
<script>
// 遍历对象
var star = {
name: 'xzq',
age: 18,
sex: 'nan',
sing: function () {
alert('shenshi');
}
}
for (const key in star) {
alert(star[key]);
}
</script>