犀牛书思考笔记(一)

写作目的:

1
2
3
4
5
重新阅读了JavaScript经典书籍---《JavaScript权威指南》,有很多收获。纪录在此。
内容可能包括:
- 与其他语言的一些重要区别
- 需要注意的一些细节问题
- 还不太理解的地方

chapter2


区分大小写

如题,JS是区分大小写的


如果你跟我一样,是先开始学习HTML的,再写JS的,那你肯定遇到过下面这种情况:

1
<input type="button" onClick="f()" />

这段代码如果放在HTML的非JS模块中,是正确的,但是如果放在JS模块中会报错。
这是因为:

  • HTML不区分大小写,而且很多人习惯上写成onClick
  • 但是JS区分大小写,必须写成小写才正确,如果上述语句放在JS模块中,那记得写成onclick

博主在某在线网站写代码的时候就不知道为啥一会大写一会小写,现在也是终于明白了

1
2
3
建议:
- 如果真要学习一门语言,视频和在线教程都只能是辅助。还是要从一本经典的书开始学起。这样对语言的细节才能把握的更好
- 学了这么久,前端语言比起JavaCC++这些编程语言更加复杂,由于HTML的“宽容性”以及浏览器兼容性问题,我觉得平时就要积累这些不同点和细节问题。

可选的分号

JS规定:语句用分号来结束,但如果语句各自独占一行,分号可以省略

  • 这个和编程风格有关,但是我个人喜欢全部写上分号(可能是最初学习C的时候保留下来的习惯)而且我也建议这样做,因为如果省略了分号有时候会出一些逻辑错误(就像书上说的)

chapter3

这一章有很多细碎的知识,且有几个概念与以前学过的编程语言有很大不同。


数据类型

JS的数据类型分为两类:原始类型和对象类型,首先我们先看第一类

原始类型

原始类型包括:数字、字符串、布尔值、null、unfefined

  • 数字
    • JS中的所有数字均用浮点数值表示(IEEE 754:一种二进制表示法,可以精确的表示分数)
      • 这样做有一个问题,看下面代码
1
2
3
4
var x=.3 - .2;
var y=.2 - .1;
x == y
结果:flase

所以我们应该注意:这会带来舍入误差

- 也有一个好处,可以正确计算除法
1
2
console.log(5/2);
result:2.5
上述代码如果在Java中,两个数都是Int型的话,结果是2
  • 全局变量NaN:表示非数字值
    • 它和任何值都不相等,包括自身
      所以我们判断一个值是不是数字的时候要用下述两个方法:
1
2
x!=x //当且仅当x为NaN的时候,表达式值为真
isNaN() //若参数为非数字或者NaN时返回真
  • 字符串

    • JS的内置功能之一是字符串连接,+ 如果用于字符串,则表示字符串连接
    • 在JS中,字符串可以当做只读数组,这个很方便,我们可以这样来访问字符串任意位置的元素:s[i]
  • 布尔值

    • 任意JS的值都可以转换为布尔值
      • undefined\null\0-0\NaN\’’\这六种值转化为false
      • 其余全部转化为true

JS中的类型转换很复杂,要单独拿出来说,这里先不写

对象类型

除了原始数据类型,其他的都是对象类型

  • 对象是“命名值”的集合
  • JS中类的概念可以看做是对象类型的子类型
  • 所以JS中还定义了一些特殊的类
    • 数组类(注:只有这个是表示带编号的值的有序集合)
    • 函数类
    • 日期类
    • 正则类
    • 错误类
类型的其他分法
  1. 可变类型和不可变类型
    • 可变类型:值可修改
      • 对象
      • 数组
    • 不可变类型:数字、布尔、null、undefined、字符串
1
注:不可变类型的值无论何时都不能被改变,字符串的一些改变内容的方法返回的都是新的字符串,原来的字符串还是没有改变
  1. 拥有方法的类型和不能拥有方法的类型
    这里涉及到一个很大的不同之处,按理,只有对象才能拥有方法,但在JS中,数字、字符串、布尔值也拥有自己的方法,这个叫包装对象
    • 拥有方法的类型:
      • 对象
      • 数字
      • 字符串
      • 布尔值
        看下面的代码:
1
2
3
4
var s = "test";
s.len = 4;
var t= s.len;
result:t= undefined
1
为什么会这样呢,这个就叫包装对象。只要引用了字符串的属性,JS就会调用new Stirng(s)转换成对象,这个对象继承了字符串的方法,并被用来处理属性的引用。一旦属性引用结束,新建对象就会销毁。所以上面代码中,第二行代码创建一个临时字符串对象,并给其len属性赋值为4,随即销毁了这个对象。而第三行通过原始的字符串值创建了一个新的对象t,属性的值还没有定义所以自然就是undefined。
1
所以,在读取字符串、数字、布尔值的属性或方法的时候,表现的像对象一样,但是如果你试图给其属性赋值,则会忽略这个操作,修改只是发生在临时对象身上,而临时对象不会保留。可以通过String()\Number()\Boolean()显式创建包装对象。且===将原始值和包装对象视为不等,因为他们类型不同
  • 不拥有方法的类型
    • null
    • undefined
JS中的变量无类型

你可能注意到了,只要用var关键字定义,你要它是什么类型它就是什么类型。

null和undefined比较特殊。单独再说

变量作用域

  1. JS中的变量作用域也比较特殊,它没有所谓块级作用域,代替为函数作用域
  • 函数作用域:在函数内声明的所有变量在函数体内始终是可见的,意味着变量在声明之前甚至已经可用(称为声明提前)
  • 将函数内的声明提前至函数体顶部,同时变量初始化留在原来的位置
1
2
3
4
5
6
var scope = "global";
function f(){
console.log(scope); //输出undefined
var scope = "local";
console.log(scope); //输出local
}
  1. 另外一点比较特殊的是,JS声明一个全局变量时,实际上是定义了全局对象的一个属性。如果这个变量你用了关键字var声明,那么这个变量无法通过delete运算符删除。如果你给一个未声明的变量赋值,则JS会自动创建一个全局变量,且这个变量可以通过delete删除。
  2. JS允许使用this关键字来引用全局变量

chapter4

这一章讲的是表达式和运算符,还是从一些不同角度来说明


属性访问

我们一般默认点号(.)是用来访问对象属性,而方括号([])是用来访问数组元素的,但是在JS中不是这样。对象的属性也可用[]来访问,不过两种访问方法是有区别的

1
2
3
4
var 0 = {x:1,y:{z:3}};
o.x //1
o.y.z //3
o["x"] //1
  • 如果属性名称是一个保留字或者包含空格和标点符号,或是一个数字(对数组来说),则必须使用方括号[]的写法
  • 当属性名是通过运算得出的值而不是固定的值的时候,必须使用方括号

当数字遇到数字字符串

什么意思呢,我们先来看一段代码

1
2
3
4
5
6
1 + 2 //3
"1" + "2" //"12"
"1" + 2 //"12" 数字转换为字符串
11 < 3 //false
"11" < "3" //true
"11" < 3 //false 字符串转换为数字

通过上面例子我们可以看出:

  • 加号运算符更加偏爱字符串,会优先把数字转化成字符串
  • 而比较运算符则更偏爱数字,会优先把字符串转换成数字

=和==和===

刚开始看到一会用==,一会用===博主也有点晕,实际我们来看看他们的区别

  • “=” 赋值运算
  • “==” 比较运算符,进行值比较,如果两个操作数不是同一类型,会先尝试做自动类型转换,再进行比较
    • null == undefined
    • 数字与字符串比较,先将字符串转换为数字再比较
    • true转换为1,false转换为0
    • 对象与原始值比较,先按规则将对象转换为原始值再比较
  • “===” 严格相等比较运算符,比较过程中不做自动类型转换
    • null 和undefined 不等于自身
    • NaN和其它任何值都不等,包括自身
    • 0 === -0
    • 两个引用值指向同一个对象、数组或函数,则相等

eval()这里博主愚笨,没看懂这个函数到底是干嘛的,还望有人指点


chapter5

这一章讲的是语句。学习过其它语言的应该都不陌生。


  • switch中 case子句的表达与switch关键字后的expression匹配时,是按照’===’运算符进行比较的
  • 没太懂标签语句的用法

continue语句

这里涉及到for语句和while语句等价的问题,需要探讨一下

  • continue 语句的作用是跳过本次循环,执行下一次循环,但在不同的循环中,有一些细节差异
    • 在while循环中,在循环开始处指定的expession会重复检测,如果检测结果为真,循环体会从头开始执行
    • 在do/while循环中,程序的执行直接跳转到循环结尾处,这时会重新判断循环条件,之后才会继续下一次循环
    • 在for循环中,首先计算自增表达式,然后再次检测test表达式,用以判断是否执行循环体