JavaScript学习笔记(四)--- 作用域、预解析和对象

JavaScript学习笔记(四)— 作用域、预解析和对象

一、作用域

1、概述

​ 通常一段程序代码中的命名变量或函数是有一定的有效作用范围,超出了这个范围,这些命名就是无效的,限定这些命名的代码范围就是这个命名的作用域,作用域的出现增强了程序的逻辑可靠性,减少了命名冲突。在 ES6 之前的JavaScript中作用域分为两种全局作用域局部作用域。(至于ES6中新增的知识 我们暂且不做讨论 留作日后)

2、全局作用域和局部作用域

全局作用域:命名的作用范围是整个JavaScript代码执行的环境,例如整个 script 标签内或者一整个js 文件内。

局部作用域:命名的作用范围一般局限于某个函数内部,所以也称为函数作用域

注意:JavaScript 中不存在块级作用域,即在 if 语句、for循环语句等环境中创建的变量,也可以在这些语句之外使用,因为 JavaScript 在执行时会进行预解析(下面进行详细介绍)。

  if(true){           //JavaScript中不存在块级作用域
    var num = 123;
    console.log(num); //123
  }
  console.log(num);   //123
3、全局变量和局部变量

​ 在 JavaScript 中,根据作用域的不同,变量分为两种:全局变量局部变量

​ 全局变量:作用域为全局作用域的的变量称为全局变量,其在 js 代码的任何位置都可以调用。在函数外部即全局作用域下 用 var 声明的变量就是全局变量。还有一种特殊情况,没有使用 var 声明的而直接进行赋值使用的变量也是全局变量,但是我们十分以及极其特别的不建议使用这种方法。

​ 局部变量:作用域为局部作用域的变量为局部变量,其只能在所在函数的内部进行调用,在函数外部超出作用域范围,无法进行调用。在某个函数内部用 var 声明的变量就是该函数的局部变量,而且具有形参的函数,其形参就是局部变量。

​ 两者的区别:全局变量在 js 代码的任何地方都可以使用,在页面加载时被创建,只有在浏览器关闭时才会被销毁,长时间占用内存,局部变量只能在函数内部使用,只有当所在函数被执行时,才会被创建初始化,当所在函数运行结束后,就会被销毁,所以比较节省内存

var a = 10; // 全局变量
e = 30; //没有声明直接进行赋值的变量也是全局变量,但是不推荐使用
function f1(c , d){  // 形参也是局部变量 
    var b = 20; // 局部变量
}
f1(1,2);
4、作用域链

​ 只要有代码就存在一个作用域,如果代码中存在函数,那么就还有一个局部作用域,如果在这个函数中还存在一个函数,那么在这个局部作用域中还有一个局部作用域。在 JS 中存在内部函数可以访问外部函数变量的机制,根据这种机制把作用域根据大小连接起来,用链式查找的方式来决定被内部函数访问的是哪些数据,这就被称为作用域链。简单来说,作用域链就是通过就近原则来查找某些数据变量的值。

var a = 1;
function fn1() {  // fn1相对于fn2是外部函数
    var a = 2;
    var b = '22';
    fn2();
    function fn2() {  // fn2相对于fn1是内部函数  相对于fn3 是外部函数
        var a = 3;
        var b = '23'
        fn3();
        function fn3() {  //fn3相对于fn2是内部函数
            var a = 4;
            console.log(a); //就近原则  a的值 4
            console.log(b); //就近原则  b的值 23
        }
    }
}
fn1();

二、预解析

1、概念

​ JavaScript 代码 是由浏览器中的 JavaScript 解析器 来执行的,解析器在运行 JS 代码时分为两步: 预解析代码执行 。预解析是指在当前作用域下,在 JS 代码执行之前,解析器会把用 var 声明的变量 以及 用 function 声明的函数 提到代码最前面,进行提前声明或定义。简单来说就是预解析会在 JS 代码执行之前把变量和函数进行声明定义 。代码执行是指从上到下依次执行 JS 语句。

