网络

教育改变生活

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 827|回复: 0
打印 上一主题 下一主题

JavaScript--变量

[复制链接]

424

主题

425

帖子

1732

积分

版主

Rank: 7Rank: 7Rank: 7

积分
1732
跳转到指定楼层
楼主
发表于 2023-9-8 22:57:16 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
实战JavaScript+JQuery实现网页交互
2变量
2.1  ECMAScript语法特征
1)区分大小写:变量、函数名、运算符等都是区分大小写的。比如:变量 test与变量 TEST 是不同的。
2)弱类型:ECMAScript是一种弱类型语言,变量无特定的类型,用var定义变量时,直接初始化为任
         意值。
         var color=”red”;
         var mun=25;
3)分号:ECMAScript 则允许开发者自行决定是否以分号结束一行代码。好的代码编写习惯是使用分
         号,因为没有分号,有些浏览器就不能正确运行。
4)注释:单行注释以“//”开头;多行注释以“/*”开头,以“*/”结尾。
5)花括号表示代码块:代码块表示一系列按顺序执行的语句,这些语句被封装在“{”和“}”之间。
         例如:
        var flag=false;
        if(flag){
                     alert(1);  //或者使用console.log(test1)写入浏览器控制台
        }
        else{
           alert(0);
        }
2.2  变量
       变量是程序设计语言中,用来储存计算结果或表示某个值的抽象概念。程序中的
数据赋给一个简短、易于记忆的名字,称为变量。变量可以通过变量名访问。
2.2.1  变量的声明
1)var声明变量
       变量可以不声明,解释程序会为变量自动创建一个值,无需明确的类型声明。但是,建议程序员先声明后使用,用var来声明变量。例如:
        var  test = ”hi”;
        var test1=”hi”, age=25;
        变量可以存放不同类型的值,这是弱类型变量的优势。例如,可以把变量初始化为字符串类型的值,后续将它修改为数字值,如下所示:
        var test=”hello”;
        console.log(test);
        test=55;
        console.log(test);
        代码将输出字符串值和数字值。但是,好的编码习惯是始终存放相同类型的值。另外,JS也允许变量先使用后声明。
2.2.1  变量的声明
2)let声明变量
       let是ES6中新增命令用于声明变量,let命令在代码块{}内有效,在{}之外不能访问。let和var的区别体现在作用域上。var的作用域被规定为一个函数作用域,而let则被规定为块作用域,块作用域比函数作用域小。但是,如果两者既没在函数中,也没在块作用域中定义,那么两者都属于全局作用域。
      {
             let  x = 2;   //let声明变量
      }
           // 离开了块作用域,就不能访问x变量了
        var允许在同一作用域中声明同名的变量,而let不可以。ES6中还有一个声明变量的命令const,const和let都是在声明的块作用域中有效,但是let声明的变量可变,值和类型都可以改变,没有限制。const声明的变量不能改变,所以,const一旦声明一个变量,就必须马上初始化。
2.2.1  变量的声明
3)变量名
变量名需要遵守的规则:
第一个字符必须是字母、下划线(_)或美元符号($);
变量名由下划线、美元符号、字母或数字字符组成;
变量名不能是保留字,且大小写敏感;
变量名第一个字符建议仅用字母开头。下面的变量都是合法的:
     var test;
     var $test1;
     var $123;
    var _test1$
2.2.1  变量的声明
4)命名规则
Camel标记法:首字母是小写的,接下来的字母都以大写字符开头。
    var  myTestValue=0;
Pascal标记法:首字母是大写的,接下来的字母都以大写字符开头。
    var  MyTestValue=”hello”;
匈牙利标记法:在以 Pascal 标记法命名的变量前附加一个小写字母(或小写字母序列),说明该变量的类型。例如,i 表示整数,s 表示字符串。
    var  iMyTestValue=0, sMySecondValue=”hello”;
2.2.1  变量的声明
5)关键字
       ECMA-262 定义了ECMAScript 支持的一套关键字(keyword)。这些关键字标识了 ECMAScript 语句的开头或结尾。根据规定,关键字是保留的,不能用作变量名或函数名。
