To boldly go where no one has gone before.

【退役生活】JavaScript 学习笔记

2020-02-08 21:19:04


JavaScript 笔记

请注意,JavaScript 和 Java 没有任何关系。

基本语法

结构

JavaScript 与 C / C++ 极为相似,甚至可以认为 JavaScript 就是 C++ 的简化版。JavaScript 没有特定的程序结构,因为 JavaScript 一般是被用于 函数定义 来辅助 HTML 页面,而不会像 C++ 程序一样被编译执行。

JavaScript 的每句话以分号结尾。

插入 JavaScript 代码

在 HTML 中插入 JavaScript 代码时使用 <script> 标签。例如:

<script>
    alert("hello, world!");
</script>

JavaScript 还可以直接向 HTML 文档写入语句,例如:

document.write("<h1>标题</h1>");
document.write("<p>段落</p>");

如果在文档加载完毕后使用 document.write(),则会覆盖整个 HTML 文档。

在 HTML 文档中可以放入无限多个脚本。脚本可以位于 <head> 或者 <body> 部分,通常把 JavaScript 函数放入 <head> 部分或者页面底部。例如:

<!DOCTYPE html>
<html>
    <head>
        <meta charset='utf-8'>
        <title>
            test
        </title>
        <script>
            function myFunction() {
                document.getElementById("demo").innerHTML =
                 "Hello world!";
            }
        </script>
    </head>
    <body>
        <p id="demo">
            JavaScript
        </p>
        <button type="button" onclick="myFunction()">
            点击查看效果
        </button>
    </body>
</html>

在这个例子中,按钮被按下后就会执行 <head> 中定义的函数 myFunction(),把段落文字变为 Hello World!

除此之外,JavaScript 代码还可以置于外部,在 <script> 的属性中引入即可。例如:

function myFunction() {
    document.getElementById("demo").innerHTML = \
     "Hello world!";
}

把这段代码保存为 myScript.js,那么 HTML 文档中就可以写:

<script src="myScript.js"></script>

输出

JavaScript 没有特定的输出函数。如果希望 JavaScript 显示数据,则有以下几种方法可选:

函数名 描述
window.alert() 弹出警告窗
document.write() 写入 HTML 文档
.innerHTML 写入 HTML 元素
console.log() 写入浏览器控制台

注释

JavaScript 注释和 C++ 一模一样。

变量

JavaScript 的所有变量统一使用 var 声明。变量的类型可以 动态变化,也就是说可以实现:

var x = 1926;
x = "frog";

JavaScript 中可以 重复声明 同一个变量,其值不会丢失。如果声明一个变量后不赋初值,则由于变量类型没有定义,所以其值为 undefined

使用 typeof varName 可以返回变量的类型。而 === 符号(即绝对相等)可以判断两个变量的 值和类型 是否都一致。如果希望声明一个变量的类型,则可以用 new,例如:

var x = new Number;

x 就有了初值 0。但这样声明后,x 的类型就不再是 Number 而是 Object

如果把值赋给尚未声明的变量,该变量将自动作为 window 的一个属性。非严格模式下,该变量可以被删除。

局部变量

let 关键字可以用于声明局部变量,例如:

var x = 10;
// 此处 x = 10
{
    let x = 2;
    // 此处 x = 2
}
// 此处 x = 10

所以在 for 循环中的定义就可以使用 let,防止出现混乱。

常量

const 关键字用于声明常量,和 C++ 中的 const int 类似。

数据类型

JavaScript 中的数据类型非常简洁,常用的只有数字、字符串、数组、对象这四种。

数字

JavaScript 中所有数字都属于同一种数据类型,也就是说 不用区分整型和浮点型,实在是太爽了。所有数字都是 64 位,等同于 C++ 中的 long long。不过既然不搞 OI 了,上限是多少又如何呢?

num.toString(bit) 可以把数字转换为 bit 位表示下的字符串。例如:

var x = 4;
str = x.toString(2);
// str = "100"

NaN 是代表非数字值的数字值,而 isNaN() 函数可以用于判断一个值是否是数字。除此之外,JavaScript 还有 Infinity 常量,代表爆 Number 的临界值(有正负),不过 Infinity 进行运算后依然是 Infinity,非常贴心。

