Web前端开发(4) JavaScript、DOM、BOM
2023-08-09 14:53:19 # NJU # Web前端开发

6.1 JS Basics

1 客户端基础

1.1 客户端脚本

image-20221125151322702

1.2 客户端编程

  • HTML适合于开发静态页面
    • 可以指定文本/图像布局, 链接…
    • Web页面每次访问时看起来都是一样的
    • 为了开发交互/响应式页面, 必须以某种形式集成编程
  • 客户端编程
    • 程序是用单独的编程(或脚本)语言编写的
      • 例如, JavaScript, JScript, VBScript
    • 程序被嵌入到Web页面的HTML中, 使用(HTML)标记来标识程序组件
      • 例如, <script type="text/javascript"> ... </script>
    • 浏览器在加载页面时执行程序, 将程序的动态输出与HTML的静态内容集成在一起
    • 还允许用户(客户端)输入信息并对其进行处理, 可在将输入提交到远程服务器之前对其进行验证

1.3 客户端与服务器端编程

  • 客户端脚本(JavaScript)的好处:
    • 可用性: 可以修改页面而不必返回到服务器(更快的UI)
    • 效率: 可以在不等待服务器的情况下对页面进行小而快速的更改
    • 事件驱动: 可以响应用户操作, 如点击和按键源代码
    • 平台独立性: 任何支持脚本的浏览器都可以解释代码
  • 服务器端编程(例如PHP)的好处:
    • 安全性: 可以访问服务器的私有数据;客户端看不到
    • 兼容性: 不受浏览器兼容性问题的影响
    • 功能全: 可以写文件, 打开与服务器的连接, 连接数据库…

1.4 常见脚本任务

  • 向Web页面添加动态特性
    • 表单数据的验证(可能是最常用的应用程序)
    • 图像翻转
    • 时间敏感或随机页面元素
    • 处理cookies
  • 定义Web接⼝
    • 利用按钮, 文本框, 可点击的图像, 提示等
  • 客户端脚本的限制
    • 因为脚本代码嵌入在页面中, 所以它对外界是可见的
    • 出于安全原因, 脚本所能做的事情是有限的
      • 例如, 无法访问客户端的硬盘驱动器
    • 因为它们被设计为在任何机器平台上运行, 所以脚本不包含特定于平台的命令
    • 脚本语言功能不全
      • 例如, JavaScript对象非常粗糙, 不适合大型项目开发

2 JavaScript概述

2.1 何为JavaScript

  • 从”玩具”到流行
    • 网页, AJAX, Web 2.0/3.0
    • 大量web应用
    • APP
    • 控制硬件, Arduino, Tessel, Espruino, NodeBots等, 应用于嵌入式系统、IOT、机器人等领域
    • 桌面程序(Electron), SpaceX 龙飞船的触控 UI 基于 Chromium + JavaScript 技术栈开发, 开放的 Web 技术就此成为了人类首个应用到载人航天领域的 GUI 技术栈
    • 服务端
    • 命令行⼯具

Always bet on JS

  • Brendan Eich 在 2011 年一次名为「JSLOL」[Eich 2011e] 的会议演讲中, 是这么描述 JavaScript 的:
    • 最早他们说 JavaScript 没法做「富互联网应用」。
    • 然后他们说 JavaScript 没法快起来。
    • 然后他们说 JavaScript 没法修复语言问题。
    • 然后他们说 JavaScript 没法做多核与 GPU 运算。
    • 他们每次都错了!我建议:永远押宝在 JS。

阿特伍德定律

  • Atwood’s Law是Jeff Atwood在2007年提出的
    • “any application that can be written in JavaScript, will eventually be written in JavaScript.”
    • 任何可以用JavaScript来写的应用, 最终都将用JavaScript来写

发展史