下面是 ECMAScript 的关键字:
break,case,catch,continue,default,delete,do,else,finally,for,function,
if,in,instanceof,new, return, switch, this, throw, try, typeof, var, void, while, with
2.2.1  变量的声明
6)保留字
       ECMA-262定义了ECMAScript 支持的一套保留字(reserved word)。保留字在某种意思上是为将来的关键字而保留的单词。因此保留字不能被用作变量名或函数名。
保留字的完整列表如下:
abstract,boolean,byte,char,class,const,debugger,double,enum,export,extends,final,float,goto,implements,import,int,interface,long,native,package,private,protected,public,short,static,super,synchronized,throws,transient,volatile
10
2.2.1  变量的声明
7)标识符
       标识符是用户编程时使用的名字,用于给变量、常量、函数、语句块等命名,以建立起名称与使用之间的关系。标识符通常由字母和数字以及其它字符构成。
2.2.2  变量的数据类型
JS有5种原始类型,分别是数字number,字符串string,布尔boolean,空null、未定义undefined,两种引用数据类型(对象object,数组array)。ES6引入一种新的原始数据类型Symbol,表示独一无二的值。
11
2.2.2  变量的数据类型
1)number类型(数值类型)
       这是ECMA-262 中定义的最特殊的类型。这种类型既可以表示 32 位的整数,也可以表示 64 位的浮点数。直接输入的(而不是从另一个变量访问的)任何数字都是number类型的字面量。
下面的代码声明了存放整数值的变量iNum,值由字面量55定义:
        var  iNum=55;
八进制字面量的首数字必须是0开头,其后的数字可以是八进制数字(0-7)。
       var  iNum=55;   //等于十进制整数45
十六进制的字面量,必须是0x开头,其后是十六进制数字(0 到 9 和 A 到 F),字母可以是大小写。
       var  iNum=0x1f;   //等于十进制整数31
       var  iNum=0xAB;   //等于十进制整数171
定义浮点数,必须包括小数点和小数点后的一位数字(例如,用 1.0 而不是 1)。
       var  fNum=3.0;
浮点字面量在进行计算前,存储的是字符串。可以用科学计数法表示浮点数,可以把一个数表示为数字(包括十进制数字)加 e(或 E),后面加乘以10的倍数。
       var  fNum=3.14e7;   //科学计数法转化成计算的值为:3.14 x 107
浮点数精度不能做精准运算,如0.1 + 0.2,返回0.300000000000004
12
2.2.2  变量的数据类型
2)string类型(字符串类型)
       string 类型是唯一没有固定大小的原始类型。字符串中每个字符都有特定的位置,首字符从位置 0 开始,第二个字符在位置 1,依此类推。字符串中最后一个字符位置一定是字符串的长度减1。
     字符串字面量是由双引号(")或单引号(')声明的。由于 ECMAScript 没有字符类型,所以可使用这两种表示法中的任何一种。
13
2.2.2  变量的数据类型
3)boolean类型(布尔类型)
       boolean 类型是JS中最常用的类型之一。它有两个值 true 和 false (即两个 Boolean 字面量)。
       var  bFound = true, bLost=false;
4)undefined类型(未定义类型)
       undefined 类型只有一个值,即 undefined。当声明的变量未初始化时,该变量的默认值是 undefined。undefined 类型的字面量。下面代码段可以测试该变量的值。
       var  oTemo;
       alert(oTemp == undefined);
       代码显示 "true",说明这两个值确实相等。可以用typeof运算符显示该变量类型。
14
2.2.2  变量的数据类型
5)null类型(空引用类型)
       null类型只有一个值,即null,表示一个空对象引用。值undefined实际上是从值 null 派生来的,因此 ECMAScript 把它们定义为相等的,null 和 undefined 的值相等,但类型不等。
      typeof undefined             // undefined
      typeof null                  // object
      null === undefined           // false
      null == undefined            // true
       尽管这两个值相等,但它们的含义不同。undefined 是声明了变量,但未对其初始化时赋予该变量的值,null 则用于表示尚未存在的对象。如果函数或方法要返回的是对象,当找不到该对象时,返回的通常是null。
15
2.2.2  变量的数据类型
6)Symbol类型(符号类型)
       ES5的对象属性名都是字符串,这容易造成属性名的冲突。ES6引入了Symbol类型,保证每个属性的名字都是独一无二的,从根本上防止属性名的冲突。Symbol值,通过Symbol函数生成。对象的属性名可以有两种类型,一种是原来就有的字符串,另一种就是新增的Symbol类型。
       let  s1 = Symbol();
       let  s2 = Symbol();
       console.log(typeof s1)   //输出symbol,注意大小写
       s1=s2   // fasle
   