数字运算符和 C++ 一模一样,非常容易上手。

Math 对象包含很多数学运算函数,可以依需调用。

字符串

字符串可以用单引号 '...' 或者双引号 "..." 标识。JavaScript 中的字符串还可以和数字直接相加,例如:

str = "ncc" + 79601;

那么 str 就变为了 "ncc79601"

在字符串内还可以用反斜杠 \ 进行拆行,例如:

var str = "today is \
a good day";

str"today is a good day"

和数组类似,str[i] 可以访问单个字符;str.length 属性可计算字符串长度。

方法 描述
indexOf() 查找一个子串首次出现的位置。如果未找到,则返回 -1
lastIndexOf() 查找一个子串最后出现的位置
match() 查找是否出现字符串。若找到则返回原字符串;否则返回 null
toUpperCase() 全部转换为大写
toLowerCase() 全部转换为小写
split() 转换为数组,参数为分隔符

数组

数组和 C++ 略微不同,使用中括号 [...] 标识。例如:

a = [1, 2, 3];

或者:

a = new Array();
a[0] = 1;
a[1] = 2;
a[2] = 3;

又或者:

a = new Array(1, 2, 3);

数组下标默认从 0 开始,而 indexOf() 方法可以返回对应元素的下标。注意数组的类型也是 object

遍历数组的方法:

for (let i = 0; i < arr.length; i++) {
    console.log(arr[i]);
}
for (let i in arr) {
    console.log(arr[i]);
}
for (let i of arr) {
    console.log(i);
    // 注意和上一种的区别
}
方法 描述
concat() 合并数组(参数不定)
join() 用数组元素组合为字符串
pop() 弹出最后一个元素
push() 在末尾压入一个元素
reverse() 翻转数组
shift() 弹出第一个元素
unshift() 在开头添加元素
splice() 在第二位置插入元素
slice() 截取子数组(左闭右开)
sort() 排序
toString 转换到字符串

对象

JavaScript 面向对象,并且 JavaScript 中的所有事物都是对象。

创建对象

JavaScript 创建对象非常简单,利用类似 Python 中字典的描述方法即可。例如:

obj = {firstName: "Max", lastName: "Tedder", age: 16};

或者使用构造函数,例如:

function person(firstName, lastName, age) {
    this.firstName = firstName;
    this.lastName  = lastName;
    this.age       = age;
    // 属性
    function changeAge(newAge) {
        this.age = newAge;
    } // 方法
}
// 创建一个对象实例:
var obj = new person("max", "tedder", 16);

访问属性

键值对在 JavaScript 中也被称为 属性。访问属性有两种方法:

obj.firstName = "Max"; // 1
obj["firstName"] = "Max"; // 2

.constructor 可以返回对象的构造函数。

对象属性的遍历方法:

for (let i in obj) {
    console.log(i, ":", obj[i]);
}

原型对象

所有的 JavaScript 对象都会从一个 prototype(原型对象)中继承属性和方法。对于一个已经定义的对象构造器,是 不能向其中添加新属性 的,要添加一个新的属性需要在在构造器函数中添加(暴力修改);除此之外,也可以使用 prototype 属性。例如:

function person(firstName, lastName, age) {
    this.firstName = firstName;
    this.lastName  = lastName;
    this.age       = age;
}
person.prototype.nationality = "China";
// 所有 person 类的 nationality 属性都为 "China"
person.prototype.changeAge = function (newAge) {
        this.age = newAge;
    } // 方法

程序结构

循环与判断

与 C++ 完全一致,不做赘述。

函数

function 关键字用于声明函数。剩余的与 C++ 和 Python 类似,可以使用 return 语句结束函数并返回一个值。arguments.length 属性可以返回函数调用的参数个数。

对于 void() 关键字,将运行括号内的语句,而无返回值。例如 void(0) 就是什么也不做。

同时,声明函数时可以不带有函数名(匿名函数)。

参数

ES6 版本支持默认参数,和 Python 相同。

JavaScript 函数有个内置的对象 arguments,其包含了函数调用的参数数组。

对象函数

函数可以作为对象方法调用,其中 this 关键字指代的是对象自己。例如:

var obj = {
    firstName: "Max",
    lastName: "Tedder",
    fullName: function () {
        return this.firstName + " " + this.lastName;
    }
}
obj.fullName(); // 返回 "Max Tedder"

函数也可以用于构造对象,例如:

function name(arg1, arg2) {
    this.firstName = arg1;
    this.lastName  = arg2;
}

var person = new name("Max", "Tedder");
// 创建了一个对象 person

预定义函数方法

call()apply() 是预定义的函数方法,两个方法可用于调用函数,例如:

function mul(a, b) {
    return a * b;
}
x = mul.call(x, 3, 4);
// 等价于:
x = mul.apply(x, [3, 4]);
// x = 12

函数表达式

函数可以通过一个表达式定义(类似 Python 中的 lambda,实际上此处也属于匿名函数),相当于 把函数赋给了一个变量,这个变量充当函数名。例如:

var mul = function(a, b) { return a * b };
var x = mul(3, 4); // x = 12

ES6 版本引入了箭头函数,比函数表达式更加简洁,例如:

var mul = (a, b) => a * b;
// 相当于  (a, b) => { return a * b };
var x = mul(3, 4); // x = 12

自调用函数

自调用函数可以理解为在声明的时候就完成调用的函数。例如:

var a = (function f1(x) {
    console.log("调用 f1");
    return x;
})(6);
console.log(a);

则会将 6 作为参数执行 f1() 函数,而后作为返回值赋给 a

this 的使用

this 指代对象自己。既可以指代具体的一个对象(作为对象方法调用),又可以指代全体对象(纯粹函数调用)。参考资料

闭包

JavaScript 支持 嵌套函数,嵌套函数可以访问上一层的函数变量。由于函数内定义的变量无法在外部调用,因此为了实现这一功能,就有了 闭包 这个概念。

简单地说,闭包就是指用函数内部的函数来实现访问函数内变量的程序结构。例如,设 f2()f1() 内部定义的一个函数,则外部可以通过 f2() 来访问 f1() 内部变量的值。在这个过程中,由于 f1() 内定义的变量被 f2() 引用,所以变量 不会被垃圾回收机制回收,也就是说这个变量会一直留在内存池中。

一个用函数内部变量实现全局变量功能的计数器:

var add = (function (base) {
    // 自调用函数
    var counter = base;
    console.log("defined counter");
    console.log(counter);
    return function () {
        // 匿名函数
        counter++;
        console.log("add counter");
        return counter;
    }
})(1);
// 相当于把内层函数赋给了 add
console.log(add());
console.log(add());
console.log(add());

输出为:

defined counter
1
add counter
2
add counter
3
add counter
4

异常捕捉

与 Python 类似,JavaScript 也支持对异常的捕捉。格式为:

try {
    // 异常抛出
} catch(err) {
    // 异常处理
} finally {
    // 异常清理
}

使用 throw 可以抛出异常,抛出的异常可以为任意类型。例如:

try {
    throw "hello!";
} catch(err) {
    alert(err);
}

变量提升

JavaScript 中,函数及变量的声明都将被提升到函数的 最顶部。所以变量可以先使用再声明。

需要注意的是,带有初始化的变量声明不会被提升。例如:

var x;
var y = 1;

x 会被提升,而 y 不会。

调试

debugger 语句可以用于替代设置断点,方便进行调试。一般在浏览器的“检查元素”处可以进行调试。

严格模式

在 JavaScript 头部添加:

"use strict";

即可进入严格模式。严格模式可以使得 JavaScript 程序更加严谨,为未来的 JavaScript 铺路。

面向 HTML

表单

在 HTML 中有 <form> 标签描述的表单,表单经提交后即可被 JavaScript 处理。

下面这个例子实现了判断 myForm 中的 firstName 是否合法。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>
            test
        </title>
        <script>
            function judge() {
                var x, text;
                x = document.forms["myForm"]["firstName"].value;
                // 从 document 对象读取表单
                if (x == null || x == "") {
                    alert("输入不合法");
                    return false;
                }
                alert("提交成功")
                return true;
            }
        </script>
    </head>
    <body>
        <form name="myForm" action="form_action.php" onsubmit="judge()">
            <input type="text" name="firstName" value="First Name">
            <input type="submit" value="提交">
            <input type="reset" value="重置">
        </form>
    </body>