2.2 JS的优势

  • 性能
    • JIT
    • 垃圾收集和动态绑定的开销
  • 对象
    • JavaScript使用原型继承模型
  • 语法
    • 对于任何有 C 家族语言使用经验的人来说很熟悉, 比如c++、Java、c# 和 PHP
  • 一等函数
    • JavaScript中的几乎所有东西都是对象, 包括函数。
  • 事件
    • 在浏览器内部, 一切都在一个事件循环中运行
  • 可重用性
    • 最可移植、可重用的代码
    • JavaScript可以模块化和封装

2.3 JS概述

  • JavaScript 是一种脚本语言
  • JavaScript 程序由 JavaScript 解释器/引擎计算和执行
    • Rhino, SpiderMonkey, V8, Squirrelfish
  • 主流用途和用法:
    • 在运行时公开应用程序的对象, 用于定制/嵌入用户逻辑。
    • 改进网站的用户界面
    • 让你的网站更容易浏览
    • 在不重新加载页面的情况下替换页面上的图像
    • 表单验证
    • 页面修饰和特效
    • 动态内容操作
    • 新兴的Web 2.0:客户端功能在客户端实现
    • 还有很多…

2.4 JavaScript vs. Java

  • 解释的, 不是编译的
  • 更宽松的语法和规则
    • 更少、更”松散”的数据类型
    • 变量不需要声明
    • 错误通常是无声的(少数例外)
  • 关键结构是函数而不是类
    • “一等”函数
  • 包含在网页中, 并与其HTML/CSS内容集成

2.5 使用<script>

  • 为网页嵌入脚本

    <script>
    script commands and comments
    </script>
  • 访问外部的脚本

    <script src="url">
    script commands and comments
    </script>

3 JavaScript基本语法

3.1 语言基础

  • JavaScript大小写敏感
    • HTML 大小写不敏感; onClick, ONCLICK, … are HTML
  • 以回车或分号(;)结束的语句
    • x = x + 1; same as x = x + 1
    • 分号减少错误
  • JavaScript 代码块

    • JavaScript 语句通过代码块的形式进行组合
    • 块由左花括号开始, 由右花括号结束
    • 块的作用是使语句序列一起执行
  • 注释: 与Java的注释语法相同

  • if/else 条件语句

    • 结构与Java的if/else语句相同
    • JavaScript几乎允许任何东西作为条件
  • for循环

    • for、for-in、for-of
    var x;
    var txt="";
    var person={fname:"Bill",lname:"Gates",age:56};
    for (x in person) {
    txt = txt + person[x];
    }
  • while, do/while

    • 同Java
    • break 和 continue 关键字也和 Java 中的一样
    cars=["BMW","Volvo","Saab","Ford"];
    var i=0;
    while (cars[i]){
    document.write(cars[i] + "<br>");
    i++;
    }

3.2 对象

  • 对象是命名属性的集合
    • 简单视图: 哈希表或关联数组
    • 可以通过一组 名称:值 对进行定义
      • objBob = {name: "Bob", grade: 'A', level: 3};
    • 新成员可以随时添加
      • objBob.fullname = 'Robert';
    • 可以有方法
  • 数组、函数也是对象
    • 对象的属性可以是函数(=方法)

3.3 变量与类型

  • 变量
    • var, let, const(case sensitive)
    • Define implicitly by its first use, which must be an assignment
      • Implicit definition has global scope, even if it occurs in nested scope?
  • 类型没有指定, 但JS确实有类型(“loosely typed”)
    • Number, Boolean, String, Array, Object, Function, Null, Undefined
    • can find out a variable’s type by calling typeof
var name = "Gates", age = 60, job = "CEO";
var carname = "Benz";
var carname;

3.4 数值

  • 数值在内部是通过双精度浮点型的形式表示的 (没有 int vs. double)
  • 同样的运算符:+ - * / % ++ -- etc
  • 优先级相似java
  • 许多运算符自动转换类型
    • "2" * 3 is 6

image-20221129170410768