2.2.2  变量的数据类型
7)引用类型
       引用类型包括了对象object和数组array。对象object也是变量,是一种特殊的数据。对象由大括号分隔。在括号内部,对象的属性以名称和值对的形式 (name : value) 来定义,对象可以包含多个属性/值对,用逗号分隔,名称和值由冒号分隔。
创建object类型数据三种方法
定义和创建单个对象,使用对象字面量方式,即使用花括号{名称:值对}。
var person = {
    name:"Bill",
    age:62,
};
定义对象构造器,然后创建构造类型的对象。
function person(name,age){  
//本例使用函数构造器
this.name=name;
this.age=age;
}
定义和创建单个对象,通过关键词 new。
var person = new Object();
person.name = "Bill";
person.age = 50;
对象属性有两种寻址(访问)方式
name=person.name;
name=person["name"];
2.2.2.2  原始数据类型和引用数据类型
1)原始数据类型
       JS原始数据类型(primitive type):Undefined、Null、Boolean、Number、String。ES6新增了Symbol类型。使用运算符typeof,可以返回变量或值的原始类型。
例如:var sTemp = "test string";
           var s1=Symbol();
           console.log(typeof sTemp);   //输出string
           console.log(typeof 86);    //输出number
           console.log(typeof s1);    //输出symbol
对变量或值调用 typeof 运算符将返回其类型,类型值可以是下列一:
●   Undefined,如果变量是 Undefined类型
●   boolean,如果变量是 Boolean类型
●   number,如果变量是 Number类型
●   string,如果变量是 String类型
●   symbol,如果变量是 Symbol类型
●   object, 如果变量是引用类型Object或Null类型
2.2.2.2  原始数据类型和引用数据类型
2)引用数据类型
引用数据类型:对象(Object)和数组(Array)。
       JS是面向对象的语言,但JS不使用类。在ES6前,JS不会创建类,也不会像其它面向对象语言一样,通过类来创建对象。JS是基于prototype,而不是基于类的。所有JS对象都会从一个prototype(原型对象)中继承属性和方法。
Boolean 对象
Boolean对象是boolean原始类型的引用类型。创建Boolean对象只需要传递布尔值作为参数。Boolean对象将覆盖 Object 对象的 ValueOf() 方法,返回原始值,即 true 和 false。ToString()方法也会被覆盖,返回字符串 "true" 或 "false"。
var oBooleanObject = new Boolean(true);
注意:在JS中很少使用 Boolean 对象,一般直接使用Boolean原始值true和false。
19
2.2.2.2  原始数据类型和引用数据类型
Number 对象
       Number 对象是 number 原始类型的引用类型。要创建 Number 对象,采用下列代码创建值为26的对象:
var oNumberObject = new Number(26);
       Number 对象,要得到数字对象的原始值,只需要使用 valueOf() 方法。
Number 对象处理数值的专用方法
l toFixed()方法,返回数字的字符串表示,并指定小数位数。
l         var oNumberObject = new Number(68);
l         alert(oNumberObject.toFixed(2));  //输出 "68.00"
l toExponential(),返回科学计数法表示的数字的字符串形式。
l         var oNumberObject = new Number(68);
l        alert(oNumberObject.toExponential(1));  //输出 "6.8e+1“
l toPrecision()方法,不使用参数,返回数字的预定形式或指数形式。若使用参数,用来指定位数个数。
l         var oNumberObject = new Number(68);
l         alert(oNumberObject.toPrecision(1));  //输出 "7e+1"
l         alert(oNumberObject.toPrecision(2));  //输出 "68"
l         alert(oNumberObject.toPrecision(3));  //输出 "68.0"
20
2.2.2.2  原始数据类型和引用数据类型
String 对象
    String对象是string原始类型的对象表示,String的valueOf()方法和 toString()方法都会返回String类型的原始值。
    var oStringObject = new String("hello world");
    alert(oStringObject.valueOf() == oStringObject.toString());       
    //输出 "true"
String 对象具有属性length,它是字符串中的字符个数:
    var oStringObject = new String("hello world");
    alert(oStringObject.length);        //输出 "11"
