《JavaScript权威指南》第三章学习笔记 类型、值和变量

第三章 类型、值和变量

  • 数据类型分类
    • 原始类型(primitive type)
      • 数字
      • 字符串
      • 布尔值
      • 特殊的原始类型
        • null
        • undefined
    • 对象类型(object type):属性的集合,每个属性都由名/值对构成。(值可以是原始类型,也可以是对象类型)
      • 特殊对象
        • 全局对象
        • 数组对象:一般对象都是无序的集合,但数组对象是带有编号的有序的集合
        • 函数:含有可执行代码的对象
          • 类:!!!
            • 日期类(Date)
            • 正则类(RegExp)
            • 错误类(Error)

对象类型是可以调用自己的方法
比如数组对象a排序时可以a.sort()
值得注意的是,原始类型中数字、字符串、布尔值也可以拥有自己的方法,那么只有null和undefined没办法拥有方法

  • 变量:无类型的(untyped),可以使用var关键字来赋予变量任意类型的值
    • 全局变量:不在函数内声明的变量
    • 局部变量:在函数内声明的变量

3.1数字

  • JavaScript中所有数字均用浮点数值表示,并不区分整数值和浮点数值
  • 数字直接量:直接出现在JavaScript程序中的数字

值得注意的是,在12前面加上负号我们可以得到它的负数-12,但这个负号是一元求反运算符,它并不是数字直接量的组成部分。

3.1.1 整型直接量

  • 十进制整型直接量:0,1,2,3,1024,65536
  • 十六进制整型直接量:0Xff,0xff //15*16+15=255(十进制)

3.1.2 浮点型直接量

  • 语法表示 [digits][.digits][(E|e)[(+|-)]digits]
例如:  
3.14  
1.33  
.3333  
6.02e23  //6.02*10^23
1.4728233E-32  //1.4728233*10^-32

3.13 JavaScript中的算术运算

  • 上溢(overflow)、下溢(underflow)、被零整除不会报错

    • 上溢结果会为infinity-infinity //表示无穷大
    • 下溢结果会为0-0//很少用到-0
    • 被零整除、无穷大相除、给负数开方、非数字的操作数会用NaN表示 //表示not-a-number,“非数字”
  • JavaScript中非数字值(包括NaN、字符串和对象)的特殊性:它和任何值都不相等,包括自己

也就是说,没办法通过x=NaN来判断x是否为NaN
但可以通过x!=x来判断,当x等于NaN的时候,表达式的结果为true,这是因为NaN的特殊性所决定的。

  • 负零值的特殊性:除了作为除数之外,它和正零值相等
var zero = 0;  //正常的零值
var negz = -0; //负零值
zero == negz; //=>true,这说明正零值和负零值相等
1/zero==1/negz; //=>false,这说明正无穷大和负无穷大不相等

3.1.4 二进制浮点数和四舍五入错误

  • JavaScript采用IEEE-754浮点数表示法,这是一种二进制法,可以精确表示$1/2$,$1/8$,$1/1024$,遗憾的是,对于$1/10$,$1/100$这样的十进制的分数却不能精确表示
//对于这样简单的数字也不能精确表达
var x=.3-.2 //3分钱-2分钱
var y=.2-.1 //2分钱-1分钱
x==y  //=>false,两个值并不相等

x==.1  //=>false,说明.3-.2!=.1
y==.1  //=>true,说明.2-.1==.1
/*
*原因是因为0.3-0.2和0.2-0.1的值进行四舍五入之后不相等 
*验证了Java语言,一样的结果
*最好的解决方法是:用整数来计数
*/

3.1.5 日期和时间

  • JavaScript语言核心包括Date()构造函数,用来创建表示日期和时间的对象。

3.2 文本

  • 字符串(string)是一组16位值组成的不可变的有序序列
  • 每个字符通常来自于Unicode字符
  • 字符集
    • 采用UTF-16编码的Unicode字符集
    • 常用Unicode字符通过16位内码表示,不能表示则用两个16位值组成的一个序列(亦称“代理项对”)表示,这以为这这些不能表示的字符串长度为2