3.5 IEEE 754

  • https://0.30000000000000004.com/#ada

  • EcmaScript的安全整数

    • Number.MAX_SAFE_INTEGER, 9007199254740991
    • 最大安全整数和最小安全整数之间的整数统称为安全整数
    • 54位有符号整数类型
    • 只有在所有的运算因子、运算结果以及中间结果都是安全整数的情况下, 才能精确的整数计算, 才适用于加法结合律和乘法分配律, 一旦有一项的值不是安全整数, 变不可控。
  • JS仅有number这个数值类型, 而number采用IEEE 754 64位双精度浮点数编码。而浮点数表示方式具有以下特点:

    • 浮点数可表示的值范围比同等位数的整数表示方式的值范围要大得多
    • 浮点数无法精确表示其值范围内的所有数值, 而有符号和无符号整数则是精确表示其值范围内的 每个数值
    • 浮点数只能精确表示 m * $2^e$的数值
    • 当 biased-exponent 为 $2^{e-1}$ - 1 时, 浮点数能精确表示该范围内的各整数值
    • 当 biased-exponent 不为 $2^{e-1}$ - 1时 , 浮点数不能精确表示该范围内的各整数值
  • 由于部分数值无法精确表示(存储), 于是在运算统计后偏差会愈见明显。

  • 通常推荐先将数字转换成整数。

  • 比较两个浮点数差值的绝对值, 是否超过误差精度

    function compareTwo(n1,n2) {
    return Math.abs( n1 - n2 ) < Number.EPSILON;
    }
    compareTwo(0.1+0.2, 0.3);

3.6 String

  • 方法: charAt, charCodeAt, fromCharCode, indexOf, lastIndexOf, replace, split, substring, toLowerCase, toUpperCase

    • charAt returns a one-letter String (there is no char type)
  • length 属性 (非Java中的方法)

    var txt = "Hello world!";
    document.write(txt.length)
  • ""''

  • 连接 +

    • 1 + 1 is 2, "1" + 1 is “11”
  • 转义, 与Java中的相同: \' \" \& \n \t \\

  • 在数值和字符串之间转换

    var x = 30;
    var s1 = "" + x; //"30"
    var n1 = parseInt("10 oranges"); //10
    var n2 = parseFloat("hello"); //NaN
  • 访问字符串的字符

    var firstLetter = s[0]; //fails in IE
    var firstLetter = s.charAt(0);
    var lastLetter = s.charAt(s.length - 1);
  • 字符串拼接变量

    // old
    var message = "The user "+ user.firstName + " " + user.lastName +
    " cannot be "+ action + "because " + validationError;
    // new
    var message = `The user ${user.firstName} ${user.lastName} cannot be
    ${action} because ${validationError}`;

3.7 布尔式犯蠢类型

  • 任何值都可以用作布尔值

    • false: 0, -0, 0.0, NaN, “”, null, undefined
    • true: anything else
  • 将值显式转换为布尔值

    • var boolValue = Boolean(otherValue);
    • var boolValue = !!(otherValue);
if(o !== null) ...
!!"" -> false
!![] -> true
+!![] -> 1

3.8 null, NaN, undefined

  • NaN
    • isNaN()
    • typeof(NaN) -> number
    • 比如字符串转换成数值失败时
    • 与所有的值都不相等。包括它自己
  • undefined
  • null
    • typeof(null) —> object
  • (-)Infinity:大到无法表示的值
var x = 1000 / 0;  // number
isNaN(x); // false

3.9 ===

  • 平常业务中比较建议尽量不要使用 ==!=。这两个比较的时候会做一些强制的类型转换, 所以比较结果很可能有误。务必使用 === 和 !==

image-20221129174208384

undifined == null -> true

3.10 Math对象

  • methods: abs, ceil, cos, floor, log, max, min, pow, random, round, sin, sqrt, tan
  • properties: E, PI
var pi_value = Math.PI;
var sqrt_value = Math.sqrt(30);