21
2.2.2.2  原始数据类型和引用数据类型
l 方法 charAt() 和 charCodeAt() 访问的是字符串中的单个字符。这两个方法都有一个参数,即要操作的字符的位置。charAt() 方法返回的是包含指定位置处的字符的字符串,charCodeAt()方法返回字符代码。
l 方法concat(),用于把一个或多个字符串连接到 String 对象的原始值上。
l indexOf() 和 lastIndexOf() 方法返回的都是指定的子串在另一个字符串中的位置,找不到子串,返回 -1。indexOf() 是从字符串的开头(位置 0)开始检索字符串, lastIndexOf()是从字符串的结尾开始检索子串。
l localeCompare()方法对字符串进行排序比较。有一个参数,指定要进行比较的字符串。返回的是下列三个值之一: 如果 String 对象按照字母顺序排在参数中的字符串之前,返回负数;如果String对象等于参数中的字符串,返回 0; 如果 String 对象按照字母顺序排在参数中的字符串之后,返回正数。
l 4种方法执行大小写转换:toLowerCase();toLocaleLowerCase();toUpperCase();toLocaleUpperCase(), 前两种方法用于把字符串转换成全小写,后两种方法用于把字符串转换成全大写。toLocaleLowerCase() 和 toLocaleUpperCase() 方法是基于特定的区域实现。
22
2.2.2.2  原始数据类型和引用数据类型
3)7种数据类型的区别
       ECMAScript包括基本数据类型和引用数据类型。基本数据类型指的是简单的数据段,引用数据类型指的是有多个值构成的对象。当变量赋值给一个变量时,解析器首先要确认这个值是基本类型值还是引用类型值。
       原始数据类型包括:Undefined,Null,Boolean,Number,String,Symbol,引用数据类型包括对象、数组、函数等。
       原始数据类型值,存储在栈(stack)中的数据段,它们的值直接存储在变量访问的位置。因为这些原始类型占据的空间是固定的,所以存储在较小的内存区域栈中,便于迅速查找变量的值。
       引用数据值,存储在堆(heap)中的对象,也就是说,存储在变量处的值是一个指针(point),指向存储对象的内存地址。
23
2.2.2.2  原始数据类型和引用数据类型
存储位置不同
原始数据类型直接存储在栈(stack)中简单数据段,占据空间小,大小固定,属于被频繁使用的数据,所以存储在栈中;
引用数据类型直接存储在堆中,占据空间大,大小不固定,引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址,当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后,从堆中获得对象实体。
传值方式不同
按值传递(call by value)是最常用的求值策略:函数的形参是被调用时所传实参的副本。修改形参的值并不会影响实参。按引用传递(call by reference)时,函数的形参接收实参的隐式引用,而不再是副本。这意味着函数形参的值如果被修改,实参也会被修改。同时两者指向相同的值。
24
2.2.2.2  原始数据类型和引用数据类型
基本数据类型按值传递,基本类型是不可变的,只有对象是可变的(mutable)。在JS中,任何看似对string值的”修改”操作,实际都是创建新的string值。
var person,name;
person = 'kn';
name=person;
person='黑白';
console.log(person, name, typeof person);   //黑白 kn string
person的改变没有改变name,说明 string 是按值传递的。赋值时创建一块新的内存空间。
引用类型按引用传递,引用类型的值是可变的,引用类型的值是保存在栈内存中,指向堆内存中的对象,JS和其他语言不同,不允许直接访问堆内存中的位置和操作堆内存空间,只能操作对象在栈内存中的引用地址。
var obj = {x : 0};
obj.x = 100;
var o = obj;
o.x = 1;
console.log(obj.x)// 1, 被修改
o = {x:100};  //等同于重新赋值,重新开辟内存,不是修改
console.log(JSON.stringify(obj),JSON.stringify(o))//{"x":1} {"x":100}
obj.x; // 1, 不会因o = {"x":100}改变
25
2.2.2.2  原始数据类型和引用数据类型
参数传递的不同
即实参复制给形参的过程不同,JS中所有函数的参数都是按值来传递的,主要原因是内存分配时的差别。
原始值:只是把变量里的值传递给参数,之后参数和这个变量互不影响。
引用值:对象变量的值是这个对象在堆内存中的内存地址(必须铭记),因此它传递的值也就是这个内存地址,这就是为什么函数内部对这个参数的修改会体现在外部的原因,因为它们指向同一个对象。
总结
原始值:在将一个保存着原始值的变量复制给另一个变量时,会将原始值的副本赋值给新变量,此后这两个变量是完全独立的,它们只是拥有相同的值而已。
引用值:在将一个保存着对象内存地址的变量复制给另一个变量时,会把这个内存地址赋值给新变量,也就是说这两个变量都指向了堆内存中的同一个对象,它们中任何一个作出的改变都会反映在另一个身上。
复制对象时并不会在堆内存中新生成一个一模一样的对象,只是多了一个指针变量,指向这个对象。
26
2.2.2.3  typeof运算符
   可以使用 typeof 操作符来检测变量的数据类型。
     typeof  "john"   // 返回 string
     typeof 3.14   // 返回 number
     typeof false   // 返回 Boolean
     typeof [1,2,3,4] //返回object,在JS中,数组是一种特殊对象类型。
     typeof {name:'John', age:34}   // 返回 object
    使用 typeof 运算符时采用引用类型存储值会出现一个问题,无论引用的是什么类型的对象,它都返回 "object"。JS引入了另一个Java运算符 instanceof 来解决这个问题。     instanceof 运算符与 typeof 运算符相似,用于识别正在处理的对象的类型。与typeof 方法不同的是,instanceof 方法要求开发者明确地确认对象为某特定类型。
     var oStringObject = new String("hello world");//创建字符串对象
     console.log(oStringObject instanceof String);//输出"true"
