Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >AJAX 原理与 CORS 跨域

AJAX 原理与 CORS 跨域

作者头像
Bug开发工程师
发布于 2018-09-21 01:47:32
发布于 2018-09-21 01:47:32
1.5K00
代码可运行
举报
文章被收录于专栏:码农沉思录码农沉思录
运行总次数:0
代码可运行

作者:赵帅强

原文:

https://segmentfault.com/a/1190000011549088

ajax作为前端开发必需的基础能力之一,你可能会使用它,但并不一定懂得其原理,以及更深入的服务器通信相关的知识。在最近两天的整理过程中,看了大量的文章,发现自己的后端能力已经限制自己在网络通信相关的知识领域的探索,还是应该尽快补齐短板。

下面我们来聊一聊 ajax相关的东西,包括 xhr/xdr/ajax/cors/http的一部分内容,其中会抛弃一些被弃用的历史包袱,如IE6/7等。

Ajax的出现

2005年, JesseJamesGarrett提出了Ajax的技术,其全称为 AsynchronousJavascriptandXML,Ajax的核心是 XMLHttpRequest对象,简称 XHR,它用于使浏览器向服务器请求额外的数据而不卸载页面,极大的提高了用户体验。在此之前,其实这种技术已经存在并被一些人实现,但并没有流行也没有被浏览器支持。不过在此之后,IE5第一次引入 XHR对象,并支持 ajax技术,后续被所有浏览器支持。

XMLHttpRequest对象和请求

XHR是一个API,为客户端提供服务端和客户端之间通信的功能,并且不会刷新页面。它并不仅仅能取回XML类型的数据,而能取回所有类型的数据,除了http协议,还支持file和ftp协议。我们可以通过其构造函数来创建一个新的 XHR对象,这个操作需要在其它所有操作之前完成:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var xhr = new XMLHttpRequest();

通过控制台我们可以很方便看到 XHR的原型链: Object->EventTarget->XMLHttpRequestEventTarget->XMLHttpRequest。它拥有原型链上和本身的方法和属性,现在看下我们常用的方法:

我们解释下它的几个主要方法,我们在创建了新的xhr对象之后,首先要调用它的 open()方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 第一个参数可以为get/post等,表示该请求的类型
// 第二个参数是请求的url,可以为相对路径或绝对路径
// 第三个参数代表是否异步,为true时异步,为false时同步
// 第四五个参数为可选的授权使用的参数,因为安全性不推荐明文使用
xhr.open('get', 'example.php', true, username, password);

在这里受同源策略的影响,当第二个参数url跨域的时候会被浏览器报安全错误。同源策略指的是当前页面和目标url协议、域名和端口均相同。后面也会讲到,除IE之外的浏览器通过XHR对象实现跨域请求,只需将url设置为绝对url即可。

当初始化请求完成后,我们调用 send()方法发送请求:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var data = new FormData();
data.append('name', 'Nicholas');
// 接受一个请求主体发送的数据,如果不需要,传入null
xhr.send(data);

当请求的类型为 get/head时,send()的参数会被忽略并置为null,send()传递的参数会影响到我们请求的头部 content-type的默认值,该字段代表返回的资源内容的类型,用于浏览器处理,如果没有设置或在一些场景下,浏览器会进行MIME嗅探来确定怎么处理返回的资源。

XHR2级中定义了 FormData数据,用于常见的类表单数据序列化:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 直接传入表单id
var data = new FormData(document.getElementById('user-form'));

// 创建类表单数据
var data = new FormData();
data.append('name', 'Nicholas');

// `FormData`可以直接被send()调用,会自动修改xhr的content-type头部
xhr.send(data);

// 请求头部的content-type: multipart/form-data; boundary=----WebKitFormBoundaryjn3q2KKRYrEH55Vz

// 请求的上传数据 Request Payload:
------WebKitFormBoundaryjn3q2KKRYrEH55Vz
Content-Disposition: form-data; name="name"

Nicholas
------WebKitFormBoundaryjn3q2KKRYrEH55Vz--

FormData常用的方法有

append/delete/entries/forEach/get/getAll/has/keys/set/values,都是常用的跟数组类似的方法,不再解释。

请求方法

