《JavaScript详解-第二版》由 [美] Ellie Quigley 著,《JavaScript权威指南 第六版》由淘宝前段译,此文是对上述两本书的阅读笔记。
脚本的安装
JavaScript与旧浏览器或受限的浏览器
隐藏JavaScript
1 | <html> |
第4行必须以双斜线开始,尽管第2行不用,这是因为JavaScript会把“–”看成一个特殊符号,造成错误
noscript标签
当浏览器被禁止JavaScript时,<noscript>
中的内容就会被显示出来,提示用户换用可以支持JavaScript的浏览器;但是当浏览器支持或打开JavaScript后,这段文字就会不再显示。
数据类型及其字面量、变量、常量
数据类型
原始类型
有三种普通原始类型:
- 数值型(number)
- 字符串型(string,只有字符串型,无字符型)
- 布尔型
两种特殊原始数据类型:
- null(当使用typeof()时,会表明它是一个特殊对象)
- undefined
数值型
JavaScript中采用浮点表示方法表示浮点数值,也就是说所能表示的浮点数值均是近似值;好在,近似值带来的误差问题,只有在比较两个值是否相等的时候才会出现。
会造成如下情况
1 | var x=.3-.2; //30美分减去20美分 |
1 | var ca = 0.3 , |
对象类型
- 对象
- 数组(一个特殊的对象)(Array)
- 全局对象(一个特殊的对象)
- 函数(一个特殊对象)
如果函数用来初始化(使用new运算符)一个新建的对象,我们称之为构造函数(constructor)。每个构造函数定义了一类(class)对象——由构造函数初始化的对象组成的集合。类可以看做是对象类型的子类型。除了数组(Array)类和函数(Function)类之外,JavaScript语言核心定义了其他三种有用的类。日期(Date)类定义了代表日期的对象。正则(RegExp)类定义了表示正则表达式(一种强大的模式匹配工具)的对象。错误(Error)类定义了那些表JavaScriy程序中运行时错误和语法错误的对象。可以通过定义自己的构造函数来定义需要的类。
JavaScript语言核心包括Date()构造函数,用来创建表示日期和时间的对象。这些日期对象的方法为日期计算提供了简单的API。日期对象不像数字那样是基本数据类型。
1
2
3
4
5
6
7
8
9
10>var then=new Date(2011,0,1);//2011年1月1日
>var later=new Date(2011,0,1,17,10,30);//同一天,当地时间5:10:30pm,
>var now=new Date();//当前日期和时间
>var elapsed=now-then;//日期减法:计算时间间隔的毫秒数
>later.getFullYear()//=>2011
>later.getMonth()//=>0:从0开始计数的月份
>later.getDate()//=>1:从1开始计数的天数
>later.getDay()//=>5:得到星期几,0代表星期日,5代表星期一
>later.getHours()//=>当地时间17:5pm
>later.getUTCHours()//使用UTC表示小时的时间,基于时区需要注意
- 在字符串中使用单引号和双引号都可以
- 在字符串中同一种引号需要成对出现,不能混搭
- 字符串变量中可以使用
\n
这种转义序列 - 其中有个特殊值
NaN
,被称为非数字,和任何操作数都不相等;NaN
和Infinity
属于JavaScript内置的两个全局变量,其基本数据类型为“Number”分别用来表示“非数字”、“无穷大”。 - 布尔类型的字面量是逻辑值,
ture
或false
、0
或1
、开
或关
null
是一个关键字,而且是对象类型的关键字undefined
不是一个关键字null
代表空,和表示没定义的undefined
不同;前者不会报错,后者会报错;例如,检测一个已声明但未初始化的变量其值是undefined
,且报错;但是可以赋值为null
,null
将作为一个占位符null
和undefined
在==
比较中是相同的,但是在===
中是不同的typeof()
是一个可以检测数据是上述数据类型中的哪一种的运算符- 数组与字符串的区别至少有一个,数组属于可变类型,而字符串属于不可变类型。也就是说一个是常量而另一个是变量。
- 数字可以使用“十进制”,也可以使用“十六进制”,但是“八进制”是不被允许使用的。
变量
定义变量可以使用var
声明,也可以不使用var
声明(严格模式下会报错,容易产生bug,应该尽量使用var
);但是不进行初始化的变量必须使用var
声明。
1
2
3
4var a = "nihao" ; // 正确
a = "nihao" ; // 正确 全局有效
var a ; // 正确
a ; // 错误
常量
定义常量使用const
,常量的量不会随程序进行而改变
1
2
3
4const a = 10;
console.log(a); // a = 10;
a = a + 10;
console.log(a); // a = 10; 不会改变
包装对象
存取字符串、数字或布尔值的属性时创建的临时对象称作包装对象。通常包装对象只被认为为一种实现细节,而不用过分关注。需要明白包装对象有别于对象。
需要注意的是:可以通过String()、Number()、Boolean()构造函数来显示的创建包装对象。
1 | var s = "test", |
类型转换
原始值转换为对象:如上所述,利用构造函数。
对象转化为原始值:分很多情况,但是这里要说的是,显示类型转化中的利用不带new
的全局函数转换
1 | Number(“3”); //字符串转为数字,还有一个专门用于数字与字符串的相互转化的函数,toString和toFixed toExponential toPrecision。 |
变量作用域
函数作用域和声明提前
JavaScript和C普通的语言特性,其中一点就是C有块级作用域而JavaScript没有,这就导致一个有意思的现象——声明提前。
1 | function f() |
等同于
1 | function f(){ |
对话框
与用户交互的方法
write()
writeln()
alert()
弹出警告框prompt()
信息输入框confirm()
确认对话框
运算符
比较型运算符
原始值的比较是值的比较:只有在他们的值相等时他们才相等。
对象的比较并非值的比较:是引用的比较,即使不同的对象包含有相同的属性以及相同的值也不同,除非两个对象的引用同一个基对象。
注:
原始值包含:undefined、null、布尔值、数字、字符串
对象包含:数组、函数
相等
==
和===
区别在于前者比较时做了数据类型转换,而后者未做数据类型转换。
1 | # 相等 —— 人和人之间的关系 |
等同
1 | # 等同 —— 人和克隆人之间的关系 —— 就我所知强语言类型中没有这个运算符 |
大小比较
运算符为>
、>=
、<
、<=
,当比较字符串的时候,是以字符串中的字符的ASCALL码为基础的大小比较。同一字母的大小写的ASCALL码不同,比较顺序,是从字符串的左边开始向右依次比较,ASCALL大的字符,其字符串更大。为了排除大小写的干扰,可以使用toLowerCase()
或toUpperCase()
函数把需要比较的字符串的字符统一变成小写或是大写。
- 比较运算符等众多运算符在同一语句中的时候,注意运算优先级
逻辑运算符
JS中的逻辑运算符在处理布尔值的判断时,和其他语言没有什么不同,不过在处理对象时,就需要好好梳理记忆下了
!运算符(逻辑非)
- 如果一个操作数是一个对象,返回false;
- 如果一个操作数是一个空字符串,返回false;
- 如果一个操作数是一个非空字符串,返回false;
- 如果一个操作数是一个数值0,返回true;
- 如果一个操作数是任意的非零字符,返回false;
- 如果一个操作数是null,返回true;
- 如果一个操作数是NaN,返回true;
- 如果一个操作数是undefined,返回true;
- 其实这样很容易就看出来其逻辑判断的值了
&&运算符(逻辑与)
- 对于布尔值,逻辑与是非常简单的,只要有一个false,就返回false;
- 对于不是布尔值的情况则:
- 如果第一个操作数是对象,则返回第二个数
- 如果第二个操作数是对象,则只有在第一个操作数的求值结果为true的情况下才会返回该对象;
- 如果第两个操作数都是对象,则返回第二个数操作数
- 如果有一个操作数是null,则返回null
- 如果有一个操作数是NaN,则返回第NaN
- 如果第一个操作数是undefined,则返回undefined
- ps:其实仔细想一下,逻辑运算符操作对象遵循从左到右的顺序来判断,逻辑与操作符(&&)先判断第一个数,如果第一个数的逻辑判断是true,则还需判断第二个数,结果输出第二个操作数;同理,如果第一个数为false,则不用考虑第二个数了,直接输出第一个数的逻辑判断结果,这和其他语言原理都是一样的。
||运算符(逻辑或)
- 对于布尔值,逻辑或是非常简单的,只要有一个true,就返回true;
- 对于不是布尔值的情况则:
- 如果第一个操作数是对象,则返第一个操作数
- 如果第一个操作数的求值结果为false,则返回第二个操作数
- 如果两个操作数都是对象,则返回第一个操作数
- 如果两个操作数是null,则返回null
- 如果两个操作数是NaN,则返回NaN
- 如果两个操作数是undefined,则返回undefined
- ps: 原理同逻辑与(&&),逻辑或(||)的判断是如果第一个操作数的逻辑判断为true,则直接输出第一个操作数,不用再考虑第二个操作数;如果第一个操作数的逻辑判断为false,则还得去判断第二个操作数的逻辑。
条件运算符
x?y:z
位运算符
运算符 | 函数 | 示例 | 规则 |
---|---|---|---|
& | 位与 | x&y | 相应位都为1,则返回1 |
位或 | x|y | ||
^ | 异或 | x^y | 相应位只有一个1才返回1,否则返回0 |
~ | 位非 | ~x | 按位取反 |
<< | 左移 | x<<y | 将二进制位向左移y,右边补零 |
| 右移 | x>>y |移出的位丢弃
| 右移补零 | x>>>y |移出的位丢弃,左边以零填充
javascript中负数的算术右移和逻辑右移都十分的让人迷惑,特别是逻辑右移>>>,你会发现即使一个很小的负数,右移之后,也会得到一个无比巨大的数,这是为什么呢?
原来在逻辑右移中符号位会随着整体一起往右移动,这样就是相当于无符号数的移动了,最后得到的就是一个正数,因为符号位不存在了。首先逻辑右移产生的一定是32位的数,然后负数的符号位为1,这意味着从第32位到符号位的位置全部由1填充,这样的数能不大吗例如-1,逻辑右移0位表现形式就是1111 1111 1111 1111 1111 1111 1111 1111 ,这样的数是当作正数来对待的!所以将-1逻辑右移N位,最后的结果都是全为1!
左移运算保留数字的符号位。例如,如果把 -2 左移 5 位,得到的是 -64,而不是 64。“符号仍然存储在第 32 位中吗?”是的,不过这在 ECMAScript 后台进行,开发者不能直接访问第 32 个数位。即使输出二进制字符串形式的负数,显示的也是负号形式(例如,-2 将显示 -10。)
有符号右移运算
有符号右移运算符由两个大于号表示(<$lt;)。它把 32 位数字中的所有数位整体右移,同时保留该数的符号(正号或负号)。有符号右移运算符恰好与左移运算相反。例如,把 64 右移 5 位,将变为 2:
1
2 >var iOld = 64; //等于二进制 1000000
>var iNew = iOld >> 5; //等于二进制 10 十进制 2同样,移动数位后会造成空位。这次,空位位于数字的左侧,但位于符号位之后。ECMAScript 用符号位的值填充这些空位,创建完整的数字,如下图所示:无符号右移运算
无符号右移运算符由三个大于号(>>>)表示,它将无符号 32 位数的所有数位整体右移。对于正数,无符号右移运算的结果与有符号右移运算一样。
用有符号右移运算中的例子,把 64 右移 5 位,将变为 2,如下:
无符号右移运算用 0 填充所有空位。对于正数,这与有符号右移运算的操作一样,而负数则被作为正数来处理。
由于无符号右移运算的结果是一个 32 位的正数,所以负数的无符号右移运算得到的总是一个非常大的数字。例如,如果把 -64 右移 5 位,将得到 134217726。如果得到这种结果的呢?
要实现这一点,需要把这个数字转换成无符号的等价形式(尽管该数字本身还是有符号的),可以通过以下代码获得这种形式:
var iUnsigned64 = -64 >>> 0;
然后,用 Number 类型的
toString()
获取它的真正的位表示,采用的基为 2:代码如下:
alert(iUnsigned64.toString(2));
这将生成 11111111111111111111111111000000,即有符号整数 -64 的二进制补码表示,不过它等于无符号整数 4294967232。
出于这种原因,使用无符号右移运算符要小心。
负数的算术右移
我们发现
-9>>2=-3
,为什么是-3
呢?首先符号位是不变的,不参加右移,然后在9右移的过程中,最低位为1的话,那么右移之后最低位仍然为1!这是很奇怪的。