27
2.2.3  变量的值
ECMAScript 中,变量可以存在两种类型的值,即原始值和引用值。原始值存储在栈(stack)中,即存储在变量访问的位置。引用值是存储在堆(heap)中的对象,存储在变量处的值是一个指针(point),指向存储对象的内存地址。
原始类型占据的空间是固定的,存储在较小的内存区域栈中。这样存储便于迅速查寻变量的值。如果一个值是引用类型的,那么它的存储空间将从堆中分配。由于引用值的大小会改变,所以不能把它放在栈中,否则会降低变量查寻的速度。存贮在栈空间中的值是该对象存储在堆中的地址,因为地址的大小是固定的。
28
2.2.3  变量的值
变量的值
基本类型赋值使用直接量,引用类型值的添加属性或方法如下:
var test = "hi", age = 25;   //定义变量test和age
var person = "xiaoming";   //定义一个基本类型值的变量
var person = new Object();   //定义一个引用类型值的变量
person.name = "xiaohong";
console.log(person.name);   //输出"xiaoming"
1)变量先声明后使用
2)变量的数据类型
变量的数据类型由值决定,如果变量值改变,变量的数据类型
也将改变。
3)未初始化变量的值
当声明的变量未初始化时,该变量的默认值是undefined。
var num = 13;  //声明变量num,赋给初始值13
num = 15;   //15赋给变量num,变量的值改变了,切记
alert(num+15);   //弹出25,此时num值为15
num = “xiaoming”;  //num表示字符串了
const totle = 30;  //声明了一个常量,赋值为30
totle=50;  //类型错误:不能给一个常量重新赋值
4)变量赋值演示案例
29
2.2.4  变量的作用域
l        变量有三种声明方式:
l var声明一个变量,可赋一个初值;
l let声明一个块作用域的局部变量,可赋一个初值;
l const声明一个块作用域的只读常量,初始化后不能修改。
l         JS中,变量分为全局变量和局部变量。全局变量在JS的任何地方都可以定义。局部变量只在函数体内定义。在声明局部变量时,必须要使用var关键字。
l  var  a = 1;  //局部变量
l  b = 1;  //全局变量
l        作用域是可访问变量、对象、函数的集合。在 JS中, 对象和函数也是变量。JS变量生命周期在它声明时开始,局部变量在函数执行完毕后销毁,全局变量在页面关闭后销毁。看以下代码:
l function test() {
l     var message=”hello”;
l     console.log(message);
l }
l test();   //输出hello
l console.log(message); //输出message没有定义
30
2.2.4.1  三种作用域
全局变量拥有全局作用域,在 JS代码中的任何地方都可以定义、访问。在函数体内声明的变量只在函数体内有定义,是局部变量。函数参数是局部变量,只在函数体内有定义。在函数体内,局部变量的优先级高于同名的全局变量。
var scope = 'global'  //声明全局变量,并赋初始值
function f () {
    var scope         
// 声明局部变量,覆盖同名的全局变量
    console.log(scope)   
// 此时 scope 已声明但还未赋值,输出 undefined
    scope = 'local'       // 赋值
    console.log(scope)   // 输出 local
}
31
2.2.4.1  三种作用域
1)全局作用域
var  i = 1;  //声明全局变量,这里var关键写也可以不写
function f(){
   var  i = 2;  //声明局部变量,这里var关键字必须写
  return  i;   //返回局部变量的值
}
console.log(f());   //显示函数返回值的局部变量值2
console.log(i);   //显示的全局变量的值1
      变量在函数外定义,即为全局变量。全局变量的使用范围称为全局作用域: 网页中所有脚本和函数均可使用。