GET是最常见的请求类型,可以将查询字符串参数添加到URL尾部,对XHR而言,该查询字符串必须经过正确编码,每个键值对必须使用 encodeURIComponent()进行编码,键值对之间由 &分割:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 封装序列化键值对
function addURLParam(url, name, value) {
    url += (url.indexOf('?') === -1 ? '?' : '&';
    url += encodeURIComponent(name) + '=' + encodeURIComponent(value);
    return url;
}

POST请求使用频率仅次于 GET请求,通常发送较多数据,且格式不限,数据传递给 send()作为参数。

HTTP一共规定了九种请求方法,每一个动词代表不同的语义,但是常用的只有上面两种:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
- OPTIONS:返回服务器针对特定资源所支持的HTTP请求方法。也可以利用向Web服务器发送'*'的请求来测试服务器的功能性。 
 - HEAD:向服务器索要与GET请求相一致的响应,只不过响应体将不会被返回。这一方法可以在不必传输整个响应内容的情况下,就可以获取包含在响应消息头中的元信息。 
 - GET:向特定的资源发出请求。 
 - POST:向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST请求可能会导致新的资源的创建和/或已有资源的修改。 
 - PUT:向指定资源位置上传其最新内容。 
 - DELETE:请求服务器删除Request-URI所标识的资源。 
 - TRACE:回显服务器收到的请求,主要用于测试或诊断。 
 - CONNECTHTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。
 - PATCH: 用于对资源进行部分修改

HTTP头部信息

每个HTTP请求和响应都带有头部信息,xhr对象允许我们操作部分头部信息。我们可以通过 xhr.setRequestHeader()方法来设置自定义的头部信息或者修改浏览器默认的正常头部信息。常用的请求头部:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 下面的实例是从我本地的一次请求取出的

Accept: 浏览器能够处理的内容类型。// */*
Accept-Charset: 浏览器能够显示的字符集。// 未取到
Accept-Encoding: 浏览器能够处理的压缩编码。// gzip,deflate
Accept-Language: 浏览器当前设置的语言。// zh-CN,zh;q=0.8,en;q=0.6
Connection: 浏览器与服务器之间连接的类型。// keep-alive
Cookie: 当前页面设置的任意Cookie。// JlogDataSource=jomodb
Host: 发出请求的页面所在域。// gzhxy-cdn-oss-06.gzhxy.baidu.com:8090
Referer: 发出请求的页面URI// http://gzhxy-cdn-oss-06.gzhxy.baidu.com:8090/jomocha/index.php?r=tools/offline/index
User-Agent: 浏览器的用户代理字符串。// Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36

我们一般不修改浏览器正常的头部信息,可能会影响到服务器响应。如果需要可以通过 xhr.setRequestHeader()进行修改:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 传入头部键值对,键值不区分大小写,如果多次设置,则追加
// 此时请求头部的content-type: application/json, text/html
xhr.setRequestHeader('content-type', 'application/json');
xhr.setRequestHeader('content-type', 'application/json');

设置头部信息需要在 open()之后, send()之前进行调用。响应的头部信息在后端处理,不在此处讲解。有一部分请求头部信息不允许设置,如 Accept-Encoding,Cookie等。

在请求返回后,我们可以获取到响应头部:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 获取指定项的响应头
xhr.getResponseHeader('content-type'); // application/json;charset=utf-8
// 获取所有的响应头部信息
xhr.getAllResponseHeaders();

这里简单说下content-type值,指的是请求和响应的HTTP内容类型,影响到服务器和浏览器对数据的处理方式,默认为 text/html,常用的如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 包含资源类型,字符编码, 边界字符串三个参数,可选填

text/html;charset=utf-8    // html标签文本
text/plain    // 纯文本
text/css    // css文件
text/javascript    // js文件
// 普通的表单数据,可以通过表单标签的enctype属性指定
application/x-www-form-urlencode
// 发送文件的POST包,包过大需要分片时使用`boundary`属性分割数据作边界
multipart/form-data; boundary=something
// json数据格式
application/json
// xml类型的标记语言
application/xml

XHR对象的响应

我们现在对请求的发起很了解了,接着看下如何拿到响应数据。如果我们给 open()传递的第三个参数是 true,则代表为同步请求,那么js会被阻塞直到拿到响应,而如果为 false则是异步请求,我们只需要绑定 xhr.onreadystatechange()事件监听响应即可。最上面的图已经说明了 readystate的值含义,所以我们可以:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// xhr v1 的写法,检测readystate的值,为4则说明数据准备完毕,需要在open()前定义
xhr.onreadystatechange = function () {
    if (xhr.readyState === 4{
        if (xhr.status === 200 || xhr.status === 304) {
            console.log(xhr.responseText);
        } else {
            console.log(xhr.statusText);
        }
    }   
}

// xhr v2 的写法,onload()事件说明数据准备完毕
xhr.onload = function () {
    if (xhr.status === 200 || xhr.status === 304) {
        console.log(xhr.responseText);
    } else {
        console.log(xhr.statusText);
    }
}

xhr对象的响应数据中包含几个属性:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
response    // 响应的数据
responseURL    // 发起响应的URL
responseType    // 响应的类型,用于浏览器强行重置响应数据的类型
responseText    // 如果为普通文本,则在这显示
responseXML    // 如果为xml类型文本,在这里显示

数据会出现在 responseText/responseXML中的哪一个,取决于服务器返回的 MIME类型,当然我们也有一些方式在浏览器端设置如何处理这些数据:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// xhr v1 的写法,设置响应资源的处理类型
xhr.overrideMimeType('text/xml');

// xhr v2 的写法, 可用值为 arraybuffer/blob/document/json/text
xhr.responseType = 'document';

响应数据相关的属性默认为 null/'',只有当请求完成并被正确解析的时候才会有值,取决于responseType的值,来确定 response/responseText/responseXML谁最终具有值。

XHR的高级功能

xhr v2里提供了超时和进度事件。

超时

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
xhr.timeout = 1000;    // 1分钟,单位为ms
xhr.ontimeout = function () {};

在请求 send()之后开始计时,等待 timeout时长后,如果没有收到响应,则触发 ontimeout()事件,超时会将 readystate=4,直接触发 onreadystatechange()事件。

请求进度

像上图所示, xhr v2定义了不同的进度事件: loadstart/progress/error/abort/load/loadend,这其中我们已经说过了 onload()事件为内容加载完成可用。现在说一下 onprogress()进度事件:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
xhr.onprogress = function (event) {
    if (event.lengthComputable) {
        console.log(event.loaded / event.total);
    }
}

该事件会接收一个 event对象,其 target属性为该xhr对象, lengthComputable属性为 total size是否已知,即是否可用进度信息, loaded属性为已经接收的字节数, total为总字节数。该事件会在数据接收期间不断触发,但间隔不确定。

跨域 CORS

提到 XHR对象,我们就会讲到跨域问题,它是为了预防某些恶意行为的安全策略,但有时候我们需要跨域来实现某些功能。需要注意的是跨域并不仅仅是前端单方面的事情,它需要后端代码进行配合,我们只是通过一些方式跳过了浏览器的阻拦。

对那些可能对服务器数据产生副作用的 HTTP 请求方法(特别是 GET 以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST 请求),浏览器必须首先使用 OPTIONS 方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。服务器确认允许之后,才发起实际的 HTTP 请求。

CORS(Cross-OriginResourceSharing,跨域资源共享)的思想是浏览器和服务端通过头部信息来进行沟通确认是否给予响应。如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Origin: http://www.baidu.com    // 浏览器的头部信息

// 如果服务端认可这个域名的跨域请求,如下设置就可跨域访问资源
Access-Control-Allow-Origin: http://www.baidu.com

如上就可以实现最简单的跨域访问,但是此时不能携带任何的 cookie,如果我们需要传递 cookie进行身份认证,需要设置:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
xhr.withCredentials = true;    // 浏览器端
Access-Control-Allow-Credentials: true;    // 服务端

这样我们就可以传递认证信息了,但如果允许认证, Access-Control-Allow-Origin不能设置为 *,而一定是具体的域名信息。

现在的浏览器都对CORS有了实现,如IE使用 XDomainRequest对象,其它浏览器使用 XMLHttpRequest对象。所以在此之前有很多奇技淫巧,如通过 jsonp/图像Ping方法都不再详述,而且其都需要服务端配合并且有很多局限性。

IE实现: XDomainRequest

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var xdr = new XDomainRequest();
xdr.open('get', 'http://www.site.com/page');
xdr.send(null);

XDR区别于XHR:

  • 不能传输cookie
  • 只能设置请求头部的content-type
  • 不能访问响应头部信息
  • 只支持get/post方法

通过这些区别可以阻止一部分的 CSRF(Cross-SiteRequestForgery,跨站点请求伪造)XSS(Cross-SiteScripting,跨站点脚本)

XDR与XHR非常相似,区别有几点:

  • open()方法只接受两个参数,请求类型和URL
  • 只允许异步请求
  • 响应完成触发onload()事件,但我们只能访问responseText原始文本,并且无法获取响应的status.
  • 异常事件都会触发error事件,并且无错误信息可用。

其余浏览器实现: XMLHttpRequest

其余浏览器通过XHR对象直接实现了CORS,你只需要做的就是 open()方法中传入一个绝对URL。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
xhr.open('get', 'http://www.site.com/page', true);

相对于普通的XHR对象,CORS-XHR依然有部分限制:

  • 不能使用setRequestHeader()定义头部
  • 不能传递cookie
  • 调用getAllResponseHeaders(),结果为空

其余跨域方法

上面的两种方法已经很成熟了,但是仍然有一部分方法可以跨域,比如 图像Ping

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var img = new Image();
img.onload = img.onerror = function () {
    console.log('done');
}
img.src = 'http://www.site.com/test?name=Nicholas';

这种方式常用于服务端统计广告的点击次数,其缺陷为:

  • 只能是GET请求
  • 单向通信,无法获取响应文本

另外还有 JSONP

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function handleResponse(response) {
    console.log(response.ip, response.city);
}

var script = document.createElement('script');
script.src = 'http://freegeoip.net/json?callback=handleResponse';
document.body.insertBefore(script, document.body.firstChild);

这种方式通过和服务器配合,跨域请求一个js文件并被服务器处理后传回:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
handleResponse({'name': 'Nicholas'});

然后直接在浏览器调用了该函数,传回的数据被当做response形参进行处理。但它也有一些缺陷:

  • 访问的方式是请求js,所以如果域名不安全,则很容易被恶意代码直接执行并攻击
  • 无法检测是否错误,因为js不支持这样的接口事件,只能超时判断

上面两种方式很容易看出,我们在支持CORS之前,使用的方法只不过是采用 img/css/js等不受跨域访问限制的对象,变相拿到了响应数据,但都有缺陷,所以如果没有历史包袱,建议采用XDR或XHR对象来实现跨域访问。

XHR的兼容性

我们可以直接到 CanIuse这个网站上查询兼容性问题:

参考资料

  1. MDN - 禁止修改的消息首部: https://developer.mozilla.org/zh-CN/docs/Glossary/%E7%A6%81%E6%AD%A2%E4%BF%AE%E6%94%B9%E7%9A%84%E6%B6%88%E6%81%AF%E9%A6%96%E9%83%A8
  2. 理解HTTP之Content-Type: http://homeway.me/2015/07/19/understand-http-about-content-type/
  3. MDN - Content-Type: https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Content-Type
  4. HTTP请求方法: https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Methods
  5. Javascript高级程序设计 第21章(Ajax与Comet)
  6. 你真的会使用XHR吗? https://segmentfault.com/a/1190000004322487
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-08-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码农沉思录 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
labuladong的算法小抄之js实现-第0章-学习算法和刷题的框架思维
文章直达地址: https://labuladong.gitbook.io/algo/di-ling-zhang-bi-du-xi-lie/xue-xi-shu-ju-jie-gou-he-suan-fa-de-gao-xiao-fang-fa
用户1974410
2022/09/20
3480
东哥手把手帮你刷通二叉树|第二期
上篇文章 手把手教你刷二叉树(第一篇) 连刷了三道二叉树题目,很多读者直呼内行。其实二叉树相关的算法真的不难,本文再来三道,手把手带你看看树的算法到底怎么做。
labuladong
2021/09/23
2600
数据结构和算法学习指南
这篇文章会涵盖之前的所有内容,并且会举很多代码的实例,谈谈如何使用框架思维,并且给对于算法无从下手的朋友给一点具体可执行的刷题建议。
五分钟学算法
2020/02/24
7340
【算法】499- 数据结构和算法学习指南
这是好久之前的一篇文章 学习数据结构的框架思维 的修订版。之前那篇文章收到广泛好评,没看过也没关系,这篇文章会涵盖之前的所有内容,并且会举很多代码的实例,谈谈如何使用框架思维,并且给对于算法无从下手的朋友给一点具体可执行的刷题建议。
pingan8787
2020/02/18
4610
[labuladong算法小抄]手把手带你刷二叉树(第一期)
我们公众号的成名之作 学习数据结构和算法的框架思维 中多次强调,先刷二叉树的题目,先刷二叉树的题目,先刷二叉树的题目,因为很多经典算法,以及我们前文讲过的所有回溯、动归、分治算法,其实都是树的问题,而树的问题就永远逃不开树的递归遍历框架这几行破代码:
唯一Chat
2021/02/25
1.1K0
[labuladong算法小抄]手把手带你刷二叉树(第一期)
LeetCode通关:连刷三十九道二叉树,刷疯了!
大家好,我是拿输出博客来督促自己刷题的老三,这一节我们来刷二叉树,二叉树相关题目在面试里非常高频,而且在力扣里数量很多,足足有几百道,不要慌,我们一步步来。我的文章很长,你们 收藏一下。
三分恶
2021/09/08
8800
通过前序+中序和后序+中序来构建二叉树
首先我们要知道,三种不同遍历方式的过程。看下图很容易理解,并且不容易忘。 前序遍历:根 左 右 中序遍历:左 根 右 后序遍历:左 右 根
帅地
2019/12/24
2.5K0
LeetCode重建二叉树详解[通俗易懂]
做完第一步之后,我们会发现,我们目前只具体确定了哪一个是根节点,哪些结点分别属于左右子树。但是由于树的递归特性。属于左子树的结点仍然符合前序遍历,中序遍历特点的。所以我们就是需要对刚刚分离出来的两部分分别再次用上述的方法,确定根节点,确定哪些结点属于左子树,哪些结点属于右子树。一次类推,直到结束。这就是这道题的大致思路。
全栈程序员站长
2022/07/01
2750
LeetCode重建二叉树详解[通俗易懂]
图解「剑指Offer」之用前序和中序遍历序列构建二叉树
题目中给出的是 前序 + 中序 的组合,那么我们仔细观察对比一下 前序遍历 与 中序遍历。
五分钟学算法
2019/08/30
3.8K0
图解「剑指Offer」之用前序和中序遍历序列构建二叉树
二叉树各种遍历真的很难?大sai带你拿捏!
很多时候我们需要使用非递归的方式实现二叉树的遍历,非递归枚举相比递归方式的难度要高出一些,效率一般会高一些,并且前中后序枚举的难度呈一个递增的形式,非递归方式的枚举有人停在非递归后序,有人停在非递归中序,有人停在非递归前序(这就有点拉胯了啊兄弟)。
bigsai
2021/10/08
7120
二叉树各种遍历真的很难?大sai带你拿捏!
东哥手把手带你套框架刷通二叉树|第一期
我们公众号的成名之作 学习数据结构和算法的框架思维 中多次强调,先刷二叉树的题目,先刷二叉树的题目,先刷二叉树的题目,因为很多经典算法,以及我们前文讲过的所有回溯、动归、分治算法,其实都是树的问题,而树的问题就永远逃不开树的递归遍历框架这几行破代码:
labuladong
2021/09/23
6150
我的刷题经验总结
两年前刚开这个公众号的时候,我写了一篇 学习数据结构和算法的框架思维,现在已经 5w 多阅读了,这对于一篇纯技术文来说是很牛逼的数据。
labuladong
2021/10/14
8270
六道入门树题目带你走入树结构的世界
需要修改一些条件,只需要在返回结果加上一个或者条件,tree1和tree2的左右子树分别比较即可
冷环渊
2022/04/06
2110
六道入门树题目带你走入树结构的世界
手把手刷二叉树系列完结篇
之前二叉树的文章,总有读者留言说看不出解法应该用前序中序还是后序,其实原因是你对前中后序的理解还不到位,这里我简单解释一下。
labuladong
2021/12/26
3900
手把手刷二叉树系列完结篇
leetcode刷题(97)——105. 从前序与中序遍历序列构造二叉树
通过上面的图观察规律,前序遍历第一个值肯定是根结点,中序遍历,根结点左边都是左子树,右边都是右子树
老马的编程之旅
2022/06/22
1950
leetcode刷题(97)——105. 从前序与中序遍历序列构造二叉树
剑指offer:重建二叉树
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
帅地
2019/03/11
2.3K0
【数据结构与算法】一起搞定面试中的二叉树题目(二)
作者:IOExceptioner 本文继续一起搞定面试中的二叉树(一)一文总结二叉树相关的面试题。 12. 二叉树的前序遍历 迭代解法 ArrayList<Integer> preOrder(TreeNode root){ Stack<TreeNode> stack = new Stack<TreeNode>(); ArrayList<Integer> list = new ArrayList<Integer>(); if(root == null){ return l
用户1634449
2018/10/18
5690
看完这篇文章,再也不怕面试官问我如何构造二叉树啦!
题目给出了一个中序遍历数组,由此我们可以通过已知根节点的值,然后遍历中序数组,找到根节点在中序数组中的索引位置 pos。
HelloWorld杰少
2022/08/04
1660
二叉树的详解与实现「建议收藏」
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/138337.html原文链接:https://javaforall.cn
全栈程序员站长
2022/09/03
3600
二叉树的详解与实现「建议收藏」
重建二叉树
该文讲述了如何通过递归的方法重建二叉树。首先,介绍了递归的基本思路,然后给出了具体的代码实现。通过一个例子来演示了如何通过递归的方法构建二叉树。
用户1148830
2018/01/03
6540
推荐阅读
相关推荐
labuladong的算法小抄之js实现-第0章-学习算法和刷题的框架思维
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验