1.什么是javaScript
-
JavaScript
历史回顾 -
JavaScript
是什么(实现)
JavaScript
是一门用来与网页交互的脚本语言,包含以下三个组成部分。
ECMAScript
:由 ECMA-262 定义并提供核心功能。
文档对象模型(DOM):提供与网页内容交互的方法和接口。
浏览器对象模型(BOM):提供与浏览器交互的方法和接口。
JavaScript
与ECMAScript
的关系
ECMAScript
是JavaScript
的规格,JavaScript
实现了ECMAScript
JavaScript
的不同版本
2.HTML中的JavaScript
JavaScript
是通过<script>
元素插入到 HTML 页面中的。这个元素可用于把JavaScript
代码嵌入到
HTML
页面中,跟其他标记混合在一起,也可用于引入保存在外部文件中的 JavaScript
。本章的重点可以总结如下。
-
要包含外部
JavaScript
文件,必须将 src 属性设置为要包含文件的 URL。文件可以跟网页在同一台服务器上,也可以位于完全不同的域。 -
所有
<script>
元素会依照它们在网页中出现的次序被解释。在不使用defer
和async
属性的情况下,包含在<script>
元素中的代码必须严格按次序解释。 -
对不推迟执行的脚本,浏览器必须解释完位于
<script>
元素中的代码,然后才能继续渲染页面的剩余部分。为此,通常应该把<script>
元素放到页面末尾,介于主内容之后及</body>
标签之前。 -
可以使用
defer
属性把脚本推迟到文档渲染完毕后再执行。推迟的脚本原则上按照它们被列出的次序执行。 -
可以使用
async
属性表示脚本不需要等待其他脚本,同时也不阻塞文档渲染,即异步加载。异步脚本不能保证按照它们在页面中出现的次序执行。 -
通过使用
<noscript>
元素,可以指定在浏览器不支持脚本时显示的内容。如果浏览器支持并启用脚本,则<noscript>
元素中的任何内容都不会被渲染。
3.语言基础
3.1 变量
let 跟 var 最明显的区别是,let 声明的范围是块作用域,而 var 声明的范围是函数作用域。
- var函数作用域:
if (true) {
var name = 'Matt';
console.log(name); // Matt
}
console.log(name); // Matt
- let块作用域:
if (true) {
let age = 26;
console.log(age); // 26
}
console.log(age); // ReferenceError: age 没有定义
const
的行为与 let
基本相同,唯一一个重要的区别是用它声明变量时必须同时初始化变量,且尝试修改 const
声明的变量会导致运行时错误。
3.2 数据类型
ECMAScript
有 6 种简单数据类型(也称为原始类型):Undefined
、Null
、Boolean
、Number
、String
和 Symbol
。Symbol
(符号)是ECMAScript 6
新增的。
还有一种复杂数据类型叫 Object
(对象)。Object
是一种无序名值对的集合。
-
typeof操作符
因为
ECMAScript
的类型系统是松散的,所以需要一种手段来确定任意变量的数据类型。typeof
操作符就是为此而生的。对一个值使用typeof
操作符会返回下列字符串之一:
"undefined"
表示值未定义;
"boolean"
表示值为布尔值;
"string"
表示值为字符串;
"number"
表示值为数值;
"object"
表示值为对象(而不是函数)或 null;"function"
表示值为函数;
"symbol"
表示值为符号。下面是使用 typeof 操作符的例子:
let message = "some string"; console.log(typeof message); // "string" console.log(typeof(message)); // "string" console.log(typeof 95); // "number"
在这个例子中,我们把一个变量(message)和一个数值字面量传给了 typeof 操作符。注意,因为 typeof 是一个操作符而不是函数,所以不需要参数(但可以使用参数)。
注意typeof在某些情况下返回的结果可能会让人费解,但技术上讲还是正确的。比如,调用typeof null 返回的是"object"。这是因为特殊值 null 被认为是一个对空对象的引用。在其他地方看到前端判断数据类型的方法:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-99ATwSTN-1610439473146)(blob:file:///8740607f-d149-4616-8e5e-292f4bc32df4)]
-
Undefined类型
⚠️:即使未初始化的变量会被自动赋予 undefined 值,但我们仍然建议在声明变量的同时进行初始化。这样,当 typeof 返回"undefined"时,你就会知道那是因为给定的变量尚未声明,而不是声明了但未初始化。
-
Null类型
Null
类型同样只有一个值,即特殊值null
。逻辑上讲,null
值表示一个空对象指针,这也是给
typeof
传一个null
会返回"object"
的原因:let car = null; console.log(typeof car); // "object"
-
Boolean类型
Boolean()转型函数可以在任意类型的数据上调用,而且始终返回一个布尔值。什么值能转换为 true
或 false 的规则取决于数据类型和实际的值。下表总结了不同类型与布尔值之间的转换规则。数据类型 转换为 true 的值 转换为 false 的值 Boolean true false String 非空字符串 “”(空字符串) Number 非零数值(包括无穷值) 0、NaN(参见后面的相关内容) Object 任意对象 null Undefined N/A(不存在) undefined -
Number 类型
-
浮点值
要定义浮点值,数值中必须包含小数点,而且小数点后面必须至少有一个数字。永远不要测试某个特定的浮点值
if (a + b == 0.3) { // 别这么干! console.log("You got 0.3."); }
-
值的范围
由于内存的限制,ECMAScript 并不支持表示这个世界上的所有数值。ECMAScript 可以表示的最小数值保存在 Number.MIN_VALUE 中,这个值在多数浏览器中是 5e324;可以表示的最大数值保存在Number.MAX_VALUE 中,这个值在多数浏览器中是 1.797 693 134 862 315 7e+308。如果某个计算得到的数值结果超出了 JavaScript 可以表示的范围,那么这个数值会被自动转换为一个特殊的 Infinity(无穷)值。
-
NaN
NaN 不等于包括 NaN 在内的任何值。例如,下面的比较操作会返回 false:
console.log(NaN == NaN); // false
-
数值转换
有 3 个函数可以将非数值转换为数值:
Number()
、parseInt()
和parseFloat()
Number()是转型函数,可用于任何数据类型。后两个函数主要用于将字符串转换为数值。
-
-
String 类型
String(字符串)数据类型表示零或多个 16 位 Unicode 字符序列。字符串可以使用双引号(")、
单引号(’)或反引号(`)标示,因此下面的代码都是合法的:
let firstName = "John"; let lastName = 'Jacob'; let lastName = `Jingleheimerschmidt`
- 转换为字符串
-
toString()方法
⚠️:toString()方法可见于数值、布尔值、对象和字符串值。(没错,字符串值也有 toString()方法,该方法只是简单地返回自身的一个副本。)null 和 undefined 值没有 toString()方法。
let age = 11; let ageAsString = age.toString(); // 字符串"11" let found = true; let foundAsString = found.toString(); // 字符串"true"
-
String()转型函数
⚠️:如果你不确定一个值是不是 null 或 undefined,可以使用 String()转型函数,它始终会返回表示相应类型值的字符串。
let value1 = 10; let value2 = true; let value3 = null; let value4; console.log(String(value1)); // "10" console.log(String(value2)); // "true" console.log(String(value3)); // "null" console.log(String(value4)); // "undefined"
用加号操作符给一个值加上一个空字符串""也可以将其转换为字符串
-
模板字面量
ECMAScript 6 新增了使用模板字面量定义字符串的能力。与使用单引号或双引号不同,模板字面量保留换行字符,可以跨行定义字符串:
let myMultiLineString = 'first line\nsecond line'; let myMultiLineTemplateLiteral = `first line second line`; console.log(myMultiLineString); // first line // second line" console.log(myMultiLineTemplateLiteral); // first line // second line console.log(myMultiLineString === myMultiLinetemplateLiteral); // true
-
字符串插值
字符串插值通过在${}中使用一个 JavaScript 表达式实现:
let value = 5; let exponent = 'second'; // 以前,字符串插值是这样实现的: let interpolatedString = value + ' to the ' + exponent + ' power is ' + (value * value); // 现在,可以用模板字面量这样实现: let interpolatedTemplateLiteral = `${ value } to the ${ exponent } power is ${ value * value }`; console.log(interpolatedString); // 5 to the second power is 25 console.log(interpolatedTemplateLiteral); // 5 to the second power is 25
-
Symbol 类型
Symbol(符号)是 ECMAScript 6 新增的数据类型。符号是原始值,且符号实例是唯一、不可变的。符号的用途是确保对象属性使用唯一标识符,不会发生属性冲突的危险。
-
Object 类型
ECMAScript 中的对象其实就是一组数据和功能的集合。对象通过 new 操作符后跟对象类型的名称
来创建。开发者可以通过创建 Object 类型的实例来创建自己的对象,然后再给对象添加属性和方法:
let o = new Object();
3.3操作符
-
一元操作符
只操作一个值的操作符叫一元操作符(unary operator)
(1)递增/递减操作符(a++或–a)
(2)一元加和减(a=+a或a=-a)
(3)位操作符(按位非(~), 按位与(&),按位或(|), 按位异或(^),左移(<<),右移(>>),无符号右移(>>>))
-
布尔操作符
布尔操作符一共有 3 个:逻辑非(!)、逻辑与(&&)和逻辑或(||)。
-
乘性操作符
ECMAScript 定义了 3 个乘性操作符:乘法(*)、除法(/)和取模(%)。
-
指数操作符
ECMAScript 7 新增了指数操作符,Math.pow()现在有了自己的操作符**,结果是一样的:
console.log(Math.pow(3, 2); // 9 console.log(3 ** 2); // 9
不仅如此,指数操作符也有自己的指数赋值操作符**=,该操作符执行指数运算和结果的赋值操作:
let squared = 3; squared **= 2; console.log(squared); // 9
-
加性操作符
加性操作符,即加法(+)和减法(-)操作符,一般都是编程语言中最简单的操作符。加性操作符在后台会发生不同数据类型的转换。
-
关系操作符
关系操作符执行比较两个值的操作,包括小于(<)、大于(>)、小于等于(<=)和大于等于(>=),
用法跟数学课上学的一样。
-
相等操作符
ECMAScript提供了两组操作符。第一组是等于和不等于,它们在比较之前执行转换。第二组是全等和不全等,它们在比较之前不执行转换。
(1)等于和不等于
ECMAScript 中的等于操作符用两个等于号(==)表示,如果操作数相等,则会返回 true。不等于操作符用叹号和等于号(!=)表示,如果两个操作数不相等,则会返回 true。这两个操作符都会先进行类型转换(通常称为强制类型转换)再确定操作数是否相等。
(2)全等和不全等
全等和不全等操作符与相等和不相等操作符类似,只不过它们在比较相等时不转换操作数。全等操
作符由 3 个等于号(===)表示,只有两个操作数在不转换的前提下相等才返回 true,比如:
let result1 = ("55" == 55); // true,转换后相等 let result2 = ("55" === 55); // false,不相等,因为数据类型不同
-
条件操作符
条件操作符是 ECMAScript 中用途最为广泛的操作符之一,语法跟 Java 中一样:
variable = boolean_expression ? true_value : false_value;
上面的代码执行了条件赋值操作,即根据条件表达式 boolean_expression 的值决定将哪个值赋
给变量 variable 。如果 boolean_expression 是 true ,则赋值 true_value ;如果
boolean_expression 是 false,则赋值 false_value。
-
赋值操作符
简单赋值用等于号(=)表示,将右手边的值赋给左手边的变量,如下所示:
let num = 10;
复合赋值使用乘性、加性或位操作符后跟等于号(=)表示。这些赋值操作符是类似如下常见赋值
操作的简写形式:
let num = 10; num += 10;
-
逗号操作符
逗号操作符可以用来在一条语句中执行多个操作,如下所示:
let num1 = 1, num2 = 2, num3 = 3;
在一条语句中同时声明多个变量是逗号操作符最常用的场景。不过,也可以使用逗号操作符来辅助
赋值。在赋值时使用逗号操作符分隔值,最终会返回表达式中最后一个值:
let num = (5, 1, 4, 8, 0); // num 的值为 0
在这个例子中,num 将被赋值为 0,因为 0 是表达式中最后一项。逗号操作符的这种使用场景并不
多见,但这种行为的确存在。
3.4语句
-
if 语句
if 语句是使用最频繁的语句之一,语法如下:
if (condition) statement1 else statement2
-
do-while 语句
do-while 语句是一种后测试循环语句,即循环体中的代码执行后才会对退出条件进行求值。换句话说,循环体内的代码至少执行一次。do-while 的语法如下:
do { statement } while (expression);
-
while 语句
while 语句是一种先测试循环语句,即先检测退出条件,再执行循环体内的代码。因此,while 循
环体内的代码有可能不会执行。下面是 while 循环的语法:
while(expression) statement
这是一个例子:
let i = 0; while (i < 10) { i += 2; }
-
for 语句
for 语句也是先测试语句,只不过增加了进入循环之前的初始化代码,以及循环执行后要执行的表达式,语法如下:
for (initialization; expression; post-loop-expression) statement
下面是一个用例:
let count = 10; for (let i = 0; i < count; i++) { console.log(i); }
-
for-in 语句
for-in 语句是一种严格的迭代语句,用于枚举对象中的非符号键属性,语法如下:
for (property in expression) statement
下面是一个例子:
for (const propName in window) { document.write(propName); }
-
for-of 语句
for-of 语句是一种严格的迭代语句,用于遍历可迭代对象的元素,语法如下:
for (property of expression) statement
下面是示例:
for (const el of [2,4,6,8]) { document.write(el); }
-
标签语句
标签语句用于给语句加标签,语法如下:
label: statement
下面是一个例子:
start: for (let i = 0; i < count; i++) { console.log(i); }
在这个例子中,start 是一个标签,可以在后面通过 break 或 continue 语句引用。标签语句的
典型应用场景是嵌套循环。
-
break 和 continue 语句
break 和 continue 语句为执行循环代码提供了更严格的控制手段。其中,break 语句用于立即退
出循环,强制执行循环后的下一条语句。而 continue 语句也用于立即退出循环,但会再次从循环顶部
开始执行。
-
with 语句
with 语句的用途是将代码作用域设置为特定的对象,其语法是:
with (expression) statement;
⚠️:由于 with 语句影响性能且难于调试其中的代码,通常不推荐在产品代码中使用 with
语句。
-
switch 语句
switch 语句是与 if 语句紧密相关的一种流控制语句,从其他语言借鉴而来。
3.5 函数
函数对任何语言来说都是核心组件,因为它们可以封装语句,然后在任何地方、任何时间执行。
ECMAScript 中的函数使用 function 关键字声明,后跟一组参数,然后是函数体。
以下是函数的基本语法:
function functionName(arg0, arg1,...,argN) {
statements
}
下面是一个例子:
function sayHi(name, message) {
console.log("Hello " + name + ", " + message);
}
4 变量、作用域与内存
JavaScript 变量是松散类型的,而且变量不过就是特定时间点一个特定值的名称而已。
4.1 原始值与引用值
ECMAScript 变量可以包含两种不同类型的数据:原始值和引用值。原始值(primitive value)就是
最简单的数据,引用值(reference value)则是由多个值构成的对象。
保存引用值的变量是按引用(by reference)访问的
-
动态属性
原始值和引用值的定义方式很类似,都是创建一个变量,然后给它赋一个值。不过,在变量保存了
这个值之后,可以对这个值做什么,则大有不同。对于引用值而言,可以随时添加、修改和删除其属性
和方法。
let person = new Object(); person.name = "Nicholas"; console.log(person.name); // "Nicholas"
原始值不能有属性,尽管尝试给原始值添加属性不会报错。比如:
let name = "Nicholas"; name.age = 27; console.log(name.age); // undefined
-
复制值
除了存储方式不同,原始值和引用值在通过变量复制时也有所不同。在通过变量把一个原始值赋值
到另一个变量时,原始值会被复制到新变量的位置。
在把引用值从一个变量赋给另一个变量时,存储在变量中的值也会被复制到新变量所在的位置。区别在于,这里复制的值实际上是一个指针,它指向存储在堆内存中的对象。操作完成后,两个变量实际
上指向同一个对象,因此一个对象上面的变化会在另一个对象上反映出来
-
传递参数
ECMAScript 中所有函数的参数都是按值传递的。这意味着函数外的值会被复制到函数内部的参数
中,就像从一个变量复制到另一个变量一样。如果是原始值,那么就跟原始值变量的复制一样,如果是
引用值,那么就跟引用值变量的复制一样。对很多开发者来说,这一块可能会不好理解,毕竟变量有按
值和按引用访问,
而传参则只有按值传递
。 很多开发者错误地认为,当在局部作用域中修改对象而变化反映到全局时,就意味着参数是按引用传递的。
-
确定类型
typeof 操作符最适合用来判断一个变量是否为原始类型。更确切地说,它是判断一个变量是否为字符串、数值、布尔值或 undefined 的最好方式。如果值是对象或 null,那么 typeof返回"object"。
typeof 虽然对原始值很有用,但它对引用值的用处不大。我们通常不关心一个值是不是对象,而是想知道它是什么类型的对象。为了解决这个问题,ECMAScript 提供了 instanceof 操作符,语法如下:
result = variable instanceof constructor
例子:
console.log(person instanceof Object); // 变量 person 是 Object 吗? console.log(colors instanceof Array); // 变量 colors 是 Array 吗? console.log(pattern instanceof RegExp); // 变量 pattern 是 RegExp 吗?