教育改变生活
标题: JavaScript--变量 [打印本页]
作者: 却尘 时间: 2023-9-8 22:57
标题: JavaScript--变量
实战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,它可以实现对象既不可被扩展和删除,而且还不被修改。
欢迎光临 教育改变生活 (http://bbs.goldoar.com/) |
Powered by Discuz! X3.2 |