本文讲AJAX相关的知识全部讲解了一遍.要想入门,选择这篇文章完全够用

本文的知识图谱:

AJAX的用处:

​ 在没有AJAX之前,每次从服务端获取数据都要刷新页面(也就是同步请求),这十分的麻烦。比如你要在一个网页上的实时聊天框中聊天,要不停地刷新界面来获取消息。这样的行为也产生了大量无用,重复的数据挤占宽带。

​ 因此AJAX应运而生,它可以在不刷新页面的情况下让页面的一部分得到服务器的数据,让页面产生变化。而做到这一切就使用了XMLHttpRequest组件,通过这个组件与服务器保持异步通信和联系.

小概念

同步和异步的概念

  • 同步:必须等待前面的任务完成,才能继续后面的任务。
  • 异步:不受当前任务的影响。

拿排队举例:

  • 同步:在银行排队时,只有等到你了,才能够去处理业务。
  • 异步:在排队的时候,可以玩手机。

AJAX所使用的数据

XML

xml一般指可扩展标记语言标准通用标记语言的子集,简称XML。是一种用于标记电子文件使其具有结构性的标记语言

XML文档的三个部分

XML声明

<?xml version="1.0" encoding="utf-8"?>

XML必须有version值,致命XML的版本号,同时定义文档的字符集.

处理指令

处理指令,简称PI (processing instruction)。处理指令用来指挥解析引擎如何解析XML文档内容。

语法

<?target instructions?>

其中:

  • target - 标识指令指向哪个应用程序。
  • instruction - 字符,描述了应用程序要处理的信息。

处理指令以特殊的 结尾。处理的内容在遇到字符串 ?> 时立即结束。

例如,在XML文档中可以使用xml-stylesheet指令,通知XML解析引擎,应用css文件显示xml文档内容。**

<?xml-stylesheet type="text/css" href="1.css"?>

XML结构数据

<?xml version="1.0"?> --XML声明
<contact-info>
<name>Tanmay Patil</name>
<company>TutorialsPoint</company>
<phone>(011) 123-4567</phone>
</contact-info>

XML结构数据中要有一个根<contact-info>和在其中的各种元素 <name>.所有的元素都必须被合并.

<name></name>

XML使用流程

XML DOM对象创建

要想处理XML就要创建一个XML DOM对象.

对于IE8以及早期的版本对于XML的支持通过基于ActiveX的MSXML组件实现.

var xmlDom=new ActiveXObject("Microsoft.XmlDom");

现在的浏览器使用document对象中的Implementation属性,使用该对象的createDocument()方法创建XML DOM对象.

createDocument("命名空间的URL字符串","包含文档根元素名称的字符串","创建文档类型")

第三个现在总为null

示例:

 document.implementation.createDocument("","",null); //创建一个空的XML DOM文档对象
document.implementation.createDocument("http://www.baidu.com","root",null); //第一个参数是命名空间,第二个是文档的根节点名称

加载XML数据

加载数据有两种方法load(),loadXML()

loadXML()

这种方法能将字符串转换为XML DOM对象.

var o=document.implementation.createDocument("","",null); //必须先创建一个XML DOM对象之后才能加载
var s="<res><record>1</record><rename>苹果</rename></res>"//一个XML字符串
o.loadXML(s);//加载XML字符串

load()方法能够加载XML文件

var o=document.implementation.createDocument("","",null); //必须先创建一个XML DOM对象
o.load("test.xml");

同步模式下,XML文件被完全加载之后才能够执行其他操作;而异步加载时,用户不用等待,可以执行其他操作

设置加载模式

load()方法默认加载模式为异步加载,也可以使用async属性来设置加载模式.

var o=document.implementation.createDocument("","",null);
var s="<res><record>1</record><rename>苹果</rename></res>"//一个XML字符串
o.loadXML(s);//加载XML字符串
o.async=true;//异步
o.async=false;//同步`

XML DOM 对象使用readyState属性跟踪加载进程,readyState属性的取值有5个.

0 尚未初始化

1 正在加载

2 完成加载

3 已经可用但是部分不可用

