0%

JavaScript详解

《JavaScript详解-第二版》由 [美] Ellie Quigley 著,《JavaScript权威指南 第六版》由淘宝前段译,此文是对上述两本书的阅读笔记。

脚本的安装

JavaScript与旧浏览器或受限的浏览器

隐藏JavaScript

1
2
3
4
5
6
7
8
9
10
11
12
<html>
<head>
<title>Old browsers</title>
</head>
<body>
<script type="text/javascript">
<!-- 在较落后的浏览器中隐藏JavaScript
document.write("<h2>welcome to maine</h2>");
//结束在较老的浏览器中隐藏JavaScript-->
</script>
</body>
</html>

第4行必须以双斜线开始,尽管第2行不用,这是因为JavaScript会把“–”看成一个特殊符号,造成错误

noscript标签

当浏览器被禁止JavaScript时,<noscript>中的内容就会被显示出来,提示用户换用可以支持JavaScript的浏览器;但是当浏览器支持或打开JavaScript后,这段文字就会不再显示。

数据类型及其字面量、变量、常量

数据类型

原始类型

有三种普通原始类型:

  • 数值型(number)
  • 字符串型(string,只有字符串型,无字符型)
  • 布尔型

两种特殊原始数据类型:

  • null(当使用typeof()时,会表明它是一个特殊对象)
  • undefined

数值型

JavaScript中采用浮点表示方法表示浮点数值,也就是说所能表示的浮点数值均是近似值;好在,近似值带来的误差问题,只有在比较两个值是否相等的时候才会出现。
会造成如下情况

1
2
3
4
5
var	x=.3-.2; //30美分减去20美分
var y=.2-.1; //20美分减去10美分
x==y //=>false:两值不相等!
x==.1 //=>false:.3-.2不等于.1
y==.1 //=>true:.2-.1等于.1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
var ca = 0.3 ,
cat = 4/10,
catc = 5/10,
catca = 6/10;

console.log(cat-ca);
console.log(catca-catc);
if(cat-ca==catca-catc)
{
console.log("相等");
}else{
console.log("不相等");
} //不相等

if(ca==0.3)
{
console.log("相等");
}else{
console.log("不相等");
} //不相等

if(0.3==0.3)
{
console.log("相等");
}else{
console.log("不相等");
} //相等
~
~

对象类型

  • 对象
    • 数组(一个特殊的对象)(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,被称为非数字,和任何操作数都不相等;NaNInfinity属于JavaScript内置的两个全局变量,其基本数据类型为“Number”分别用来表示“非数字”、“无穷大”。
  • 布尔类型的字面量是逻辑值,turefalse01
  • null是一个关键字,而且是对象类型的关键字
  • undefined 不是一个关键字
  • null 代表空,和表示没定义的undefined不同;前者不会报错,后者会报错;例如,检测一个已声明但未初始化的变量其值是undefined,且报错;但是可以赋值为nullnull将作为一个占位符
  • nullundefined==比较中是相同的,但是在===中是不同的
  • typeof()是一个可以检测数据是上述数据类型中的哪一种的运算符
  • 数组与字符串的区别至少有一个,数组属于可变类型,而字符串属于不可变类型。也就是说一个是常量而另一个是变量。
  • 数字可以使用“十进制”,也可以使用“十六进制”,但是“八进制”是不被允许使用的。

变量

定义变量可以使用var声明,也可以不使用var声明(严格模式下会报错,容易产生bug,应该尽量使用var);但是不进行初始化的变量必须使用var声明。

1
2
3
4
var a = "nihao" ;  // 正确
a = "nihao" ; // 正确 全局有效
var a ; // 正确
a ; // 错误

常量

定义常量使用const,常量的量不会随程序进行而改变

1
2
3
4
const a = 10;
console.log(a); // a = 10;
a = a + 10;
console.log(a); // a = 10; 不会改变

包装对象

存取字符串、数字或布尔值的属性时创建的临时对象称作包装对象。通常包装对象只被认为为一种实现细节,而不用过分关注。需要明白包装对象有别于对象。

需要注意的是:可以通过String()、Number()、Boolean()构造函数来显示的创建包装对象。

1
2
3
4
5
6
var s = "test",
n = 1,
b = true;
var S = new String(s), //由字符串(原始值类型)改成了字符串(对象类型)
N = new Number(n),
B = new Boolean(b);

类型转换

原始值转换为对象:如上所述,利用构造函数。
对象转化为原始值:分很多情况,但是这里要说的是,显示类型转化中的利用不带new的全局函数转换

1
2
3
4
Number(“3”);  //字符串转为数字,还有一个专门用于数字与字符串的相互转化的函数,toString和toFixed toExponential toPrecision。
String(false);
Boolean([]);
Object(3);

变量作用域

函数作用域和声明提前

JavaScript和C普通的语言特性,其中一点就是C有块级作用域而JavaScript没有,这就导致一个有意思的现象——声明提前。

1
2
3
4
5
6
function f()
{
console.log(scope);
var scope = “local”;
console.log(scope);
}

等同于

1
2
3
4
5
6
function f(){
var scope; //声明提前
console.log(scope);
scope = “local”;
console.log(scope);
}

对话框

与用户交互的方法

  • write()
  • writeln()
  • alert() 弹出警告框
  • prompt() 信息输入框
  • confirm() 确认对话框

运算符

比较型运算符

原始值的比较是值的比较:只有在他们的值相等时他们才相等。
对象的比较并非值的比较:是引用的比较,即使不同的对象包含有相同的属性以及相同的值也不同,除非两个对象的引用同一个基对象。
注:
原始值包含:undefined、null、布尔值、数字、字符串
对象包含:数组、函数

相等

=====区别在于前者比较时做了数据类型转换,而后者未做数据类型转换。

1
2
3
4
5
6
7
8
9
10
11
12
13
# 相等 —— 人和人之间的关系 
"William" == "William" //true
"william" == "William" //false,大小写
5 == 5.0 //true
"54" == 54 //true,和强类型语言不同的地方之一
"5.4" == 5.4 //true,不管类型是否相同,只要组成的字符是相同的就为true
NaN == NaN //false,NaN和任何操作数都不相等,哪怕它自己
null == null //true
-0 == +0 //true
false == false //true
true == 1 //true
true == '1' //true
null == undefined //true

等同

1
2
3
4
5
6
7
8
9
10
11
12
13
# 等同 —— 人和克隆人之间的关系 —— 就我所知强语言类型中没有这个运算符
"William" === "William" //true
"william" === "William" //false
5 === 5.0 //true
"54" === 54 //false
"5.4" === 5.4 //false
NaN === NaN //false,NaN和任何操作数都不等同,哪怕它自己
null === null //true
-0 === +0 //true
false === false //true
true === 1 //false
true === '1' //false
null === undefined //false

大小比较

运算符为>>=<<=,当比较字符串的时候,是以字符串中的字符的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!这是很奇怪的。