变量在函数内没有声明(没有使用var关键字),该变量也是全局变量。实例中 carName 虽然在函数内,却是一个全局变量。
function myFunction() {
   carName = "Volvo"; // 函数内没有申明,变量carName是全局变量
}
32
2.2.4.1  三种作用域
2)函数级作用域
function functionName(parameters) {
   //执行的代码
}
      JS使用关键字function定义函数。函数可以通过声明定义,也可以是一个表达式。
        变量在函数内声明(使用var关键字),即为局部变量,局部变量的使用范围称为局部作用域或函数级作用域。局部变量只能在函数内部访问。因为局部变量只作用于函数内,所以不同的函数可以使用相同名称的变量。局部变量在函数开始执行时创建,函数执行完后局部变量会自动销毁。
function doSomething() {
    var thing = "吃早餐";  //定义了局部变量
}
console.log(thing);
// uncaught ReferenceError: not defined
33
2.2.4.1  三种作用域
2)函数级作用域
利用嵌套函数作用域可以访问,在外层函数中,嵌套一个内层函数,那么这个内层函数可以向上访问到外部作用域的变量。
内层函数可以访问到外层函数的变量,也可以通过返回内层函数来实现。
function outter() {
    var thing = "吃早餐";
    function inner() {
        console.log(thing);
    }
    inner();
}
outter();  // 吃早餐
function outter() {
    var thing = "吃晚餐";   
    function inner() {
        console.log(thing);
    }
    return inner;
}
var foo = outter();
foo();  // 吃晚餐
34
2.2.4.1  三种作用域
3)块级作用域
使用 var 关键字声明的变量不具备块级作用域的特性,它在 {} 外依然能被访问到。
{
    var  x = 2;
}
// 这里可以访问x变量
       ES2015(ES6)新增加了let和const两个重要的关键字。let声明的变量只在let命令所在的代码块内有效。const声明一个只读的常量,声明后常量的值就不能改变。
       ES6之前,JS只有全局作用域与函数级作用域两种。ES6 新增了块级作用域。使用 let 或者 const 命令声明的变量只在其块级作用域内有定义。块级作用域的出现使一些场景变得更加合理。比如:用 var 命令声明的函数作用域内的局部变量会覆盖同名的全局变量,而用 let 命令声明则不会。