3.11 逻辑运算符

  • > < >= <= && || ! == != === !==

  • 大多数逻辑运算符自动转换类型

    • 5 < “7” 为true
    • 42 == 42.0 为 true
    • “5.0” == 5 为 true
  • ===!==是严格的相等检查, 同时比较类型和值

    • “5.0” === 5 is false

3.12 数组

  • 两种方法初始化数组
  • 长度属性(在添加元素时根据需要增长)
var empty = [];
var mycars = new Array();
mycars[0] = "Saab"
mycars[1] = "Volvo"
mycars[2] = "BMW"
var misc = new Array(1.1, true, "BMW");

3.13 Array方法

  • 数组可以实现多种数据结构: list, queue, stack, …
  • 方法: concat, join, pop, push, reverse, shift, slice, sort, splice, toString, unshift
    • push and pop add / remove from back
    • unshift and shift add / remove from front
    • shift and pop return the element that is removed
var fruits = ["Banana", "Orange"];
fruits.push("Apple");
fruits.unshift("Mango");
fruits.pop();
fruits.shift();
fruits.sort();
alert(fruits);

3.14 函数

function functionName(parameters) {
//
}
  • 声明可以出现在函数体中

    • Local variables, “inner” functions
    • =>
      • (arg1, arg2) => {//something here}
  • 匿名函数

    • (function (x,y) {return x+y}) (2,3);

3.15 参数

  • 参数传递

    • 基本类型按值传递, 对象按引用传递
  • 调用可以提供任意数量的实参

    • functionname.length : # of arguments in definition
    • functionname.arguments.length : # args in call

6.2 JS DOM、BOM

1 DOM

  • 当一个网页被加载时, 浏览器会创建一个页面的文档对象模型 (Document Object Model, DOM)
  • HTML DOM 模型被构造成一个对象树

image-20221130222429027

1.1 功能

  • 通过对象模型, JS获得了创建动态 HTML 所需的所有功能
    • JavaScript可以更改页面中的所有HTML元素
    • JavaScript可以更改页面中的所有HTML属性
    • JavaScript可以更改页面中的所有CSS样式
    • JavaScript可以删除现有的HTML元素和属性
    • JavaScript可以添加新的HTML元素和属性
    • JavaScript可以对页面中所有现有的HTML事件做出反应
    • JavaScript可以在页面中创建新的HTML事件

1.2 DOM是什么

  • DOM是W3C(万维网联盟)标准
  • DOM定义了⼀个访问文档的标准:
    • W3C文档对象模型(DOM)是⼀个平台和语言无关的接口, 它允许程序和脚本动态访问和更新文档的内容、结构和样式
  • W3C DOM标准分为3个不同的部分:
    • 核心DOM——所有文档类型的标准模型
    • XML DOM——XML文档的标准模型
    • HTML DOM——HTML文档的标准模型

1.3 HTML DOM

  • HTML DOM 是 HTML 的标准对象模型和编程接口。它定义了:
    • HTML 元素作为对象
    • 所有 HTML 元素的属性
    • 访问所有 HTML 元素的方法
    • 所有 HTML 元素的事件
  • 换句话说: HTML DOM 是获取、更改、添加或删除 HTML 元素的标准

1.4 DOM 编程接口

  • 可以使用 JS(以及其他编程语言)访问 HTML DOM
  • 在 DOM 中, 所有 HTML 元素都定义为对象
  • 编程接口是每个对象的属性和方法
  • 属性是可以获取或设置的值(如更改 HTML 元素的内容)
  • 方法是可以执行的操作(如添加或删除 HTML 元素)

1.5 DOM 元素

  • 页面上的每个元素都有一个对应的 DOM 对象
  • 使用 objectName.attributeName 访问/修改 DOM 对象的属性
  • 事实上, 浏览器在运行时将 Web 页面计算为相应的 DOM 对象
<script>
document.getElementById("demo").innerHTML = "Hello World!";
</script>

1.6 查找 HTML 元素

  • 通过 id 查找
    • var myElement = document.getElementById("intro");
  • 通过 标记名 查找
    • var x = document.getElementsByTagName("p")
  • 通过 类名 查找
    • var x = document.getElementsByClassName("intro");
  • 通过 CSS选择器 查找
    • var x = document.querySelectorAll("p.intro");
  • 通过 HTML对象集合 查找
    • var x = document.forms["frm1"];

1.7 JS HTML DOM 导航

  • 页面的元素嵌套在对象的树状结构中——DOM树
    • DOM 具有遍历该树的属性和方法

image-20221130222429027

1.7.1 DOM 节点类型

  • 每个节点都有 nodeType(节点类型)、nodeName(节点名称)、nodeValue(节点值) 属性
  • HTML DOM 中常出现的类型
    • Element(元素节点)
    • Text(文本节点)
    • Attr(属性节点)
    • Comment(注释节点)
    • Document(文档节点)
    • DocumentFragment(文档片段节点)
<p>
This is a paragraph of text with a
<a href="/path/page,html">link in it</a>
</p>

image-20221201152247948

1.7.2 遍历 DOM 树

  • 每个节点的 DOM 对象都有以下属性

    image-20221201152838756

  • 浏览器不兼容问题 (IE很糟糕)

  • 遍历实例

    image-20221201152951766

2 BOM

  • 浏览器对象模型(Browser Object Model, 简称 BOM)是 JS 的组成部分之一, BOM 赋予了 JS 程序与浏览器交互的能力
  • 每个浏览器的 JS 程序都可以引入以下全局对象

image-20221201153325357

2.1 window 对象

  • 所有浏览器都支持 window 对象。他表示浏览器的窗口
  • 所有 JS 全局对象、函数和变量都自动成为窗口对象的成员
  • 在客户端 JS 中, Window 对象是全局对象, 所有的表达式都在当前的环境中计算。也就是说, 要引用当前窗口根本不需要特殊的语法, 可以把那个窗口的属性作为全局变量来使用
    • window.document.getElementById("header");
    • document.getElementById("header");

2.2 document 对象

  • 每个载入浏览器的 HTML 文档都会成为 Document 对象
  • Document 对象使我们可以从脚本中对 HTML 页面中的所有元素进行访问
  • 提示: Document 对象是 Window 对象的一部分, 可以通过 window.document 属性对其进行访问
  • Document 对象是 HTML 文档的根节点
  • 属性:
    • anchors, body, cookie, domain, forms, images, links, referrer, title, URL
  • 方法
    • getElementById
    • getElementsByName
    • getElementsByTagName
    • close, open, write, writeln

2.3 location 对象

  • Location 对象包含有关当前 URL 的信息
  • Location 对象是 Window 对象的一个部分, 可通过 window.location 属性来访问
  • 例子
    • window.location.href 设置或返回完整的 URL
    • window.location.hostname 设置或返回当前 URL 的主机名
    • window.location.pathname 设置或返回当前 URL 的路径部分
    • window.location.protocol 设置或返回当前 URL 的协议。 (http://或https://)
    • window.location.assign() 加载新的文档

2.4 navigator 对象

  • window.navigator 对象包含有关浏览器的信息
  • 注释: 没有应用于 navigator 对象的公开标准, 不过所有浏览器都支持该对象
  • 例子:
    • navigator.appName 返回浏览器的名称
    • navigator.onLine 返回指明系统是否处于脱机模式的布尔值
    • navigator.appCodeName 返回浏览器的代码名
    • navigator.platform 返回运行浏览器的操作系统平台
<script>
document.getElementById("demo").innerHTML =
"Name is " + navigator.appName + ". Code name is " +
navigator.appCodeName;
</script>

<p id="demo"></p>
  • 注意: 来自 navigator 对象的信息通常会产生误导, 不应该用于检测浏览器版本, 因为:
    • 不同的浏览器可以使用相同的名称
    • 浏览器所有者可以更改浏览器数据
    • 一些浏览器为了绕过站点测试而故意错误标识自身
    • 浏览器不能报告比浏览器晚发布的新操作系统

2.5 screen 对象

  • window.screen 对象包含有关客户端显示屏幕的信息

  • 注释: 没有应用于 screen 对象的公开标准, 不过所有浏览器都支持该对象

  • 属性:
    • screen.width 返回显示器屏幕的宽度
    • screen.height 返回显示屏幕的高度
    • screen.availWidth 返回显示屏幕的宽度 (除 Windows 任务栏之外)
    • screen.availHeight 返回显示屏幕的高度 (除 Windows 任务栏之外)
    • screen.colorDepth 返回目标设备或缓冲器上的调色板的比特深度
    • screen.pixelDepth 返回显示屏幕的颜色分辨率(比特每像素)

2.6 history 对象

  • window.history 对象包括用户(在浏览器窗口中)访问过的 URL
  • History 对象是 window 对象的一部分, 可通过 window.history 属性对其进行访问
  • 为了保护用户的隐私, 对 JS 访问该对象的方式有限制
  • 注释: 没有应用于 History 对象的公开标准, 不过所有浏览器都支持该对象
  • 方法:
    • history.back() 加载 history 列表中的前一个 URL
    • history.forward() 加载 history 列表中的下一个 URL

2.7 Cookies

  • Cookies 可以让你在网页中存储用户信息
  • cookie 是存储在计算机上的小文本文件中的数据
  • Cookies 的发明是为了解决”如何记住用户信息”的问题:
    • 当⼀个用户访问⼀个网页时, 他的名字会被存储在⼀个 cookie 中。下次用户访问该页面时, cookie 就会”记住”他的名字。
  • cookie 以 名称-值对 的形式保存, 例如 username=Tom
  • 当浏览器从服务器请求⼀个网页时, 属于该网页的 cookies 被添加到请求中。通过这种方式, 服务器获得必要的数据来”记住”关于用户的信息
  • JS 可以使用 document.cookie 属性来创建、读取和删除cookie

    • 例如: document.cookie="username=Tom";
  • 还可以添加一个过期日期(以UTC时间表示)。缺省情况下, 关闭浏览器时删除 cookie:

    • document.cookie="username=Tom; expires=Thu, 18 Sep 2015 10:00:00 UTC";
  • 通过路径参数,可以告诉浏览器 cookie 属于哪个路径。缺省情况下,cookie 属于当前页面

    • document.cookie="username=Tom; expires=Thu, 18 Sep 2015 10:00:00 UTC; path=/";

2.7.2 如何使用 cookies

  • 使用 JS 读取一个 cookie

    • var x = document.cookie;

    • document.cookie will return all cookies in one string much like: cookie1=value1; cookie2=value2; cookie3=value3;

  • 使用 JS 修改

    • document.cookie="username=Tom; expires=Thu, 18 Sep 2015 10:00:00 UTC; path=/";
  • 使用 JS 删除 Cookie,非常简单,只需将expires参数设置为⼀个过去的日期:

    • document.cookie = "username=; expires=Thu, 01 Jan 1970 00:00:00 UTC";
    • 注意,在删除 cookie 时不必指定 cookie 值。
function setCookie(cname, cvalue, exdays) {
var d = new Date();
d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
var expires = "expires=" + d.toGMTString();
document.cookie = cname + "=" + cvalue + "; " + expires;
}
function getCookie(cname) {
var name = cname + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i];
while (c.charAt(0) == ' ') {
c = c.substring(1);
}
if (c.indexOf(name) == 0) {
return c.substring(name.length, c.length);
}
}
return "";
}
function checkCookie() {
var user = getCookie("username");
if (user != "") {
alert("Welcome again " + user);
} else {
user = prompt("Please enter your name:", "");
if (user != "" && user != null) {
setCookie("username", user, 30);
}
}
}