4 完全可用

为此XML DOM对象有一个叫做onreadystatechange属性,当每次readyState属性发生变化之后,就会触发readyState事件.

xmlhttp.onreadystatechange=function()//当readyState属性发生变化,就触发readyState
{
if (xmlhttp.readyState==4 && xmlhttp.status==200)//这个之后会讲,
{
document.getElementById("myDiv").innerHTML=xmlhttp.responseText;
}
}

显示XML数据

XML严格执行DOM2标准,可以使用documenElement属性获取根元素,使用childNodes,firstNodes等都可以遍历.(这个自己百度一下吧,就是用来访问节点的属性.功能很全,太多了)

function text(o) {
var s="";
for (var i=0;i<o.childNodes.length;i++){//遍历子节点
if(o.childNodes[i].hasChildNodes()) {//如果当前节点在子节点
a += text(o.childNodes[i]);//则递归读取子节点包含的内容
}else{
s+=o.childNodes[i].nodeValue;
}
}
return s;//返回节点内容
}

还有一种方式就是可以使用XMLSerializer对象来实现

  function xml(o) {//传入节点
var _o=new XMLSerializer();//创建XMLSerializer对象
return -o.serializeToString(o);//将节点的内容变成字符串
}

好了这就是XML的全部内容

大体流程是这样的:

下面来看JSON的内容

JSON的内容比较简单

因为本身来讲JSON就是一种远比XML更加灵活的语言,

