文章目录
第1章:什么是JavaScript
1. JavaScript实现
- 核心(ECMAScript):由ECMA-262定义并提供核心功能
- 文档对象模型(DOM):提供与网页内容交互的方法和接口
- 浏览器对象模型(BOM):提供与浏览器交互的方法和接口
2. ECMAScript
- ECMA-262定义的一种语言基准,在此基础上构建更稳健的脚本语言
- Web浏览器只是ECMAScript实现的一种宿主环境(host environment),Node.js是服务器端JavaScript平台,也是一种宿主环境。
- ECMA-262定义了:语法,类型,语句,关键字,保留字,操作符,全局对象
3. DOM
- 文档对象模型,Document Object Model,是API。
- DOM将页面抽象为一组分层节点,创建表示文档的树。
4. BOM
- 浏览器对象模型,也是API。
- 通常把任何特定于浏览器的扩展都归在BOM的范畴内。
- 没有标准,HTML5之后,BOM实现细节日趋一致。
第2章:HTML中的JavaScript
1.<Script>元素
- 将JavaScript插入HTML的主要方法是使用<script>元素
- 是HTML规范
- <script>元素有8个属性
- async:可选。立即开始下载脚本,但不阻止其他页面动作,异步。
- charset:可选。使用src属性指定的代码字符集,很少用,因为大多数浏览器不care它的值。
- crossorigin:可选。配置相关请求的CORS(跨资源共享)设置,默认不使用CORS。
- defer:可选。表示脚本可以延迟到文档完全被解析和显示之后再执行。
- integrity:可选。用于确保内容分发网络(CDN,Content Delivery Network)不会提供恶意内容。
- language:废弃。
- src:可选。表示包含要执行的代码的外部文件的URL,可以跟网页在同一台服务器上,也可以位于完全不同的域。
- type:可选。代替language,表示代码块中脚本语言的内容类型(也称MIME类型)。例:“text/javascript”
- 使用方式
- 网页中嵌入JavaScript代码
- 在网页中包含JavaScript文件
- 使用了src属性的<script>元素不应该再在<script>和</script>标签中再包含其他JavaScript代码。如果两者都提供的话,浏览器只会下载并执行脚本文件,忽略行内代码。
- 建议将JavaScript引用放在<body>元素中的页面内容后面,因为页面在浏览器解析到<body>的起始标签时开始渲染,放在前面会导致页面渲染的明显延迟。
- 推迟执行脚本:defer属性。该属性只对外部脚本文件有效。HTML5规范要求脚本应该按照出现顺序执行,所以第一个defer的脚本会在第二个defer的脚本之前执行。但实际上不一定,so最好只包含一个defer脚本。因为有的浏览器会忽略这个属性,所以还是把要推迟执行的脚本放在页面底部比较好。
- 异步执行脚本:async属性。也只适用于外部脚本。但是标记为async的脚本不能保证按照出现次序执行,因为它们之间没有依赖关系。
- 动态加在脚本:使用DOM API,通过向DOM中动态添加script元素加载指定的脚本。只要创建一个script元素并将其添加到DOM即可。这种方式以异步加载,相当于添加了async属性,但是不是所有的浏览器都支持这个属性,所以可以将其设置为同步加载。
let script =document.createElemnet('script')`` script.src='gibberish.js' script.async = false; // 设置为同步加载 document.head.apendChild(script)
这种方式获取的资源对浏览器的预加载器不可见,严重影响性能。要想让预加载器知道动态请求文件的存在,可以在文档头部显式声明它们:
<link rel="preload" href="gibberish.js">
- XHTML中的变化:XHTML中使用JavaScript必须指定type属性且值为text/javascript。XHTML模式会在页面的MIME类型被指定为"application/xhtml+xml"时触发。
2.行内代码于外部文件
- 推荐使用外部文件,理由如下
- 可维护性。用一个目录保存所有的js文件,更容易维护,开发者可以独立于使用它们的HTML页面来编辑代码。
- 缓存。浏览器会根据特定的设置缓存所有外部链接的js文件,所以如果两个页面都用到同一个文件,只需要下载一次,从而使页面加载更快。
- 适应未来。包含外部js文件的语法在HTML和XHTML中一样。
3.文档模式
- 使用doctype切换文档模式
- 混杂模式(quirks mode):省略文档开头的doctype声明
- 标准模式(standards mode)
- 准标准模式(almost standards mode)
4.<noscript>元素
- 用于给不支持js的浏览器提供替代内容。
- 如今的浏览器已经100%支持js
- 对于禁用js的浏览器来说,这个元素有用
第3章 语言基础
1.语法
- 区分大小写
- 标识符:
- 可以由一个或多个字符组成,第一个字符必须是一个字母,下划线(_)或美元符号($),剩下的其他字符可以是字母、下划线、美元符号或数字。
- 按照惯例使用驼峰大小写形式,不是强制性的。
- 关键字、保留字、true、false和null不能作为标识符。
- 注释:
- 单行注释:// 注释内容
- 多行注释:/* 注释内容 */
- 严格模式(strict mode):
- "use strict"启用严格模式,可以对整个脚本启用,也可以对指定函数启用,这是一个预处理指令。
- 所有现代浏览器都支持严格模式。
- 语句
- 以分号结尾,没有分号意味着由解析器确定语句在哪结尾,也有效,但不推荐。
- 多条语句可以合并到一个代码块中,代码块由 ‘{’ 标识开始,'}'标识结束。
- if之类的控制语句只在执行多条语句时要求必须有代码块,但推荐加代码块,减少出错。
2.关键字与保留字
- 关键字
- 保留字
3.变量
- var关键字
- 可以保存任何类型的值,没有初始化的情况下保存一个特殊值undefined
- 可以同时定义变量并设置它的值
- 可以改变保存的值,也可以改变值的类型
- var声明作用域:在函数内部定义的变量只在函数内部有效,在函数内定义变量时省略var操作符,可以创建一个全局变量,但不推荐这么做。
- 定义多个变量可以在一条语句中用逗号分隔
- var声明提升:var声明的变量会自动提升到函数作用域顶部
- 反复使用var声明同一个变量也没有问题
- let声明
- let声明的范围是块作用域,而var声明的范围是函数作用域,块作用域是函数作用域的子集
- let不允许同一个块作用域中出现冗余声明
- 暂时性死区:let与var的一个重要区别是,let声明的变量不会在作用域中被提升。let声明之前的执行瞬间被称为“暂时性死区”(temporal dead zone),在此阶段引用任何后面才声明的变量都会抛出ReferenceError。
- 全局声明:let在全局作用域中声明的变量不会成为window对象的属性(var声明的变量会)。
- 条件声明:不能使用let进行条件式声明(条件声明是一种反模式)
- for循环中的let声明:使用let声明for循环中的迭代变量,则变量的作用域仅限于for循环块内部,不会出现渗透到循环体外部的情况。
- const声明
- 声明变量时必须同时初始化变量。
- 尝试修改const声明的变量会导致运行时错误。不能声明迭代变量。
- 不允许重复声明。
- 声明的作用域也是块。
- const声明的限制只适用于它指向的变量的引用,可以修改这个对象内部的属性。
- 声明风格及最佳实践
- 不使用var
- const优先,let次之
4.数据类型
- 简单数据类型:Undefined、Null、Boolean、Number、String和Symbol。
- 复杂数据类型:Object
- typeof操作符:用来确定变量的数据类型,只是一个操作符,不是函数,不需要参数(当然也可以使用参数)
- Undefined类型:
- 只有一个值,就是特殊值undefined。
- 当var和let声明了变量但是没有初始化时,默认给变量赋值undefined。
- 不必显式地将变量值设置为undefined。
- 注意:无论是声明后未赋值还是未声明,typeof返回的都是"undefined",逻辑上是对的。建议在声明变量的同时初始化,这样当typeof返回"undefined"的时候就知道是未声明而不是声明了但未初始化。
- Null类型:
- 只有一个值,特殊值null。表示一个空对象指针。
- 当一个变量要保存对象而又没有对象可保存时,将变量显式的赋值为null。
- 注意:用比较操作符(==)比较null和undefined始终返回true。
- Boolean类型:
- 有两个字面值:true,false
- Boolean()转型函数:将其他类型的值转换为布尔值
- Number类型
- 八进制:第一位必须为0,严格模式下无效。应该使用前缀0o
- 十六进制:前缀0x。
- 浮点值:
- 数值中必须包含小数点,小数点后面必须至少有一个数字。小数点前不是必须有整数,但推荐加上。
- 科学计数法表示:一个数值(整数或浮点数)后跟一个大写或小写的字母e,再加上一个要乘的10的多少次幂。例:3.125e7,3e-17。
- 值的范围:
- 最小值:Number.MIN_VALUE,大概是5e-324。
- 最大值:Number.MAX_VALUE,大概是1.797 693 134 862 315 7e+308。
- 任何无法表示的负数:-Infinity(负无穷大),Number.NEGATIVE_INFINITY
- 任何无法表示的正数:Infinity(正无穷大),Number.POSITIVE_INFINITY
- 如果计算返回正 Infinity 或负 Infinity,则该值将不能再进一步用于任何计算。
- isFinite()函数:用于确定一个数是不是有限大。
- NaN
- 不是数值,not a number。用于表示本来要返回数值的操作失败了。
- 任何涉及NaN的操作始终返回NaN。
- NaN不等于包括NaN在内的任何值。
- isNaN()函数:接受一个参数,判断该参数是否“不是数值”。
- 数值转换:有三个函数可以将非数值转换为数值
- Number():可用于任何数据类型。
- parseInt():主要用于将字符串转换为数值。可以接收两个参数,第二个参数表示底数(进制数)。建议给第二个参数。
- parseFloat():主要用于将字符串转换为数值。
- String类型
- 0个或多个16位Unicode字符序列
- 可以用双引号(")、单引号(')或反引号(`)标示
- 字符串的长度通过length属性过去,例:text.length
- toString()方法:把一个值转换为字符串。对数值调用这个方法时,可以接受一个底数参数。null和undefined值没有这个方法。
- String():当不确定一个值是不是null或undefined时用。
- 用加号操作符给一个值加上一个空字符串""也可以将其转换为字符串。
- 字符串差值:通过在${}中使用一个JavaScript表达式实现。嵌套的模板字符串无须转义,在插值表达式中可以调用函数和方法。模板也可以插入自己之前的值。
- 模板字面量标签函数(没看懂!,到底什么是标签函数啊?)第67页
- 原始字符串:String.raw标签函数,也可以通过标签函数的raw属性获得每个字符串的原始内容
- Symbol类型:(这里面蛮多不懂的地方,用到的时候再回来看)
- 符号类型,确保对象属性使用唯一标识符。
- 基本用法:
- 使用Symbol()函数初始化。let sym = Symbol();
- 调用Symbol()函数时,可以传入一个字符串参数作为对符号的描述,let othersym = Symbol(‘foo’);这个字符串参数可以用来调试代码,但是与符号定义或标识完全无关。
- 符号没有字面量语法
- Symbol()函数不能与new关键字一起作为构造函数使用
let mySymbol = new Symbol(); // TypeError: Symbol is not a constructor
- 使用全局符号注册表
- 使用Symbol.for()方法:
let fooGlobalSymbol = Symbol.for('foo');
- 在全局注册表中定义的符号跟使用Symbol()定义的符号并不等同。
- 全局注册表中的符号必须使用字符串来创建,因此作为参数传给Symbol.for()的任何值都会被转换为字符串。注册表中使用的键同时也会被用作符号描述。
let emptyGlobalSymbol = Symbol.for();
console.log(emptyGlobalSymbol); // Symbol(undefined)
- 使用Symbol.keyFor()来查询全局注册表,返回该全局符号对应的字符串键。
// 创建全局符号
let s = Symbol.for('foo');
console.log(Symbol.keyFor(s)); // foo
- 使用Symbol.for()方法:
- 使用符号作为属性(从这里开始往下的符号部分的内容没看懂,用到的时候再回来看)
- 常用内置符号
- Object类型:
- 对象实际就是一组数据和功能的集合。通过new操作符后跟对象类型的名称来创建(同Java)。例:let o = new Object();
- 如果构造函数没有参数,可以省略括号,合法但不推荐。
- 每个Object实例都有以下属性和方法:
- constructor:用于创建当前对象的函数。
- hasOwnProperty(propertyName):用于判断当前对象实例上是否存在给定的属性。
- isPrototypeOf(Object):用于判断当前对象是否为另一个对象的原型。
- propertyIsEnumerable(propertyName):用于判断给定的属性是否可以使用for-in语句枚举。
- toLocaleString():返回对象的字符串表示,该字符串反映对象所在的本地化执行环境。
- toString():返回对象的字符串表示。
- valueOf():返回对象对应的字符串、数值或布尔值表示。
- Object是所有对象的基类,所以任何对象都有这些属性和方法。
5.操作符
- 一元操作符
- 递增/递减操作符
- 同C语言
- 可以作用于任何值,整数,字符串,布尔值,浮点值甚至对象都可以。
- 一元加和减
- 同高中数学
- 应用到非数值时会执行于使用Number()转型函数一样的类型转换。
- 一元加(+)放到变量前头,对数值没有任何影响。
- 一元减(-)放在变量前头,主要用于把数值变成负值。
- 主要用于基本算数,也可用于数据类型转换。
- 递增/递减操作符
- 位操作符
- 在应用位操作时,在后台64位数值会转换为32位数值,然后执行位操作,最后再把结果从32位转换为64位存储起来。副作用是:特殊值NaN和Infinity在位操作中都会被当成0处理。
- 应用到非数值,首先会使用Number()函数将该值转换为数值。
- 按位非(~):返回数值的补数。
- 按位与(&)
- 按位或(|)
- 按位异或(^)
- 左移(<<):保留符号。
- 有符号右移(>>):保留符号
- 无符号右移(>>>)
- 布尔操作符
- 逻辑非(!)
- 返回值一定是布尔值。
- 首先将操作符转换为布尔值,然后对其取反。
- 同时使用两个叹号(!!),相当于调用了转型函数Boolean()。
- 规则:
如果操作数是对象,则返回false。
如果操作数是空字符串,则返回true。
如果操作数是非空字符串,则返回false。
如果操作数是数值0,则返回true。
如果操作数是非0 数值(包括Infinity),则返回false。
如果操作数是null,则返回true。
如果操作数是NaN,则返回true。
如果操作数是undefined,则返回true。
- 逻辑与(&&)
- 如果有操作数不是布尔值,返回值不一定是布尔值。
- 规则:
如果第一个操作数是对象,则返回第二个操作数。
如果第二个操作数是对象,则只有第一个操作数求值为true才会返回该对象。
如果两个操作数都是对象,则返回第二个操作数。
如果有一个操作数是null,则返回null。
如果有一个操作数是NaN,则返回NaN。
如果有一个操作数是undefined,则返回NaN。
- 逻辑或(||)
- 如果有操作数不是布尔值,返回值不一定是布尔值。
- 规则
如果第一个操作数是对象,则返回第一个操作数。
如果第一个操作数求值为false,则返回第二个操作数。
如果两个操作数都是对象,则返回第一个操作数。
如果两个操作数都是null,则返回null。
如果两个操作数都是NaN,则返回NaN。
如果两个操作数都是undefined,则返回undefined。
- 逻辑非(!)
- 乘性操作符:乘法、除法和取模。作用同Java,C语言。当操作数不是数值时,会在后台使用Number()转型函数转换为数值。
- 乘法(*)
- 操作符都是数值,执行常规乘法运算。如果不能表示乘积,则返回Infinity或-Infinity。
- 如果有任一操作数是NaN,则返回NaN。
- 如果是Infinity乘以0,则返回NaN。
- 如果是Infinity乘以非0的有限数值,则根据第二个操作数的符号返回Infinity或-Infinity。
- 如果是Infinity乘以Infinity,则返回Infinity。
- 如果不是数值的操作数,则先在后台用Number()将其转换为数值,然后再应用上述规则。
- 除法(/)
- 操作符都是数值,执行常规除法运算。如果不能表示商,则返回Infinity或-Infinity。
- 如果有任一操作数是NaN,则返回NaN。
- 如果是Infinity除以Infinity,则返回NaN。
- 如果是0除以0,则返回NaN。
- 如果是非0的有限值除以0,则根据第一个操作数的符号返回Infinity或-Infinity。
- 如果是Infinity除以任何值,则根据第二个操作数的符号返回Infinity或-Infinity。
- 如果不是数值的操作数,则先在后台用Number()将其转换为数值,然后再应用上述规则。
- 取模(%)
- 如果操作数是数值,则执行常规除法运算,返回余数。
- 如果被除数是无限值,除数是有限值,则返回NaN。
- 如果被除数是有限值,除数是0,则返回NaN。
- 如果是Infinity除以Infinity,则返回NaN。
- 如果被除数是有限值,除数是无限值,则返回被除数。
- 如果被除数是0,除数不是0,则返回0。
- 如果不是数值的操作数,则先在后台用Number()将其转换为数值,然后再应用上述规则。
- 乘法(*)
- 指数操作符:**
- 等价于Math.pow()
- 指数操作符也有自己的指数赋值操作符**=
- 加性操作符
- 加法操作符(+)
- 减法操作符(-)
- 关系操作符:都返回布尔值
- 小于(<)
- 大于(>)
- 小于等于(<=)
- 大于等于(>=)
- 相等操作符
- 等于(==)和不等于(!=):先进行类型转换,再确定操作数是否相等。
- 全等(===)和不全等(!==):比较相等时不转换操作数。
- 条件操作符:同Java中的三目运算符
variable = boolean_expression ? true_value : false_value;
- 赋值操作符
- 简单赋值(=)
- 复合赋值:使用乘性、加性或位操作符后跟等于号(=)表示
- 逗号操作符(,):可以用来在一条语句中执行多个操作
6.语句
- if语句:
if(condition) statement1 else statement2;
if(condition1) statement1 else if(condition2) statement2 else statement3;
- do-while语句
- while语句
- for语句:建议使用let声明迭代器变量
- for-in语句
- 建议使用const声明变量
for(const property in expression) statement;
- ECMAScript中对象的属性是无序的。因此for-in语句不能保证返回对象属性的顺序。
- for-of语句
- 一种严格的迭代语句,用于遍历可迭代对象的元素
for(property of expression) statement;
- 建议使用const声明迭代变量
- 会按照可迭代对象的next()方法产生值的顺序迭代元素
- 标签语句
- 用于给语句加标签,语法:label: statement
- 标签可以通过break或continue语句引用。(不太明白)
- break和continue语句
- break:立即退出循环,强制执行循环后的下一条语句。
- continue:立即退出循环,再次从循环顶部开始执行。
- 可以与标签语句一起使用,返回代码中特定的位置。(明白了)
- with语句
- 用途是将作用域设置为特定的对象,语法:
with(expression) statement;
- 主要场景是针对一个对象反复操作
- 严格模式不允许使用with语句,会抛出错误。
- 影响性能难调试,不推荐使用!
- 用途是将作用域设置为特定的对象,语法:
- switch语句(同Java)
7.函数
- 使用function关键字声明,后跟一组参数,然后是函数体。
function functionName(arg0,arg1,…,argN){
statements
}
- 使用函数名调用函数
- 不需要指定是否返回值,任何函数在任何时间都可以使用return语句来返回函数的值
- 不指定返回值的函数实际上会返回特殊值undefined