script 标签
服务器在传送 JavaScript 文件时使用的 MIME 类型通常是 application/x–javascript
传统的做法,所有 script
元素都应该放在页面的 head
元素中
<head>
<script type="text/javascript" src="example1.js"></script>
<script type="text/javascript" src="example2.js"></script>
</head>
浏览器在遇到body
标签时才开始呈现内容
意味着必须等到全部 JavaScript 代码都被下载解析和执行完成以后,才能开始呈现页面的内容
现代 Web 应用程序一般都把全部 JavaScript
引用放在body
元素中页面内容的后面
<body>
<!-- 这里放内容 -->
<script src="example1.js"></script>
<script src="example2.js"></script>
</body>
defer & async
这两个属性只适用于外部脚本文件,HTML5
的实现会忽略给嵌入脚本设置的这两个属性
浏览器立即下载文件
-
延迟脚本 defer
HTML 4.01 为<script>
标签定义了defer
属性
defer
告诉浏览器立即下载文件,等待下载,但延迟执行。包含的脚本将延迟到浏览器遇到</html>
标签后再执行
这脚本会先于DOMContentLoaded
事件<head> <title>Example HTML Page</title> <script defer="defer" src="example1.js"></script> <script defer="defer" src="example2.js"></script> </head> <body> <!-- 这里放内容 --> </body>
-
异步脚本 async
async告诉浏览器立即下载文件但不等待下载,且延迟执行
async 的脚本并不保证按照标签的先后顺序执行
指定 async 属性的目的是不让页面等待两个脚本下载,从而异步加载页面其他内容
异步脚本一定会在页面的 load 事件前执行,但可能会在 DOMContentLoaded 事件触发之前或之后执行
为此,建议异步脚本不要在加载期间修改 DOM
这种注释 JavaScript 代码的格式得到了所有浏览器的认可,也能被正确解释,但由于所有浏览器都已经支持 JavaScript,因此也就没有必要再使用这种格式了
<script><!--
function sayHi(){
alert("Hi!");
}
//--></script>
嵌入代码与外部文件
尽可能使用外部文件来包含 JavaScript 代码,即 结构 - 行为 - 样式分离
- 可维护性
遍及不同 HTML 页面的 JavaScript 会造成维护问题。但把所有 JavaScript 文件都放在一个文件夹中,维护起来就轻松多了。
而且开发人员因此也能够在不触及 HTML 标记的情况下,集中精力编辑 JavaScript 代码 - 可缓存
浏览器能够根据具体的设置缓存链接的所有外部 JavaScript 文件。
也就是说,如果有两个页面都使用同一个文件,那么这个文件只需下载一次。因此能够加快页面加载的速度
<noscript> 元素
早期浏览器都面临一个特殊的问题,即当浏览器不支持JavaScript 时如何让页面平稳地退化。对这个问题的最终解决方案就是创造一个<noscript>
元素,用以在不支持 JavaScript
的浏览器中显示替代的内容。
这个元素可以包含能够出现在文档<body>
中的任何HTML 元素——<script>
元素除外。包含在<noscript>
元素中的内容只有在浏览器不支持 js 或被禁用情况下呈现
<body>
<noscript>
<p>本页面需要浏览器支持(启用)JavaScript</P>
</noscript>
</body>
小结
- 在包含外部 JavaScript 文件时,必须将 src 属性设置为指向相应文件的 URL。而这个文件既可以是与包含它的页面位于同一个服务器上的文件,也可以是其他任何域中的文件
- 所有元素都会按照它们在页面中出现的先后顺序依次被解析。在所有 script 中,不使用 defer 和 async 属性的 script 会被按序加载和解析,只有在解析完
<html>
元素中的所有代码之后,才会开始解析带有 defer 和 async 属性的 script 代码 - 由于浏览器按需加载解析的机制,一般应该把 script 元素放在页面最后,即主要内容后面
</body>
标签前 - noscript 标签用于浏览器禁用 js 时呈现的内容
语法
-
区分大小写
ECMAScript 中的一切(变量、函数名和操作符)都区分大小写 -
标识符
所谓标识符,就是指变量、函数、属性的名字,或者函数的参数
标识符以字母、下划线、美元符号开头,其他字符可以是字母、下划线、美元符号或数字
不能把关键字、保留字、true、false 和 null 用作标识符
按照惯例,ECMAScript 标识符采用驼峰大小写格式 -
注释
-
严格模式
“use strict”
顶部添加,也可以指定函数在严格模式
这行代码看起来像是字符串,而且也没有赋值给任何变量,但其实它是一个编译指示(pragma)用于告诉支持的 JavaScript 引擎切换到严格模式 -
语句
ECMAScript 中的语句以一个分号结尾;如果省略分号,则由解析器确定语句的结尾。
虽然语句结尾的分号不是必需的,但我们建议任何时候都不要省略它。解析器不必再花时间推测应该在哪里插入分号,增进代码的性能
可以使用 C 风格的语法把多条语句组合到一个代码块中{ var a = 1; console.log(a); }
-
变量
ECMAScript 的变量是松散类型的,所谓松散类型就是可以用来保存任何类型的数据
换句话说,每个变量仅仅是一个用于保存值的占位符而已
由于 ECMAScript 是松散类型的,因而使用不同类型初始
化变量的操作可以放在一条语句中来完成var message = "hi", found = false, age = 29; message = 100; // 有效,但不推荐
在函数中使用 var 定义一个变量,那么这个变量在函数退出后就会被销毁。函数被调用时,就会创建该变量并为其赋值,而在此之后,这个变量又会立即被销毁
-
数据类型
ECMAScript 中有 6 种简单数据类型和1种复杂数据类型
Undefined、Null、Boolean、Number、String、Symbol -
typeof
操作符
typeof 是一种来检测给定变量数据类型的手段,是一个操作符而不是函数。
因此圆括号可以使用,但不必需。(可以使用的原因是 js 中()可以代表分组 )typeof(message); typeof (message); typeof message;
返回值可以是以下几种
undefined
/boolean
/string
/number
/symbol
object
/function
从技术角度讲,函数在 ECMAScript 中是对象,不是一种数据类型。然而,函数也确实有一些特殊的属性,因此通过 typeof 操作符来区分函数和其他对象是有必要的
-
Undefined类型
声明变量但未对其加以初始化时这个变量的值就是 undefined
但是未声明的变量 typeof 返回值也是 undefined。但是这两个 undefined 有一定区别var a; typeof a // undefined typeof b // undefined alert(a) // undefined alert(b) // 报错 alert(typeof b) // 弹出 undefined
对于尚未声明过的变量,只能执行一项操作,即使用 typeof 操作符检测其数据类型(对未经声明的变量调用 delete 不会导致错误,但这样做没什么实际意义,而且在严格模式下确实会导致错误)
-
Null类型
Null 类型是第二个只有一个值的数据类型,这个特殊的值是 null
从逻辑角度来看,null 值表示一个空对象指针
而这也正是使用 typeof null 返 “object” 的原因实际上,undefined 值是派生自 null 值的,因此 ECMA-262 规定对它们值的相等性测试要返回 true
undefined == null // true 值相等 隐式转换为两边为布尔 undefined ==+ null // false 类型不同 Boolean(NaN) // false undefined == NaN // false NaN == null // false false == undefined //false '' == undefined // false 所以说为什么 undefined == null 因为 tm undefined 值是派生自 null 值的
-
Boolean类型
虽然 Boolean 类型的字面值只有两个,但 ECMAScript 中所有类型的值都有与这两个 Boolean 等价的值Boolean true false String 任何非空 '' Number 非 0 0/NaN Object 任何对象 null Undefined undefined
-
Number类型
-
整数字面量
除了以十进制表示外,整数还可以通过八进制(以 8 为基数)或十六进制(以 16 为基数)的字面值
八进制字面值的第一位必须是 0 后跟八进制数字序列(0~7)
十六进制字面值的前两位必须是 0x 后跟任何十六进制数字(0~9 及 A~F)八进制字面量在严格模式下是无效的,会导致支持的 JavaScript 引擎抛出错误
在进行算术计算时,所有以八进制和十六进制表示的数值最终都将被转换成十进制数值。var octalNum1 = 070; // 八进制的 56 var octalNum2 = 079; // 无效的八进制数值——解析为 79 var hexNum1 = 0xA; // 十六进制的 10 var hexNum2 = 0x1f; // 十六进制的 31
-
浮点数值
所谓浮点数值,就是该数值中必须包含一个小数点并且小数点后面必须至少有一位数字
由于浮点数值需要的内存空间是保存整数值的两倍,因此 ECMAScript 会不失时机地将浮点数值转换为整数值var floatNum1 = 1.; // 小数点后面没有数字——解析为 1 var floatNum2 = 10.0; // 整数——解析为 10
-
极大或极小数值
浮点数值的最高精度是 17 位小数,但在进行算术计算时其精确度远远不如整数
0.1 加 0.2 的结果不是 0.3,而是 0.300000000000000040.1 + 0.2 // 0.30000000000000004 0.15 + 0.15 // 0.3 0.25 + 0.05 // 0.3 console.log(0.10000000000000001) // 0.1
关于浮点数值计算会产生舍入误差的问题,有一点需要明确:这是使用基于 IEEE754 数值的浮点计算的通病,ECMAScript 并非独此一家;其他使用相同数值格式的语言也存在这个问题
关于此问题的博客 -
NaN
NaN 是一个特殊的数值,任何涉及 NaN 的操作都会返回 NaN。其次,NaN 与任何值都不相等,包括 NaN 本身。NaN == NaN // false
isNaN() 用于检测 是否非数。该函数会尝试将这个值转换为数值
isNaN(NaN) // true isNaN('blue') // true isNaN(false) // false
-
类型转换
有 3 个函数可以把非数值转换为数值:Number()、parseInt() 和 parseFloat()
Number()可以用于任何数据类型,而另两个函数则专门用于把字符串转换成数值。这 3 个函数对于同样的输入会有返回不同的结果- Number()函数的转换规则如下
-
Boolean 值,true 和 false 将分别被转换为 1 和 0
-
null 值返回 0
-
undefined 返回 NaN
-
数字值,8/16 进制返回 10 进制数,10 进制等值返回
-
字符串 遵循下列规则
- 如果字符串中只包含数字(整数/浮点/8/16进制 返回 10 进制
- ‘’ 返回 0
- 如果字符串中包含除上述格式之外的字符,则将其转换为 NaN
-
如果是对象,则调用对象的 valueOf()方法,然后依照前面的规则转换返回的值。如果转换的结果是 NaN,则调用对象的 toString()方法,然后再次依照前面的规则转换返回的字符串值。
Number('1b') // NaN Number('0xA‘) // 10
-
- parseInt/parseInt
由于 Number()函数在转换字符串时比较复杂而且不够合理,因此在处理整数的时候更常用的是 parseInt() 函数。
parseInt() 函数在转换字符串时,更多的是看其是否符合数值模式,它会找到第一个非空格字符。如果第一个字符不是数字字符或者负号会返回 NaN
paresInt 接受第二个参数为进制基数
parseFloat()只解析十进制值,因此它没有用第二个参数parseInt('') // NaN Number('') // 0
- Number()函数的转换规则如下
-
-
String类型
- 转义字符串整体长度算 1
'\u03a3'.length // 1
- 字符串不可变
字符串一旦创建,它们的值就不能改变。要改变某个变量保存的字符串,首先要销毁原来的字符串,然后再用另一个包含新值的字符串填充该变量var lang = "Java"; lang = lang + "Script"; 首先创建一个能容纳 10 个字符的新字符串 然后在这个字符串中填充"Java"和"Script" 销毁字符串"Java"和字符串"Script"
- 转义字符串整体长度算 1
操作符
-
算术操作符
+-*/ ++ -- // 自加自减 += -= *= /= ** // 次方 % // 取模
-
位操作符
-
逻辑操作符
&& || ! 返回表达式出结果的值
-
关系操作符
< > <= >= 在 if 判断中 if( num >= 2 && num <= 5){} // 可以 if( 2<= num <= 5){} // 不可以 eg var num = 3; if(2<= num <= 5){alert()} // true if(5<= num <= 2){alert()} // true 这里是或的关系 在比较字符串的时候,按位比较字符的 unicode 编码值 '23' < '3' // true 如有有一个 number 则转化成 number 比较 '23' < 3 // false NaN < 1 // false NaN > 1 // false 'Brick' < 'alphabet' // true 大写字母编码小于小写 如果要真正按字母表顺序比较字符串 "Brick".toLowerCase() < "alphabet".toLowerCase() // false
-
相等操作符
- 相等和不相等——先转换再比较
- 全等和不全等——仅比较而不转换
-
逗号操作符
逗号操作符还可以用于赋值总会返回表达式中的最后一项var a,b,c; var num = (5, 1, 4, 8, 0); // num 的值为 0
语句
-
流程控制语句
- if
虽然 if 只有一行语句可以不写{},但是正规的做法总是加上{} if(){}else{} 也可以都写在一行省略{} if() xxx else if() xx else xx
- switch case
switch 语句中使用任何数据类型,每个 case 的值不一定是常量,可以是变量,甚至是表达式
我个人总结下来就是 switch 中的和 case 两者布尔值的比较switch ("hello world") { case "hello" + " world": alert("Greeting was found."); break; case "goodbye": alert("Closing was found."); break; default: alert("Unexpected message was found."); } var num = 25; switch (true) { case num < 0: alert("Less than 0."); break; case num >= 0 && num <= 10: alert("Between 0 and 10."); break; case num > 10 && num <= 20: alert("Between 10 and 20."); break; default: alert("More than 20."); }
- if
-
循环语句
-
for 语句
-
for-in 语句
-
do while
do-while 语句是一种后测试循环语句,至少执行一次do { 语句 } while (条件)
-
while
while 语句属于前测试循环语句。如果条件 + 循环,可以考虑用这个while(条件){ 语句 }
- with 语句
with 语句会导致性能下降,不建议使用 with 语句,这里只做介绍
with(location){ var qs = search.substring(1); var hostName = hostname; var url = href; } 类似解构赋值,但是解构赋值好像不能进一步对数据操作,只能结构出来值 var {} = location
- with 语句
-
数组
-
增删改查
-
栈方法 push/pop 先进后出
-
队列 shift/unshift 先进后出
这两个方法删除时返回被删除项,增加时候返回数组新的 lengthlet arr = []; arr.push(1,2,3) // 3 arr -> [1,2,3] arr.pop() // 3 被删除的项
-
indexOf/lastIndexOf 查询参数在数组中的索引值,如果数组中没有该项则返回 -1
let arr = [{name:1}, 1]; arr.indexOf({name:1}) // -1\\
-
数组最强大的方法 split
arr.split(startIndex, deleteCount, insertItems)
任意索引增加:arr.split(startIndex, 0, insertItems) 任意索引删除: arr.split(startIndex, deleteCount) 任意索引替换: arr.split(startIndex, 1, inserItems)
-
-
toString/toLocalString/join
alert 函数后台会对参数调用 toString 方法,所以 alert 一个对象时,会输出 [object object] 等字样
数组的 tostring() 方法会对每项都调用 toString(),返回由每项 toString 后的字符串,中间用逗号分隔
为了解决数组转字符串只能以逗号分隔的问题,join 方法支持自定义分隔符alert({name:1}) // [object object] alert( [1,2,3] ) // 1,2,3 alert( [1, {}] ) // 1,[object Object] [1,2,3].join('-') // "1-2-3"
-
遍历
- forEach()
- map
- every/some
- filter
-
重排
数组中内置了两种对重排方法,分别是 reverse 和 sort- reverse 方法的作用相当直观明了,但不够灵活。所以才有了 sort 方法
- sort
- 默认情况下按照 item 的 toString 后的值升序排列数组项,比较的是字符串的 unicode 大小
ps: 字符串比较是按照位置来,如果两项的第一位 unicode 大小一样,就比较第二项的 unicode,以此类推let arr= [1,2,15,25]; arr.sort() // [1,15,2,25]
- 默认排序方式在很多情况下是不理想的,因此 sort 接受一个比较函数,作为排序的依据。
比较函数接收两个参数,如果第一个参数应该位于第二个之前则返回一个负数,如果两个参数相等
则返回 0,如果第一个参数应该位于第二个之后则返回一个正数。
如果比较函数返回值不能转化成数字类型,则按照默认情况排序。正常情况下排序是对 item 是数组或者能转化成数值的字符进行排序,比如[ ‘a’, ‘b’ ]排序, ‘b’ - ‘b’ 值为 NaN,没有意义所以不做深入let arr = ['1', '2', '15', '25'] arr.sort(function compare(a, b) { if (a < b) { return 1; } else if (a > b) { return -1; } else { return 0; } }); 简化比较函数, b - a 的返回值可以处理以上三种情况(返回值: 正升负降0相等) arr.sort((a, b) => b - a)
- 默认情况下按照 item 的 toString 后的值升序排列数组项,比较的是字符串的 unicode 大小
-
操作方法
所有可以创建新数组的方法都可以浅拷贝数组 slice/concat/filter/map 。所以谈数组浅拷贝是有点扯-
concat
concat 方法基于当前数组中的所有项创建一个新数组,接受任意类型/任意个数的参数,如果参数为数组则会将两个数组合并,如果不是数组则会添加到新数组中
具体来说,这个方法会先创建当前数组一个副本,然后将接收到的参数添加到这个副本的末尾,最后返回新构建的数组var colors = ["red", "green", "blue"]; var colors2 = colors.concat("yellow", ["black", "brown"]); alert(colors); //red,green,blue alert(colors2); //red,green,blue,yellow,black,brown
-