使用 let 关键字来实现块级作用域。let 声明的变量只在 let 命令所在的代码块{}内有效,在{}之外不能访问。
{
    let  x = 2;
}
// 这里不能访问x变量
35
2.2.4.1  三种作用域
3)块级作用域
for (var i = 0; i < 5; i++) {
    //   
}
console.log(i)  // 输出5
for (let i = 0; i < 5; i++) {
    //   
}
console.log(i)  // 输出Reference Error
for (let i = 0; i < 5; i++) {
    let i = 'hello world'
    console.log(i)  // 输出 hello world
}
var命令声明循环变量会泄露为全局变量,而用 let 命令声明则不会。另外,for循环还有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。
36
2.2.4.1  三种作用域
4)let、const和var作用域的区别
var i = 0  //声明全局变量i,也可以不用var
var a = [1,2,3,4,5]
for(var i = 0; i < 4;i++){
// console.log(a);
}  //循环执行结束,全局变量i值修改为4
console.log(a);//5
var i = 0
var a = [1,2,3,4,5]
for(let i = 0; i<4;i++){
//console.log(a);
}  //循环执行结束,全局变量i值为0,没有修改
console.log(a);//1
使用var关键字声明的变量在任何地方都可以修改。在相同的作用域或块级作用域中,不能使用let关键字来重置var关键字声明的变量。const 用于声明一个或多个常量,声明时必须进行初始化,且初始化后值不可再修改,不存在变量提升机制。
let关键字定义的变量需要先声明再使用,不存在变量提升机制。let和const都是声明一个块级作用域的变量及常量,都不能和它所在作用域内的其他变量或函数拥有相同的名称,即不能重复申明,不易发生变量命名污染的问题,能规避冲突,使得代码简洁优雅。
37
2.2.4.1  三种作用域
4)let、const和var作用域的区别
var x = 10;
// 这里输出 x 为 10
{
    const x = 2;
    // 这里输出 x 为 2
}
// 这里输出 x 为 10
let和const两者还有以下区别:const声明的常量必须初始化,而let声明的变量不用。const 定义常量的值不能通过再赋值修改,也不能再次声明。而 let 定义的变量值可以修改。
38
2.2.4.1  三种作用域
4)let、const和var作用域的区别
var a = [];
for (var i = 0; i < 10; i++) {
  a = function () {
    console.log(i);
  };
}
a[5]();   // 结果为10;
var a = [];
for (let i = 0; i < 10; i++) {
  a = function () {
    console.log(i);
  };
}
a[5]();   //结果为 5;
由于没有块级作用域,i 变量全局只有一个,当 for 循坏结束,变量 i 的值等于10。a[5]()对应的函数是输出i的值,而此时变量 i 的值已经是10,因此输出结果是10。同样a[0]()、a[1]()等也是一样结果。
数组内的索引为5,函数内的变量值为5,每次循环,会创建新的块级作用域,然后重新声明一个新的变量 i;JS 的解释引擎会记住上次循环的变量值,所以能够返回正确的结果,输出5。
39
2.2.4.1  三种作用域
4)let、const和var作用域的区别
function test() {
        message='hi';
        console.log(message);
    }
console.log(message);   //程序出错
function test() {
        message=”hello”;
        console.log(message);
    }
    test();
    console.log(message);