2、变量预解析

​ 变量预解析又称为变量提升,是指将带有 var 变量的声明提升到当前作用域最前面,但是有一点要注意,预解析只会提升变量的声明,并不会提升变量的赋值,哪怕变量得声明和赋值是同一条语句,解析器也会将他们拆分开执行。而且一定要是带有 var 的变量声明才会被提升。

console.log(num);  // 结果为: undefined
var num = 10;      

/*上面的代码相当于以下代码
var num;   //只将声明进行提升
console.log(num);  //输出一个只进行声明 而没进行赋值 的变量  结果为 undefined
num = 10;    // 此时才进行赋值
*/
//特殊情况
var a = b = c = 9 ;
//此时相当于: var a = 9; b = 9; c = 9 ;   就是说 b 和 c前面没有 var   ,
//属于没有声明直接使用的全局变量  而且因为没有 var 所以预解析的时候不会被提升
3、函数预解析

​ 函数预解析又称为函数提升,是指将带有 function 并且 拥有函数名的函数的声明提升到当前作用域的最前面,但只是声明(包括整个函数体),并不会被调用。

fn(); // 控制台输出 函数fn被调用
function fn() {
    console.log('函数fn被调用');
}

// 相当于以下代码
function fn() {
    console.log('函数fn被调用');
}
fn(); // 控制台输出 函数fn被调用

//特殊情况 函数表达式声明函数问题
fn();
var  fn = function() {
    console.log('想不到吧');
}
//运行结果为 报错提示 ”fn is not a function"
//这段代码相当于在执行变量提升,先把变量声明提升,再在原位置对其进行赋值,
//只不过赋的值是一个函数。至于函数 虽然有 function  但是没有变量名,所以不会进行函数提升。

4、优先级

如果我们声明的函数跟变量出现了同名的情况,那么函数预解析的优先级会高于变量的预解析,就算是先声明了函数,后声明了同名变量,函数也不会被覆盖的,调用同名时,指向的是函数。但在变量赋值之后,变量就会覆盖掉同名函数,此时内存中就只存在变量,调用同名时,指向的是变量:

function test() {
    return "这个是test函数";
}
var test;
console.log("给test变量赋值前输出的test---", test);
console.log("给test变量赋值前输出的test()---", test());
test = "这是test变量";
console.log("给test变量赋值后输出的test---", test);
console.log("给test变量赋值后输出的test()---", test());
代码执行结果:

在这里插入图片描述

三、对象

1、概念

​ 在 JavaScript 中 ,对象是一组相关属性和方法的集合,通过这些属性和方法来表示一个事物的特征和行为。所有具体的事物都可以形式化成对象,比如:那个学生、桌上的红苹果等等。 对象中可以存放所有类型的数据,例如字符串、数值、数组、函数(方法)等。对象由属性方法组成,属性是用来表示事物的特征,比如: 大小、重量、年龄等,常用名词来命名。方法用来表示事物的行为,例如:打电话、敲代码、玩游戏等,常用动词来进行命名。对象的属性是用来储存具体数据的,对象的方法是用来储存函数的。

//创建一个张三丰的对象
var obj = {
    "name" : "张三疯",
    "sex" : "男",
    "age" : 128,
    "height" : 154
    "gongfu" : function(){
    	alert('太极八卦掌');
    }
}
2、创建对象

①通过字面量创建对象

​ 对象的字面量是 {} 。我们可以通过花括号 { } 来创建对象,{ } 内以键值对的形式表示这个对象的属性和方法。我们用字面量创建一个对象后,通常会将其赋值给一个用 var 声明的变量,方便对这个对象进行调用。

//通过字面量创建一个名为 star 的对象
var star = {
    name : 'pink',
    age : 18,
    sex : '男',
    sayHi : function(){
        alert('大家好啊~');
    }
};

②利用new Object 创建对象