var p="π"; `
p.length //=>1,p包含一个16位值
var e="e";
e.length //=>2,e由UTF-16编码后包含两个16位值:"\ud835\udc52"
  • JavaScript不会对字符串做标准化处理,不会保证字符串是合法的UTF-16格式,此后果是一定程度上会导致乱码

3.2.1 字符串直接量

  • 可以用单引号也可以用双引号
""空字符,长度为0
"hello"
'testing'
  • 单引号可以包含双引号,双引号也可以包含单引号
'name="myform"'
"Wouldn't you prefer O'Reilly's book?"
  • ECMAScript3中,字符串直接量必须写在一行,另起一行可以用转义字符\n
  • ECMAScript中,字符串直接量可以拆分成数行,每行必须以反斜线“\”结束
"two\nlines"
"one\
long\
line"
  • 编码风格:JavaScript代码和HTML代码有时会混合在一起,最好是写JavaScript字符串直接量时使用单引号,写HTML字符串直接量时使用双引号<button onclick="alert('Thank you')">Click Me</button>

3.2.2 转义字符(escape sequence)

  • 当我们使用单引号表示字符串直接量时,如果字符串直接量中也需要用到单引号的话,这时候转义字符就会很有用了var s='I can\'t tell you!'

3.2.3 字符串的使用

  • 两个字符串相加用“+”
  • 字符串还提供许多可以调用的方法

3.2.4 模式匹配

  • JavaScript定义了RegExp()构造函数,用来创建“正则表达式”
/*
*用两条斜线来包括一个正则表达式直接量
*第二条斜线之后跟随一个及以上的字母,用来修饰正则表达式的含义
*/
/^HTML/     //匹配以HTML开始的字符串
/[1-9][0-9]*/       //匹配一个非零数字,后面是任意个数字
/\bjavascript\b/i       //匹配单词“javascript”,忽略大小写

3.3 布尔值

  • truefalse
  • 假值和真值
    • 假值:类型转化为布尔值时为false的值
      1.undefined
      2.null
      3.0
      4.-0
      5.NaN
    • 真值:假值以外都为真值
if(i!==null)...
//这种情况下,只有当i不等于null时语句结果才为true,进而执行if后面的代码

if(i)
//在这种情况下,只有当i不等于undefined、null、0、-0、NaN时语句结果才为true
  • 布尔运算符
    • &&:与(AND)
    • ||:或(OR)
    • !:非(NOT)

3.4 null和undefined

  • null:可以表示数字、字符串和对象是“无值”的一个特殊的对象
  • undefined:更深层次的“空值”,可表示变量还没有初始化,也可以表示对象属性或数组元素不存在
  • 不同点
    • 性质不一样
      • null是关键字
      • undefined是全局变量
        • 它的值就是“undefined”(在ECMAScript5后,undefined只能读)
        • 类型是undefined类型,说明它是这个类型里面唯一的成员
    • 用处不一样
      • null表示程序级的、正常的或意料之中的值的空缺
      • undefined表示系统级的、出乎意料的或雷士错误的值的空缺
  • 相同点
    • 含义相等:都表示值的空缺(如果用“==”比较的话两者是相等的)
    • 都为假值:转化为布尔值时都为假值

3.5 全局对象(global object)

  • JavaScript解释器启动时(或者任何Web浏览器加载新页面时),它将创建一个新的全局对象,全局对象包含
    • 全局属性:比如undefinedInfinityNaN
    • 全局函数:比如isNaN()parseInt()eval()
    • 构造函数:比如Date()RegExp()String()ObjectArray()
    • 全局对象:比如MathJSON

全局对象的初始属性不是保留字,但他们应该当做保留字来对待

3.6 包装对象

  • 对象
    • 属性:与JavaScript对象相关的值,可用“.”符号来引用属性值
    • 方法:属性的子集,当属性值为函数时,称其为“方法”
  • 字符串也同样具有属性和方法
var s="Hello World!";
var word=s.substring(s.indexOf("")+1,s.lenth);

字符串既然不是对象,为什么它也会有属性?
答:只有引用字符串s的属性时,JavaScript会调用new String(s)创建一个字符串的包装对象,来处理字符串属性的引用,引用结束后这个包装对象就销毁了。

  • 包装对象:存取字符串、数字和布尔值的属性时创建的临时对象称为包装对象
var s="test";
s.len=4;
var t=s.len;
console.log("t="+t); 
//控制台输出:t=undefined
//这是因为4赋给的是一个新创建的包装对象,而s.len又是作为另一个包装对象赋给t

3.7 不可变的原始值和可变的对象引用

  • 引用类型:对象的别称,用来和JavaScript的基本类型区别开来
  • 对象的复制、比较都是引用的复制、比较
var a=[1,2,3];
var b=a;
b===a;  //=>true,说明a和b引用同一个数组对象
  • 如果真的想得到对象的副本,就必须显式赋值对象的每个属性
var a=['a','b','c'];
var b=[];
for (var i=0;i<a.lenth;i++){
    b[i]=a[i];
}
  • 如果真的想比较,则必须比较它们的属性或元素

3.8 类型转换

字符串 数字 布尔值 对象
undefined "undefined" NaN false throws TypeError
null "null" 0 false throws TypeError
"" 0 false new String("")
"1.2" 1.2 true new String("1.2")
"one" NaN true new String("one")
{} 参考3.8.3节 参考3.8.3节 true
[] "" 0 true
[9] "9" 9 true
['a'] 使用join()方法 NaN true
function(){} 参考3.8.3节 NaN true

3.8.1 转换和相等性

4.9.1节详细讲解了 "="运算符做了哪些类型转换,同样介绍了 "="恒等运算符并未做任何类型转换

3.8.2 显式类型转换

  • 为什么要做显式转换?不得已或使代码易读
  • 最简单的方法:使用Boolean()Number()String()Object()
    var a=Number("3");
	var b=String(false);
	var c=Boolean([]);
	var d=Object(3);
	console.log(a); //结果为:3
	console.log(b); //结果为:"false"(或使用false.toString()方法)
	console.log(c); //结果为:true
	console.log(d); //结果为:new Number(3)

值得注意的是,除了null和undefined之外任何值都具有toString()方法

  • Number类的toString()方法可以指定转换基数(范围是2~36进制)

Java无此特性

    binaryString=a.toString(2);
	octalString=a.toString(8);
	hexString=a.toString(16);
	console.log(binaryString); //结果为:"10001"
	console.log(octalString); //结果为:"21"
	console.log(hexString); //结果为:"11"
  • Number类处理小数时
    • toFixed()方法:可指定小数点后位数,默认
    • toExponential()方法:使用指数计数法,小数点前只有一位,可指定小数点
    • toPrecison()方法:上述两种方法的体现,可指定有效位数,位数小于数字整数部分时会转换为指数计数形式。
    var n=123456.789;
	console.log(n.toFixed());  //123457
	console.log(n.toFixed(0)); //123457
	console.log(n.toFixed(2)); //123456.79
	console.log(n.toFixed(5)); //12345.78900

	console.log(n.toExponential()); //1.23456789e+5
	console.log(n.toExponential(1)); //1.2e+5
	console.log(n.toExponential(3)); //1.235e+5
	
	console.log(n.toPrecision()); //123456.789
	console.log(n.toPrecision(4)); //1.235e+5
	console.log(n.toPrecision(7)); //123456.8
	console.log(n.toPrecision(10)); //123456.7890
  • parseInt()函数和parseFloat()函数(它们是全局函数,不从属于任何类的方法)更加灵活。parseInt()只解析整数,而parseFloat()则可以解析整数和浮点数。如果字符串前缀是"0x“或者”0X",parseInt()将其解释为十六进制数,parseInt()parseFloat()都会跳过任意数量的前导空格,尽可能解析更多数值字符,并忽略后面的内容。如果第一个非空格字符是非法的数字直接量,将最终返回NaN
    parseInt("3 blind mice")//=>3
    
    parseFloat("3.14 meters")//=>3.14
    
    parseInt("-12.34")//=>-12
    
    parseInt("0xFF")//=>255
    
    parseInt("0xff")//=>255
    
    parseInt("-0XFF")//=>-255
    
    parseFloat(".1")//=>0.1
    
    parseInt("0.1")//=>0
    
    parseInt(".1")//=>NaN:整数不能以"."开始
    
    parseFloat("$72.47");//=>NaN:数字不能以"$"开始

parseInt()可以接收第二个可选参数,这个参数指定数字转换的基数,合法的取值范围是2~36,例如:

parseInt("11",2);//=>3(1*2+1)

parseInt("ff",16);//=>255(15*16+15)

parseInt("zz",36);//=>1295(35*36+35)

parseInt("077",8);//=>63(7*8+7)

parseInt("077",10);//=>77(7*10+7)

3.8.3 对象转换为原始值

  • 对象–>布尔值:所有的对象(包括数组和函数)都转换为true
  • 对象–>字符串\数字:
    • toString()方法:
    • valueOf()方法:如果存在任意原始值,它就默认将对象转换为表示它的原始值。对象是复合值,而且大多数对象无法真正表示为一个原始值,因此默认的valueOf()方法简单地返回对象本身,而不是返回一个原始值。数组、函数和正则表达式简单地继承了这个默认方法,调用这些类型的实例的valueOf()方法只是简单返回对象本身。日期类定义的valueOf()方法会返回它的一个内部表示:1970年1月1日以来的毫秒数。

这里提到的字符串和数字的转换规则只适用于本地对象(native object)。宿主对象(例如,由Web浏览器定义的对象)根据各自的算法可以转换成字符串和数字。

3.9 变量声明

  • 关键字var声明变量
<!-- 可以声明一个或多个 -->
var i;
var j,k;
<!-- 可以将变量的初始赋值和声明合写在一起 -->
var message="hello";
var i=0,j=0,k=0;

如果声明了变量但为赋值的话,那么它的初始值就为undefined

3.10 变量作用域

  • 变量的作用域:程序源代码中定义这个变量发挥作用的区域
    • 全局变量拥有全局作用域
    • 局部变量只有局部作用域

局部变量的优先级高于同名的全局变量

3.10.1 函数作用域或声明提前

  • 函数作用域:函数体中,不管变量在哪里声明,它在整个函数体中都算是有定义的

JavaScript没有像C语言这样的块级作用域的概念,取而代之的是函数作用域

function f1(){
    for(var k=0;k<10;k++){ //k在整个函数体内都算是有定义的,而不仅仅是在循环内
        console.log(k)
    }
    cosole.log(k); //k已经定义了,输出10
}
  • 声明提前:变量在声明之前就可以使用了,不过值可能为undefined
var scope = "global";
function f2(){
    console.log(scope);//输出“undefined”,而不是“global”
    var scope="local";
    console.log(scope);//输出“local”
}

3.10.2 作为属性的变量

  • 当声明一个JavaScript全局变量时,实际上是定义了全局对象的一个属性。

当使用var声明一个变量时,创建的这个属性是不可配置的(见6.7节),也就是说这个变量无法通过delete运算符删除。可能你已经注意到了,如果你没有使用严格模式并给一个未声明的变量赋值的话,JavaScript会自动创建一个全局变量。以这种方式创建的变量是全局对象的正常的可配值属性,并可以删除它们:

var truevar=1;//声明一个不可删除的全局变量

fakevar=2;//创建全局对象的一个可删除的属性

this.fakevar2=3;//同上

delete truevar//=>false:变量并没有被删除

delete fakevar//=>true:变量被删除

delete this.fakevar2//=>true:变量被删除

3.10.3 作用域链

  • 换个角度来解读变量作用(将局部变量也看做对象的属性):每一段JavaScript代码(全局代码或函数)都有一个与之关联的作用域链(scope chain)。这个作用域链是一个对象列表或者链表,这组对象定义了这段代码“作用域中”的变量。当JavaScript需要查找变量x的值的时候(这个过程称做“变量解析”(variable resolution)),它会从链中的第一个对象开始查找,如果这个对象有一个名为x的属性,则会直接使用这个属性的值,如果第一个对象中不存在名为x的属性,JavaScript会继续查找链上的下一个对象。如果第二个对象依然没有名为x的属性,则会继续查找下一个对象,以此类推。如果作用域链上没有任何一个对象含有属性x,那么就认为这段代码的作用域链上不存在x,并最终抛出一个引用错误(ReferenceError)异常。

  • 只有全局变量

graph LR
A(全局对象)-->B(var global1)
A-->C(var global2)
A-->D(var global3)

  • 不含嵌套的函数体内
graph TD
A(定义函数参数和局部变量的对象)-->B(全局对象)
  • 含有嵌套函数的函数体
graph TD
A(定义嵌套函数参数和局部变量的对象)-->B
B(定义函数参数和局部变量的对象)-->C(全局对象)

发布了25 篇原创文章 · 获赞 5 · 访问量 4579

猜你喜欢

转载自blog.csdn.net/Cap220590/article/details/104462726