</html>

同样,JavaScript 还可以用于判断普通的输入是否合法,例如:

<script>
    function judge() {
        var x = document.getElementById("inputBox").value;
        var text;
        if (isNaN(x) || x <= 0) {
            text = "输入错误";
        } else {
            text = "输入正确";
        }
        document.getElementById("infor").innerHTML = text;
    }
</script>

<input id="inputBox" value="输入一个正数">
<button onclick="judge()">check</button>
<p id="infor"></p>

JSON

JSON 是用于存储和传输数据的格式。一般一个 JSON 文件描述了一个 JavaScript 对象,便于作为文本在服务器和客户端之间传输。例如:

{"processor": [
    {"name": "8700K", "gen": "8th"},
    {"name": "8705G", "gen": "8th"},
    {"name": "9700K", "gen": "9th"},
    {"name": "1065G7", "gen": "10th"}
]}

对象转 JSON

str = JSON.stringify(obj);

JSON 转对象

obj = JSON.parse(str);

HTML DOM

DOM 全名为 文档对象模型(Document Object Model),上文出现过的 document 对象就是一个 DOM。DOM 按照 HTML 标签的父子关系将整个 HTML 文档划分为一个树形结构。

查找 HTML 元素

通过 id 查找

使用 getElementById 方法,例如:

var x = document.getElementById("demo");

如果未找到,则会返回 null

通过标签名查找

使用 getElementsByTagName 方法,例如:

var x = getElementById("demo");
var y = getElementsByTagName("p");
// 查找 <main> 标签中的所有 <p> 标签(DOM Collection)

通过类名查找

使用 getElementByClassName 方法,例如:

var x = getElementByClassName("studentInfor");

改变 HTML

改变输出流

在上文提到的 document.write() 方法就是改变 HTML 输出流的方法,其可以在 HTML 文档中直接写入内容。当然,绝对不能在 HTML 加载完毕之后使用这个方法,不然会使原来的文档被覆盖。

改变内容

查找到元素后,修改 innerHTML 属性可以修改 HTML 元素的内容。例如:

document.getElementById("demo").innerHTML = "你好";

改变元素属性

查找到元素后,修改相应的属性即可。例如:

document.getElementById("image").src = "earth.jpg";

改变 CSS

查找到元素后,修改 style.property 即可改变相应的样式,例如:

document.getElementById("demo").style.color="#00ff00";
document.getElementById("demo").style.fontSize="larger";

使用事件

HTML 事件是发生在 HTML 元素上的事情。HTML 元素可以添加事件属性,例如:

<标签 事件名="JavaScript 代码()">

常见的 HTML 事件有:

事件名 描述
onchange 元素改变
onclick 点击元素
onmouseover 在元素上移动鼠标
onmouseout 从元素上移出鼠标
onkeydown 按下键盘
onload 完成页面加载
onfocus 输入框获得焦点
onblur 输入框失去焦点

例如:

document.getElementById("button").onclick = function () {
    document.getElementById("demo").innerHTML = Date();
} // 实用了匿名函数

注意:传入事件名的应该是函数,如果是调用已定义的函数,则函数名后 不应该加括号

addEventListener() 方法可以向元素添加事件句柄,这使得一个元素可以拥有多个事件句柄。新添加的事件句柄不会覆盖已有的事件句柄。可以向 任何 DOM 对象 添加事件监听,不仅仅是 HTML 元素。如:window 对象。

格式为:

element.addEventListener(event, function, useCapture);
// 此处的事件名要去掉 "on"
// useCapture 默认为 false

useCapture 指的是事件传递方式,它决定了事件触发的顺序。当属性为 false,传递方式为 冒泡(由内而外);当属性为 true,传递方式为 捕获(由外而内)。 例如:

document.getElementById("button").addEventListener("click", function () {
    alert("Hello World!");
});

如果需要传递参数,则使用匿名函数调用带参数的函数。

removeEventListener() 方法可以用于移除事件句柄,格式与 addEventListener() 一致(无 useCapture 参数)。

为了使 EventListener 稳定工作,可以把 addEventListener 写入 window.onload 事件,也就是:

window.onload = function () {
    var x = document.getElementById("button");
    x.addEventListener("click", myFunction);
};

新建元素

要创建新的 HTML 元素(节点)需要先创建一个元素,然后用 appendChild() 方法在已存在的元素中添加它。例如:

<div id="div1">
    <p>第一段</p>
    <!--新的一段会出现在这里-->
</div>
<script>
    var newPara = document.createElement("p");
    var newNode = document.createTextNode("第二段");
    newPara.appendChild(node);
    var element = document.getElementById("div1");
    element.appendChild(para);
</script>

appendChild() 方法可以把节点添加到元素末尾,而 insertBefore() 方法则可以把节点添加到特定元素之前。在调用次方法时,必须传入参照节点的对象。例如:

<div id="div1">
    <!--新的一段会出现在这里-->
    <p id="p1">第一段</p>
</div>
<script>
    var newPara = document.createElement("p");
    var newNode = document.createTextNode("第二段");
    newPara.appendChild(node);
    var element = document.getElementById("div1");
    var child = document.getElementById("p1");
    element.insertBefore(newPara, child);
    // 注意两者函数参数的区别
</script>

removeChild() 方法可以移除已存在的元素。其使用方法和 appendChild() 相同。需要注意的是,使用 removeChild() 方法时 必须知道目标元素的父元素

replaceChild() 方法可以替换已存在的元素。例如:

<div id="div1">
    <p id="p1">第一段</p>
    <p id="p2">第二段</p>
</div>
<script>
    var newPara = document.createElement("p");
    var newNode = document.createTextNode("第三段");
    newPara.appendChild(node);
    var element = document.getElementById("div1");
    var child = document.getElementById("p2");
    element.replaceChild(newPara, child);
</script>

HTML Collection

getElementsByTagName() 方法返回 HTML Collection 对象,包含对应标签名的所有元素。HTML Collection 对象虽然支持数组的一些操作,但其 不是数组,而是特殊的一种对象。

下面这个例子修改所有段落的背景颜色:

var myCollection = document.getElementsByTagName("p");
for (let i = 0; i < myCollection.length; i++) {
    myCollection[i].style.backgroundColor = "red";
}

NodeList

NodeList 不同于却类似于 HTML Collection 对象,是一个从文档中获取的节点列表。

例如将上一个例子改为用 NodeList 实现:

var myNodeList = document.querySelectorAll("p");
// 获取 <p> 元素的集合
for (let i = 0; i < myNodeList.length; i++) {
    myNodeList[i].style.backgroundColor = "red";
}

JavaScript Window

浏览器对象模型(BOM)使 JavaScript 有能力与浏览器交流。window 对象就是一个 BOM。HTML DOM 的 document 也是 window 对象的属性(window.document),不过在代码中可以将 window 省略。

Window 尺寸

属性 描述
window.innerHeight 内部高度(包括滚动条)
window.innerWidth 内部宽度(包括滚动条)
screen.availHright 可用屏幕高度
screen.availWidth 可用屏幕宽度

Window 地址

属性 / 方法 描述
location.hostname 主机域名
location.pathname 当前页面路径
location.port 主机端口
location.protocol web 协议
location.href 当前页面 url
location.assign(url) 加载新文档

Window Navigator

navigator 包含访问者浏览器的相关信息。

弹窗

方法 描述
alert() 警告框
confirm() 确认框
prompt() 提示框

计时事件

方法 描述
setInterval(function, ms) 间隔指定毫秒数循环执行指定代码
clearInterval() 停止执行 setInterval() 方法
setTimeout(function, ms) 在指定毫秒数后执行指定代码
clearTimeout() 停止执行 setTimeout() 方法

Cookie

Cookie 用于存储 web 页面的用户信息。在连接关闭后,服务端不会记录用户的信息,此时就需要 Cookie 记录客户端的用户信息。例如:

document.cookie="username=Max Tedder; userid=114514";

删除 Cookie 时,只需要把等号后的内容删除。例如:

document.cookie="username=; userid=";

不过由于涉及 Cookie 的操作在现阶段用处不大,因此不做赘述。


至此,JavaScript 的基本内容就讲解完毕了。