JSON](https://baike.baidu.com/item/JSON)(JavaScript Object Notation, JS 对象简谱) 是一种轻量级的数据交换格式。它基于 ECMAScript (欧洲计算机协会制定的js规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。

下面是一个比较我就可以直观的看到区别

JSON和XML的区别

来自烽火戏诸侯的代码

<?xml version="1.0" encoding="utf-8" ?>
<country>
<name>中国</name>
<province>
<name>黑龙江</name>
<citys>
<city>哈尔滨</city>
<city>大庆</city>
</citys>   
</province>
<province>
<name>广东</name>
<citys>
<city>广州</city>
<city>深圳</city>
<city>珠海</city>
</citys>   
</province>
<province>
<name>台湾</name>
<citys>
 <city>台北</city>
 <city>高雄</city>
</citys> 
</province>
<province>
<name>新疆</name>
<citys>
<city>乌鲁木齐</city>
</citys>
</province>
</country>

JSON

var country =
{
name: "中国",
provinces: [
{ name: "黑龙江", citys: { city: ["哈尔滨", "大庆"]} },
{ name: "广东", citys: { city: ["广州", "深圳", "珠海"]} },
{ name: "台湾", citys: { city: ["台北", "高雄"]} },
{ name: "新疆", citys: { city: ["乌鲁木齐"]} }
]
}

这么一看就可以十分直观的看出来,两者的区别,在代码量上面XML是JSON的数倍之多.

结构也更加的复杂.

但是我么也要看到,XML有很强的可读性,你几乎不用非任何的力气就了解他说表达的内容.

这两点就是XML和JSON的差别

JSON的结构

JSON的结构由下面的三种类型的数据组成

值:字符串,数组,布尔值,对象,null,数值
对象:一组无序键值对
数组:一组有序的值列表,可以通过数组访问其中的值.

值:

1 -值
"helo ,world" -字符串
true,false -布尔值

对象

{
"name":"aaw4c", --两者(值,键)必须都是字符串类型
"date":"2020",
"book":{ --json允许嵌套
"name":[{"lang":"cn","XPATH语言基础"}] --数组
}
}

数组

["value1","value2","value3"....]

和JavaScript一样这是数组直接量形式

[1,true,"false"]

可以把不同类型的值放在一个数组中

解析JSON

ECMAScript5提供一个全局的JSON对象,用来序列化JSON

JSON.parse()能够把一个json转换成一个ECMAScript的值(对象或数组),

JSON.parse(text [,reviver)

JSON的格式一定要标准,key和value一定要用双引号包括,否则会出现解析异常

var json='{"name":"张三","qq":"11111"}';
var contact=Json.parse(json);
document.write(contact.name+","+contact.qq);

reviver是一个可选参数,他表示的是一个转换函数,将传入对象的每一个成员调用这个参数.

如果包含嵌套对象,则先于父对象转换嵌套对象

  • 如果reviver函数返回一个有效值则成员值替换为转换后的值
  • 如果reviver函数返回一个相同值则不变
  • 如果reviver函数返回一个null或undefined则删除成员
  var a='{"a":"12","b":"13"}';

    var contact=JSON.parse(a,int);

    function int() { //reviver函数
if(typeof value=='string'){
return parseInt(value);
}else {
return value;
}
}
document.write(contact.b);//返回13

使用AJAX

在这里我们才终于讲到了AJAX

但是还要降一点HTTP的知识,这样才能更好的理解.

这是本文的HTTP知识图表.

请求

HTTP的请求信息由三部分组成:请求行,消息报头,请求正文(可选)

<request-line>
<headers>
<blank line>
[<request-body>]

请求行<request-line>以一个方法符号开头,以空格分隔,后面跟着请求的URI和协议的版本

Method Request-URI HTTP-Version CRLF

Method表示请求方法一般用大写形式显示如POST,GET.(HTTP定义了其他的,但是我们一般就用这俩)

Request-URI表示统一资源符

HTTP-Version表示请求的HTTP协议版本

CRLF表示回车换行

请求行之后是消息报头部分消息报头后是一个空行,然后才是请求正文

GET / HTTP/1.1 --Method Request-URI HTTP-Version (对号入座)
HOST:www.baidu.com --第一个消息报头,HOST头部指出的是请求的域名,结合上面的Request-URI,就可以的定位具体地址
User-Agent:Mozilla/5.0 --由浏览器进行定义
Connection: Keep-Alive-- 通常都设置为这个
--这一行必须留空格
POST / HTTP/ (CRLF) -post请求和get略有区别,首先从请求行开始处的get变成post
HOST:www.baidu.com
User-Agent:Mozilla/5.0 --由浏览器进行定义
Content-Type:application/x-www-form-urlencoded --需要增加的内容,说明请求主题内容是如何编码
Content-Length:348 --必须增加的内容,请求的内容长度
Connection: Keep-Alive

响应

HTTP响应也是由三个部分组成:状态行,消息报头,响应正文

<status-line>
<headers>
<blank line>

状态行<status-line>格式如下

HTTP-Version Status-Code Reason-Phrase CRLF
HTTP-Version --表示服务器的HTTP协议的版本
Status-Code --表示服务器发回的响应状态代码
Reason-Phrase --表示状态代码的文本描述

状态代码:

1. 信息提示 - 1xx
2. 成功 - 2xx
3. 重定向 - 3xx
4. 客户端错误 - 4xx
5. 服务器错误 - 5xx

可以到以下链接中去具体了解.

状态代码

响应示例:

HTTP/1.1 200 OK
Date: Thu, 16 Aug 2018 03:10:03 GMT
Server: Apache/2.4.18 (Win32) OpenSSL/1.0.2e mod_fcgid/2.3.9
X-Powered-By: PHP/5.5.30
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Set-Cookie: XDEBUG_SESSION=netbeans-xdebug; expires=Thu, 16-Aug-2018 04:10:03 GMT; Max-Age=3600; path=/
Set-Cookie: _mcloudauth=xZ4y7BzG%2Bh3n4DAX1oy5wXUMZ0616h1WuywKO8Nf%2FcqI%3D59ec05a6d0b741340615114f8ea2a352; path=/; httponly
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: text/html; charset=UTF-8 35
{"code":0,"data":["mobile","13928892857"],"msg":"ok"}
0

AJAX定义XMLHttpRequest对象

这其实才是AJAX的核心内容

XMLHttpRequest对象有各种属性和方法

属性

readyState --HTTP 请求的状态
responseText --目前为止为服务器接收到的响应体(不包括头部),或者如果还没有接收到数据的话,就是空字符串。
responseXML --对请求的响应,解析为 XML 并作为 Document 对象返回。
status --由服务器返回的 HTTP 状态代码,
statusText --这个属性用名称而不是数字指定了请求的 HTTP 的状态代码。

使用XMLHttpRequest对象实现异步通信一般需要下面几个步骤

  1. 定义XMLHttpRequest实例对象

  2. 调用XMLHttpRequest对象的open方法打开服务器端URL地址

  3. 注册onreadystatechange事件处理函数,准备接受响应数据,并进行处理

  4. 调用XMLHttpRequest对象的send()方法发送请求

1.定义XMLHttpRequest实力对象

if (window.XMLHttpRequest)
{// code for all new browsers
xmlhttp=new XMLHttpRequest();//就这么定义实例对象
}
else if (window.ActiveXObject)
{// code for IE5 and IE6
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
//可以适配所有的浏览器

2.调用XMLHttpRequest对象的open方法打开服务器端URL地址

创建XMLHttpRequest对象之后,就可以使用该对象的open()方法建立一个HTTP请求.

open()方法如下

xmlhttp.open(bstrMethod,bstrUrl,varAsync,bstrUser,bstrPassword);
/*
bstrMethod:HTTP方法字符串,不分大小写.例如GET,POST
bstrUrl:请求的URL地址字符串,可以为绝对地址或相对地址
varAsync:布尔值,可选参数,请求指定是否为异步.
bstrUser:可选参数,要是需要验证,从这里输入用户名
bstrPassword:可选参数,要是需要验证,从这里输入密码
*/

一个小案例:

xmlhttp.open("GET",url,true);//只有前三个

3.注册onreadystatechange事件处理函数,准备接受响应数据,并进行处理

XMLHttpRequest对象的responseBody等.

4.调用XMLHttpRequest对象的send()方法发送请求

xmlhttp.send(box);
//box表示将要通过该请求发送的数据,如果不传递信息则使用null
//这个方法中的同步方法取决于open()方法中的varAsync参数,如果为false,此方法会等待请求完成后返回,如果为true则立刻返回.

这就是AJAX的主要步骤

发送GET请求

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<h1>Ajax 发送 get 请求</h1>
<input type="button" value="发送get_ajax请求" id='btnAjax'> <script type="text/javascript">
// 绑定点击事件
document.querySelector('#btnAjax').onclick = function () {
// 发送ajax 请求 需要 五步 // (1)创建异步对象
var ajaxObj = new XMLHttpRequest(); // (2)设置请求的参数。包括:请求的方法、请求的url。
ajaxObj.open('get', '02-ajax.php'); // (3)发送请求
ajaxObj.send(); //(4)注册事件。 onreadystatechange事件,状态改变时就会调用。
//如果要在数据完整请求回来的时候才调用,我们需要手动写一些判断的逻辑。
ajaxObj.onreadystatechange = function () {
// 为了保证 数据 完整返回,我们一般会判断 两个值
if (ajaxObj.readyState == 4 && ajaxObj.status == 200) {
// 如果能够进到这个判断 说明 数据 完美的回来了,并且请求的页面是存在的
// 5.在注册的事件中 获取 返回的 内容 并修改页面的显示
console.log('数据返回成功'); // 数据是保存在 异步对象的 属性中
console.log(ajaxObj.responseText); // 修改页面的显示
document.querySelector('h1').innerHTML = ajaxObj.responseText;
}
}
}
</script>
</body>
</html>

发送POST请求

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<h1>Ajax 发送 get 请求</h1>
<input type="button" value="发送put_ajax请求" id='btnAjax'>
<script type="text/javascript"> // 异步对象
var xhr = new XMLHttpRequest(); // 设置属性
xhr.open('post', '02.post.php'); // 如果想要使用post提交数据,必须添加此行
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); // 将数据通过send方法传递
xhr.send('name=fox&age=18'); // 发送并接受返回值
xhr.onreadystatechange = function () {
// 这步为判断服务器是否正确响应
if (xhr.readyState == 4 && xhr.status == 200) {
alert(xhr.responseText);
}
};
</script>
</body>
</html>

跟踪状态

XMLHttpRequest对象通过readyState属性实时跟踪异步交互状态

注册 onreadystatechange 事件后,每当 readyState 属性改变时,就会调用 onreadystatechange 函数。

readyState:(存有 XMLHttpRequest 的状态。从 0 到 4 发生变化)

0: 未初始化

1: 初始化,但是尚未调用send()

2: 发送数据,表示send()已经调用

3: 数据传输中,已经接收到部分数据

4: 请求已完成,且响应已就绪

如果readyState属性为4,则说明响应完毕,那么就可以安全的读取返回的数据.

在XMLHttpRequest对象中可以借助status属性获取HTTP当前的状态码.如果readyState属性为4且status为200则HTTP响应顺利完成.

 if (ajaxObj.readyState == 4 && ajaxObj.status == 200)

如下两个则是两种实例:

获取XML数据

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<input type="button" value="获取XMl数据" id='getXML'>
</body>
</html>
<script type="text/javascript">
document.querySelector('#getXML').onclick = function () {
var ajax = new XMLHttpRequest(); ajax.open('get','get_XMl.php'); ajax.send(); ajax.onreadystatechange = function () {
if (ajax.readyState == 4 && ajax.status==200) {
// 如果 返回的是 xml文件
console.log(ajax.responseText); // 异步 对象中 有另外一个属性 用来专门获取 xml
// xml对象 在浏览器段 就是一个 document对象
// 解析时 可以直接使用 querySelector 或者 getElementById等等 document对象 有的语法
console.log(ajax.responseXML);
console.log(ajax.responseXML.querySelector('kuzi').innerHTML);
// 下面这个 页面文档对象 如果要获取某个标签
console.log(window.document); }
}
}
</script>

获取JSON数据

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<h1>获取 json 数据</h1>
<input type="button" value="获取json" id='btnJson'>
</body>
</html>
<script type="text/javascript">
// 获取的是一个 如果要获取多个
// document.querySelectorAll(selector)
document.querySelector("#btnJson").onclick = function () {
var ajax = new XMLHttpRequest(); ajax.open('get','myJson.php'); ajax.send(); ajax.onreadystatechange = function () {
if (ajax.readyState==4&&ajax.status==200) {
// json 字符串 是字符串 所以我们可以 通过 responseText获取
console.log(ajax.responseText); // 转化为 js对象
var jsObj = JSON.parse(ajax.responseText); console.log(jsObj); // 拼接ul s
var str = ''; str+='<ul>';
str+='<li>'+jsObj.name+'</li>';
str+='<li>'+jsObj.skill+'</li>';
str+='<li>'+jsObj.friend+'</li>';
str+='</ul>'; // 设置到界面上 document.body.innerHTML = str;
}
}
}
</script>

以上内容就是AJAX的内容了.

下面时AJAX的导图,大家可以照着练习一下,看看自己是否记住了内容

AJAX完全解读的更多相关文章

  1. 详细解读Jquery各Ajax函数:$.get(),$.post(),$.ajax(),$.getJSON()

    一,$.get(url,[data],[callback]) 说明:url为请求地址,data为请求数据的列表(是可选的,也可以将要传的参数写在url里面),callback为请求成功后的回调函数,该 ...

  2. 详细解读Jquery的$.get(),$.post(),$.ajax(),$.getJSON()用法

    一,$.get(url,[data],[callback]) 说明:url为请求地址,data为请求数据的列表,callback为请求成功后的回调函数,该函数接受两个参数,第一个为服务器返回的数据,第 ...

  3. 详细解读Jquery各Ajax函数

    $.get(),$.post(),$.ajax(),$.getJSON() 一,$.get(url,[data],[callback]) 说明:url为请求地址,data为请求数据的列表,callba ...

  4. 详细解读Jquery各Ajax函数:$.get(),$.post(),$.ajax(),$.getJSON() —(转)

    一,$.get(url,[data],[callback]) 说明:url为请求地址,data为请求数据的列表(是可选的,也可以将要传的参数写在url里面),callback为请求成功后的回调函数,该 ...

  5. 关于jQuery的ajax的源码的dataType解读

    $.ajax其实底层还是用的XMLHttpRequest,对于加载数据的格式datatype有:xml.text.html.json.jsonp.script. 其中xml.text不需要处理,直接使 ...

  6. 详细解读Jquery各Ajax函数:$.get(),$.post(),$.ajax(),$.getJSON()【转】【补】

    一,$.get(url,[data],[callback]) 说明:url为请求地址,data为请求数据的列表(是可选的,也可以将要传的参数写在url里面),callback为请求成功后的回调函数,该 ...

  7. zeptojs库解读3之ajax模块

    对于ajax,三步骤,第一,创建xhr对象:第二,发送请求:第三,处理响应. 但在编写过程中,实际中会碰到以下问题, 1.超时 2.跨域 3.后退 解决方法: 1.超时 设置定时器,规定的时间内未返回 ...

  8. 2014年辛星jquery解读第三节 Ajax

    ***************Ajax********************* 1.Ajax是Asynchronous Javascript And  XML的简写,它指的是异步Javascript ...

  9. Jquery的$.get(),$.post(),$.ajax(),$.getJSON()用法详细解读

    1.$.get $.get()方法使用GET方式来进行异步请求,它的语法结构为: $.get( url [, data] [, callback] ) 解释一下这个函数的各个参数: url:strin ...

随机推荐

  1. C++走向远洋——57(项目二2、动物这样叫、抽象类)

    */ * Copyright (c) 2016,烟台大学计算机与控制工程学院 * All rights reserved. * 文件名:text.cpp * 作者:常轩 * 微信公众号:Worldhe ...

  2. 这几个IDEA高级调试技巧,用完就是香

    一个项目启动两次 测试分布式项目时,经常要一个项目启动2次,不用将一个项目打开多次启动,配置一下即可 1.点击Edit Configurations 2.勾选Allow parallel run 3. ...

  3. go微服务框架kratos学习笔记十(熔断器)

    目录 go微服务框架kratos学习笔记十(熔断器) 什么是熔断 熔断器逻辑 kratos Breaker kratos 熔断逻辑 kratos熔断器使用说明 bladmaster client br ...

  4. JS逆向某网站登录密码分析

    声明: 本文仅供研究学习使用,请勿用于非法用途! 目标网站 aHR0cHM6Ly9hdXRoLmFsaXBheS5jb20vbG9naW4vaW5kZXguaHRt 今日目标网站是某知名支付网站,感觉 ...

  5. .Net vs .Net Core,我改如何选择?看这一篇文章就够了

    前言 .Net目前支持构建服务器端应用程序的两种实现主要有两种,.NET Framework和.NET Core.两者共享许多相同的组件,并且您可以在两者之间共享代码.但是,两者之间存在根本差异,在我 ...

  6. 论JS函数传参时:值传递与引用传递的区别

    什么是值传递:值传递是指在调用函数时将实际参数(实参)复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数. 值传递的总结:也就是说,将实参复制到函数中的这个过程叫值传递 什么是 ...

  7. iview中遇到table的坑(已经修改了table的数据,但是界面没有更新)

    https://blog.csdn.net/bigdargon/article/details/89381466 https://blog.csdn.net/qiuyan_f/article/deta ...

  8. 树莓派上搭建唤醒词检测引擎 Snowboy

    Snowboy 是一款高度可定制的唤醒词检测引擎,可以用于实时嵌入式系统,并且始终监听(即使离线).当前,它可以运行在 Raspberry Pi.(Ubuntu)Linux 和 Mac OS X 系统 ...

  9. 手把手教你用Abp vnext构建API接口服务

    ABP是一个开源应用程序框架,该项目是ASP.NET Boilerplate Web应用程序框架的下一代,专注于基于ASP.NET Core的Web应用程序开发,也支持开发控制台应用程序. 官方网站: ...

  10. css中:overflow:hidden清除浮动的原理

    要想彻底清除浮动的影响,适合的属性不是 clear 而是 overflow. 一般使用 overflow:hidden,利用 BFC 的“结界”特性彻底解决浮动对外部或兄弟元素的影响. 1. 前言: ...