​ 首先利用内置构造函数Object() 创建一个对象,并将其赋值给一个变量。但此时的对象还只是一个空对象,里面没有任何属性和方法。我们需要通过调用对象属性和方法的方式,来为对象添加属性和方法。

​ 注意:Object() 第一个字母要大写;而且需要在前面写 new 关键字; 添加属性的方式为 对象名.属性名 = 值 ;添加方法的方式为: 对象名.方法名 = function() { }

var a = new Obect(); // 此时还是一个空对象
a.name = 'pink';    // 添加属性和方法
a.age = 18;
a.sex = '男';
a.sayHi = function(){
    alert('大家好啊~');
}

③利用构造函数创建对象

​ 构造函数是一种特殊的函数,是用来初始化对象的,就是为对象的属性和方法赋初值。我们通常把一类对象中的公共属性和方法抽取出来,封装到构造函数中,然后通过new + 构造函数 的形式来创建对象,在创建多个有公共属性和方法的对象时可以大大提升效率。

​ *注意:构造函数名我们一般约定首字母大写 ;构造函数内的属性和方法前面需要添加 this,用来表示当前的属性和方法;必须通过 new 来调用构造函数,调用构造函数创建对象的过程被称为对象的实例化 ;构造函数本身会返回所构造的对象,所以不需要写 return;构造函数的形参和实参是按照先后顺序一一对应传值的。

//构造函数的封装
function 构造函数名(形参1,形参2,形参3) {
     this.属性名1 = 参数1;
     this.属性名2 = 参数2;
     this.属性名3 = 参数3;
     this.方法名 = 函数体;
}

//构造函数的调用
var obj = new 构造函数名(实参1,实参2,实参3)

//实例
function Star(name,sex,age,sang) {
     this.name = name;
     this.sex = sex;
     this.age = age;
     this.sing = function(sang){
         alert(sang);
     };
}
var star = new Star('刘德华','男',18,'忘情水');
3、访问对象

​ 当一个对象被创建之后,我们在使用它的时候就是对其进行访问,访问对象属性的方法有两种,分别为: 对象.属性名对象名[‘属性名’] 。调用对象方法的方式为:对象.方法名 ( ) ,注意一定要加括号,有参数的要在括号里写实参

console.log(star.name)     // 访问对象name属性
console.log(star['name'])  // 访问对象name属性
star.sing();              // 访问对象 sayHi 方法
4、遍历对象

​ 在我们创建了一个对象后,我们如何才能将对象的所有属性从头到尾访问一遍呢。难道我们要通过前面说的访问对象属性的方法,一个个的进行访问吗?当然不是!那样也太麻烦了,不仅代码量很大 ,而且还必须对访问的对象十分了解才行。我们现在有一种更简便的方法:for…in 遍历语句。它通过声明变量来一一遍历对象中的所有属性。对象的方法也会被访问到,只不过不会执行,仅能获取到方法的内容。

// 语法格式
for (变量 in 对象名字) {
    // 在此执行代码
}

for (var k in obj) {   // 变量是自定义的 可以是 k 也可以是 key 更可以是 a b c...
    console.log(k);      // 这里的 k 表示的只是属性名,而不是属性值
    console.log(obj[k]); // 这里的 obj[k] 代表的是属性值 ,此时不需要加引号,
    //而且只能采取这种形式获得属性值,不能采用对象.属性名的形式
    console.log(obj.k);  // 这里的 obj.k 不能获取属性值,结果为 undefined
}

// 实例
        function Star(name,sex,age) {
            this.name = name;
            this.sex = sex;
            this.age = age;
            this.sing = function(){
                alert('666');
            };
        }
        var star = new Star('刘德华','男',18);
        for(var k in star){
            console.log(star[k]);
            console.log(star.k);  
        }
// 控制台输出结果

刘德华
undefinedundefined
18
undefined
ƒ (){
                alert('666');
            }
undefined
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

努力的小朱同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值