未使用var操作符声明的变量message为全局变量,未调用test()方法,message就属于未定义状态。错误信息提示message没有定义。
未使用var操作符声明的变量message为全局变量,必须调用test()方法, message才会有效。程序输出2行”hello”。
40
2.2.4.2  变量提升(Hoisting机制)
变量提升,似乎意味着变量和函数的声明会移动到代码的最前面。但是,实际上变量和函数声明在代码里的位置是不会动的,而是在编译阶段放入内存时,提升到代码前面。
num = 6;
num + 7;
var  num;
var x=1;
console.log(x + ” ” + y);   
//输出1 undefined
var y=2;
由于变量提升,不会出错。
JS仅提升声明,不提升初始化值。如果你先使用的变量,再声明并初始化它,变量值是undefined。
2.2.4.2  变量提升(Hoisting机制)
1)提升优点
JS在执行任何代码段之前,将函数声明放入内存中进行提升,这就允许在声明该函数之前使用该函数。变量可以在声明之前进行初始化和使用。但是如果没有初始化,就不能使用它们。函数和变量相比,会被优先提升,即函数会被提升到更靠前的位置。
/*正确的方式:先声明函数,再调用*/
function aName(name){
   console.log("这是 " + name);
}
aName("Tigger");   
//输出"这是 Tigger"
/*不推荐的方式:先调用函数,再声明*/
aName("Horse");
function aName(name){
   console.log("这是 " + name);  
//输出"这是 Horse"
}
在定义函数之前调用函数,仍然可以工作。
正常处理方式(先声明,后调用)
42
2.2.4.2  变量提升(Hoisting机制)
2)提升规则
var 声明的变量,提升时只声明,不赋值,默认为undefined(demo1中的变量a);不用关键字var,直接赋值的变量不能提升(demo1中的变量b);
函数提升会连带函数体一起提升,而函数不会被执行(deom2);
/**demo1**/
console.log('a=',a);    //a=undefined
console.log('b=',b);   
// Uncaught ReferenceError: b is not defined
var a=1;
b=6;
/**deom2**/
console.log('a=',a)  // a=function  a() {console.log("func a()")}
function  a() {
console.log("func a()")
}
43
2.2.4.2  变量提升(Hoisting机制)
2)提升规则
函数的优先级高于变量,函数声明提前到当前作用域最顶端(deom3);
预解析的顺序是从上到下执行,若变量或函数重名,提升时后面的定义会覆盖前面的定义(demo4);
/**deom3**/
console.log('a=',a)   // a=function  a() {console.log("fun a")}
var a=4
function a(){
console.log("fun a")
}
var a=5
console.log("a=",a)   // a=5
/**deom4**/
console.log('a=',a)  // a=undefined
var a =2
console.log('a=',a)   //a=2
var a =4   //修改了a
console.log('a=',a)   // a=4
44
2.2.4.2  变量提升(Hoisting机制)
2)提升规则
函数执行时,函数内部的变量声明和函数声明也按照以上规则进行提升;(deom6)
用函数表达式声明函数,按照声明变量规则进行提升(先后顺序);(deom5)
/**deom6**/
console.log('b=',b)   //输出function b(i)定义
var a=3
function b(i){
    console.log('a=',a)   
    var a=4
    function a(){
        console.log('fun a')
    }
    console.log('a=',a)   
}
b()   
//调用函数,先输出function a()定义,然后输出4
/**deom5**/
console.log('a=',a)   // a=undefined
var a=function(){console.log('a1')}
var a=5
console.log(a)
var a=function(){console.log('a2')}
console.log('a=',a)   
// a= function(){console.log('a2')}
2.3  const常量
常量表示一些固定不变的数据,使用const关键字创建常量,声明的同时必须赋值。为了区分变量与常量,一般变量名采用小写或驼峰标识,常量采用全大写的形式。
对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指针,const只能保证这个指针是固定的,至于它指向的数据结构是不是可变的,就完全不能控制了。因此,将一个对象声明为常量必须非常小心。
const foo = {};
foo.prop = 123;   // 为 foo 添加一个属性
// 将 foo 指向另一个对象,就会报错
foo = {}; // TypeError: "foo" is read-only
const a = [];
a.push('Hello');
a.length = 0;
a = ['Dave'];   
// 报错,常量a是一个数组,就不可以将另一个数组赋值给a
对象常量只是不允许修改引用地址,但是属性还是可以被修改、扩展和删除。
常量foo储存的是一个地址,指向一个对象。不可变的只是这个地址,但对象本身是可变的,所以依然可以为其添加新属性。
l 一个真正的对象常量,必须满足以下三点:
l 对象的属性不得被扩展;
l 对象的属性不得被删除;
l 对象的属性不得被修改;
var Obj = {
name: 'jack'
}
Object.seal(Obj)
Obj.age = 23 // 扩展属性
console.log(Obj.age)
// undefined(说明扩展失败了)
delete Obj.name // 删除属性
console.log(Obj.name)
// 'jack'(说明删除失败了)
var Obj = {
name: 'jack'
}
Object.preventExtensions(Obj)
Obj.age = 23   // 扩展属性
console.log(Obj.age)   
// undefined(说明扩展失败了)
防止对象属性被删除,Object.seal方法不仅可以保证对象的属性不被扩展,还能防止属性被删除。
防止对象属性被修改,Object.freeze,它可以实现对象既不可被扩展和删除,而且还不被修改
var Obj = {
name: 'jack',
extraInfo: {
age: 23
}
}
Object.freeze(Obj)
Obj.extraInfo.age = 80
console.log(Obj.extraInfo.age)  
// 80,还是修改了
var Obj = {
name: 'jack'
}
Object.freeze(Obj)
Obj.age = 23 // 扩展属性
console.log(Obj.age)
// undefined(说明扩展失败了)
delete Obj.name // 删除属性
console.log(Obj.name)
// 'jack'(说明删除失败了)
Obj.name = 'lucy' // 修改属性
console.log(Obj.name)
// 'jack'(说明修改失败)
Object.freeze虽然实现了真正的对象常量,但是它的一切操作只在顶级对象属性上生效。
防止对象属性被修改,Object.freeze,它可以实现对象既不可被扩展和删除,而且还不被修改。

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

WEB前端

QQ|手机版|小黑屋|金桨网|助学堂  咨询请联系站长。

GMT+8, 2025-1-2 23:39 , Processed in 0.040658 second(s), 21 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表