Live Note

Remain optimistic

AJAX:实现原理与技术背景

在现代 Web 开发中,AJAX(Asynchronous JavaScript and XML,异步 JavaScript 和 XML)是一种不可或缺的技术。它通过异步通信实现了网页的无刷新更新,极大地提升了用户体验。那么,AJAX 是如何实现的?它的出现又有着怎样的背景?

从静态到动态的 Web 进化

Web 1.0 的局限性

在互联网早期,网页主要依赖同步请求模型。当用户点击链接或提交表单时,浏览器会向服务器发送请求,然后等待服务器返回全新的 HTML 页面。这一过程会导致整个页面刷新,用户不得不面对短暂的空白和等待。对于简单的静态网站,这种模式尚可接受,但随着 Web 应用复杂性的增加,例如实时聊天、动态表单验证等需求,这种方式的弊端愈发明显——用户体验差、交互效率低。

动态 Web 的需求

进入 21 世纪,互联网用户对 Web 应用的要求日益提高。人们希望网页能像桌面应用程序一样,提供流畅的交互和实时更新。例如,在输入搜索关键词时实时显示建议,或在聊天应用中即时刷新消息。这些需求推动了 Web 技术向动态化、异步化的方向发展。

技术基础的成熟

AJAX 并非凭空出现,它建立在一系列已有技术之上:

  • JavaScript:作为客户端脚本语言,负责处理用户交互和动态更新页面内容。
  • XMLHttpRequest(XHR):浏览器内置对象,允许在后台与服务器通信。
  • DOM(文档对象模型):提供操作页面结构的接口,使数据能够无缝嵌入。
  • XML:最初用于数据交换(后来 JSON 逐渐取而代之)。

这些技术的成熟为 AJAX 的诞生奠定了基础。尤其是 XMLHttpRequest,最早由微软在 Internet Explorer 5 中引入,随后被其他浏览器标准化,成为 AJAX 的核心组件。

Read more »

什么是 Basic Authentication?

Basic Authentication 是一种 HTTP 协议的认证方式,它使用用户名和密码对客户端进行身份验证。
RFC 2617 定义了 Basic Authentication 协议,它规定了客户端如何向服务器发送请求,以及服务器如何验证用户名和密码。
当用户试图访问受保护的资源时,服务器会判断 HTTP Header 中的 Authorization 字段是否包含有效的 Basic Authentication 认证信息。如果没有,服务器会返回 401 Unauthorized 状态码,要求客户端提供认证信息,并在响应头中添加 WWW-Authenticate: Basic 字段。

Read more »

Battery

Definition

A Fenwick tree or binary indexed tree is a data structure that can efficiently update elements and calculate prefix sums in a table of numbers.

  1. update(index, delta): 将delta加到index位置上。
  2. prefixSum(index): 求 sum[1, index]的值。
  3. rangeSum(from, to): 求 sum[from, to] 的值。

时间复杂度:

  1. update(index, delta): 更新需要循环更新每一个parent的值,从index开始到最后一个parent。复杂度为O(n)
  2. prefixSum(index): 直接返回sum[index + 1]即可,复杂度为O(1)
  3. rangeSum(from, to): 直接返回sum[to + 1] - sum[from]即可,复杂度为O(1)

Build

现在有个nums初始数组,通过这个数组构造一个BITArray
构造Binary Indexed Array

  1. 实现insert(index, delta):
1
2
3
4
5
6
function insert(index, delta) {
while (index < this.BITArray.length) {
this.BITArray[index] += delta
index += index & -index // BIT中,parent的index计算方法为:parent = child + (child & -child)
}
}
  1. 构造 BITArray:
1
2
3
4
5
6
function MyArray(nums) {
this.BITArray = new Array(nums.length + 1).fill(0)
for (let i = 0, len = nums.length; i < len; i++) {
this.insert(i + 1, nums[i]) // 在每个index处循环插入,delta就是初始值。
}
}
  1. 实现sum(index):
1
2
3
4
5
6
7
8
9
function sum(index) {
let sum = 0
while (index > 0) {
sum += this.BITArray[index]
index -= index & -index // BIT中,child的计算方法为:child = parent - (parent & -parent)
}

return sum
}
  1. 实现perfixSum(from, to)
1
2
3
function perfixSum(from, to) {
return this.sum(to + 1) - this.sum(from)
}

在某个论坛有人问了下面的一段代码

1
2
3
4
5
6
7
8
9
10
11
12
function f() {
console.log("outside")
}
function a() {
f()
{
function f() {
console.log("inside")
}
}
}
a()

问的是为什么在浏览器中是f is not a function
这个问题其实很好回答,存在函数提升,但是仔细想想又不对,因为函数提升是把整个函数都提升到当前作用域,所以按理来说 f 并不会是undefined
如果按照函数提升的话,结果应该是像这样:

1
2
3
4
5
6
7
8
9
10
11
function f() {
console.log("outside")
}

function a() {
function f() {
console.log("inside")
}
f() { }
}
a()

所以结果应该是inside才对,用 IE7 可以发现结果确实是 inside。
那这里为什么是 undefined 呢?
后面那位兄弟说在阮一峰的《ES6 入门》中说道,ES6 规定块级作用域中的函数定义规定为函数表达式。如果是定义为函数表达式的话,那就会像这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function f() {
console.log("outside")
}

function a() {
var f
f()
{
var f = function f() {
console.log("inside")
}
}
}
a()

这么一来这个undefined就可以解释了,因为只存在变量提升,而还未定义,所以就会出现undefined。但是那本书后还加了一句,浏览器并未遵循实现。
后面在 starkoverflow 上找到了一个老哥的回答,他的解释为这是因为strict & non-strict mode的不同。

  • let 和 const 在两种模式下行为一样
  • function 在两种模式下的行为就不是一样的,因为浏览器会扩展非严格模式,从而兼容老代码。

所以,在strict mode中,第一段代码输出的是outsidenon-strict mode中就会报错,因为会变成下面这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function f() {
console.log("outside")
}

function a() {
var f // function-scoped
f()
{
let f1 = function () {
console.log("inside")
} // block-scoped
f = f1
}
}
a()

MDN 中关于Block-level functions in non-strict code的也只有一句话: Don't.