0%

记录一种解题思路……

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
let str = "aaafdaewrebdafdasfdsafdsafb";
function getStrSum(str) {
/*
@思路
/1.首先遍历字符串每一数据
/2.把找到的数据放进对象中
/3.if判断是否有重复数据,如果有就+1,没有就等于1
/4.str.charAt(i) 返回当前下标对应的字符 列如let str=abc; str.charAt(1)获取的是b
/5.把保存对象遍历,进行比对,取最大的值出来打印
*/
let obj = {};
for (let i = 0; i < str.length; i++) {
if (obj[str.charAt(i)]) {
obj[str.charAt(i)]++;
} else {
obj[str.charAt(i)] = 1;
}
}
let sum = 0;
let number;
for (let key in obj) {
if (obj[key] > sum) {
sum = obj[key];
number = key;
}
}
console.log(number + "出现了=====" + sum + "次");
}
getStrSum(str);

WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。

WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

传统轮询(Traditional Polling)

当前Web应用中较常见的一种持续通信方式,通常采取 setInterval 或者 setTimeout 实现。例如如果我们想要定时获取并刷新页面上的数据,可以结合Ajax写出如下实现:

1
2
3
4
5
setInterval(function() {
$.get("/path/to/server", function(data, status) {
console.log(data);
});
}, 10000);

上面的程序会每隔10秒向服务器请求一次数据,并在数据到达后存储。这个实现方法通常可以满足简单的需求,然而同时也存在着很大的缺陷:在网络情况不稳定的情况下,服务器从接收请求、发送请求到客户端接收请求的总时间有可能超过10秒,而请求是以10秒间隔发送的,这样会导致接收的数据到达先后顺序与发送顺序不一致。于是出现了采用 setTimeout 的轮询方式:

1
2
3
4
5
6
7
8
9
function poll() {
setTimeout(function() {
$.get("/path/to/server", function(data, status) {
console.log(data);
// 发起下一次请求
poll();
});
}, 10000);
}

程序首先设置10秒后发起请求,当数据返回后再隔10秒发起第二次请求,以此类推。这样的话虽然无法保证两次请求之间的时间间隔为固定值,但是可以保证到达数据的顺序。

缺陷

程序在每次请求时都会新建一个HTTP请求,然而并不是每次都能返回所需的新数据。当同时发起的请求达到一定数目时,会对服务器造成较大负担。

长轮询(long poll)

客户端发送一个request后,服务器拿到这个连接,如果有消息,才返回response给客户端。没有消息,就一直不返回response。之后客户端再次发送request, 重复上次的动作。

总结

http协议的特点是服务器不能主动联系客户端,只能由客户端发起。它的被动性预示了在完成双向通信时需要不停的连接或连接一直打开,这就需要服务器快速的处理速度或高并发的能力,是非常消耗资源的。

什么是websocket?

WebSocket是HTML5的一个新协议,它允许服务端向客户端传递信息,实现浏览器和客户端双工通信

因为 HTTP 协议有一个缺陷:通信只能由客户端发起。 举例来说,我们想了解今天的天气,只能是客户端向服务器发出请求,服务器返回查询结果。HTTP 协议做不到服务器主动向客户端推送信息。这种单向请求的特点,注定了如果服务器有连续的状态变化,客户端要获知就非常麻烦。我们只能使用”轮询”:每隔一段时候,就发出一个询问,轮询的效率低,非常浪费资源(因为必须不停连接,或者 HTTP 连接始终打开)。因此,工程师们一直在思考,有没有更好的方法。WebSocket 就是这样发明的。

Websocket的特点:

服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。

  • 与 HTTP 协议有着良好的兼容性。默认端口也是 80 和 443 ,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
  • 建立在TCP协议基础之上,和http协议同属于应用层
  • 数据格式比较轻量,性能开销小,通信高效。
  • 可以发送文本,也可以发送二进制数据。
  • 没有同源限制,客户端可以与任意服务器通信
  • 协议标识符是ws(如果加密,则为wss),服务器网址就是 URL,如ws://localhost:8023

跨平台的WebSocket通信库socket.io

跨平台的WebSocket通信库,具有前后端一致的API,可以触发和响应自定义的事件。socket.io最核心的两个api就是emit 和 on了 ,服务端和客户端都有这两个api。通过 emit 和 on可以实现服务器与客户端之间的双向通信。

  • emit :发射一个事件,第一个参数为事件名,第二个参数为要发送的数据,第三个参数为回调函数(如需对方接受到信息后立即得到确认时,则需要用到回调函数)。
  • on :监听一个 emit 发射的事件,第一个参数为要监听的事件名,第二个参数为回调函数,用来接收对方发来的数据,该函数的第一个参数为接收的数据。

服务端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var app = require('express')();
var http = require('http');
var socketio = require("socket.io");
const server = http.createServer(app)
const io = socketio(server)
var count = 0;
// WebSocket 连接服务器
io.on('connection', (socket)=> {
//// 所有的事件触发响应都写在这里
setInterval(()=>{
count++
//向建立该连接的客户端发送消息
socket.emit('mynameEv', { name:"你我贷"+count})
},1000)
//监听客户端发送信息
socket.on('yournameEv', function (data) {
console.log(data)
})
})

app.get('/', function (req, res) {
res.sendfile(__dirname + '/index.html');
});
// 启用3000端口
server.listen(3000)

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<body>
<div id="myname"></div>
<script src="http://localhost:3000/socket.io/socket.io.js"/>
<script>
var count = 0;
const socket = io.connect('http://localhost:3000')
socket.on('mynameEv', (data)=>{
document.getElementById("myname").innerHTML = data.name;
console.log(data.name)
setInterval(()=>{
count++
socket.emit('yournameEv', { name:"飞旋"+count})
},1000)

})
</script>
</body>

对于搭建大型项目前后端分离是必须的,前后端分离的好处不用多说。前端工作一个重要的技能就是对接后台给的接口,从最初的Ajax,到目前比较火的fetch和axios,因为axios可以拦截请求和响应,转换请求数据和响应数据,并对响应回来的内容自动转换为json类型的数据等优势,个人比较喜欢使用axios。

ajax

Ajax是一种可以在浏览器和服务器之间使用异步数据传输(HTTP请求)的技术。使用它可以让页面请求少量的数据,而不用刷新整个页面。而传统的页面(不使用Ajax)要刷新部分内容,必须重载整个网页页面。它基于的是XMLHttpRequest(XHR)。这是一个比较粗糙的API,不符合关注分离的设计原则(Separation of Concerns),配置和使用都不是那么友好。

fetch

fetch是在ES6出现的,使用了ES6中的promise对象。Fetch是基于promise设计的。提供了一个 JavaScript接口,用于访问和操纵HTTP管道的部分,例如请求和响应。它还提供了一个全局fetch()方法,该方法提供了一种简单,合理的方式来跨网络异步获取资源。fetch不是ajax的进一步封装,而是原生js。

fetch不是ajax的进一步封装,而是原生js,没有使用XMLHttpRequest对象。

旧版本的浏览器不支持Promise,需要使用polyfill es6-promise。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
function handleResponse(res: Response): Promise<any> {
if (res.status === 200) {
return res.json()
} else {
return handleError(res)
}
}
function buildQueryString(params: {} | undefined) {
let qs = ''
if (params) {
qs = Object.keys(params)
.filter(k => params[k] !== undefined && params[k] !== null)
.map(k => `${encodeURIComponent(k)}=${encodeURIComponent(params[k])}`)
.join('&')
}

return qs === '' ? '' : `?${qs}`
}

private getWith(path: string, params: {} | undefined, handler): Promise<any> {
const token = ""
let url = `${path}${buildQueryString(params)}`
return fetch(url, {
method: 'GET',
headers: {
Authorization: `Bearer ${token}`
}
}).then(handler)
}

private get(path, params: {} | undefined): Promise<any> {
return this.getWith(path, params, handleResponse)
}

优点:

  • 符合关注分离,没有将输入、输出和用事件来跟踪的状态混杂在一个对象里
  • 更好更方便的写法
  • 更加底层,提供的API丰富(request, response)
  • 脱离了XHR,是ES规范里新的实现方式

缺点:

  • fetchtch只对网络请求报错,对400,500都当做成功的请求,需要封装去处理
  • fetch默认不会带cookie,需要添加配置项
  • fetch不支持abort,不支持超时控制,使用setTimeout及Promise.reject的实现的超时控制并不能阻止请求过程继续在后台运行,造成了量的浪费
  • fetch没有办法原生监测请求的进度,而XHR可以。

axios

axios是一个基于promise的HTTP库,支持promise的所有API。

axios也是对原生XHR的一种封装,不过是Promise实现版本,用于浏览器和node.js的http客户端

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
axios
.post(url, data, {
headers: {
Accept: "application/json",
"Content-Type": "application/json"
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});

优缺点:

  • 从 node.js 创建 http 请求。
  • 支持 Promise API。
  • 提供了一些并发请求的接口(重要,方便了很多的操作)。
  • 在浏览器中创建 XMLHttpRequests。
  • 在 node.js 则创建 http 请求。(自动性强)
  • 支持 Promise API。
  • 支持拦截请求和响应。
  • 转换请求和响应数据。
  • 取消请求。
  • 自动转换 JSON 数据。
  • 客户端支持防止CSRF。
  • 客户端支持防御 XSRF。

为什么要用axios?

axios 是一个基于Promise 用于浏览器和 nodejs 的 HTTP 客户端,它本身具有以下特征:

  • 从浏览器中创建 XMLHttpRequest
  • 从 node.js 发出 http 请求
  • 支持 Promise API
  • 拦截请求和响应
  • 转换请求和响应数据
  • 取消请求
  • 自动转换JSON数据
  • 客户端支持防止CSRF/XSRF(防止CSRF:就是让你的每个请求都带一个从cookie中拿到的key, 根据浏览器同源策略,假冒的网站是拿不到你cookie中得key的,这样,后台就可以轻松辨别出这个请求是否是用户在假冒网站上的误导输入,从而采取正确的策略。)
  • axios既提供了并发的封装,也没有fetch的各种问题,而且体积也较小,当之无愧现在最应该选用的请求的方式。

浅拷贝:复制变量的地址,所有地址对象都指向一个数据,而且所有地址对象都能修改这个数据。

深拷贝:复制变量的值。对于非基本类型的变量,可以通过递归至基本类型后,再复制。数组中可以使用slice和concat方法来进行深拷贝。

递归是什么? 如果一个函数在内部调用自身本身,这个函数就是递归函数。

1、使用递归的方式实现深拷贝

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//使用递归的方式实现数组、对象的深拷贝
function deepClone1(obj) {
//判断拷贝的要进行深拷贝的是数组还是对象,是数组的话进行数组拷贝,对象的话进行对象拷贝
var objClone = Array.isArray(obj) ? [] : {};
//进行深拷贝的不能为空,并且是对象或者是
if (obj && typeof obj === "object") {
for (key in obj) {
if (obj.hasOwnProperty(key)) {
if (obj[key] && typeof obj[key] === "object") {
objClone[key] = deepClone1(obj[key]);
} else {
objClone[key] = obj[key];
}
}
}
}
return objClone;
}

2、通过 JSON 对象实现深拷贝

1
2
3
4
5
6
//通过js的内置对象JSON来进行数组对象的深拷贝
function deepClone2(obj) {
var _obj = JSON.stringify(obj),
objClone = JSON.parse(_obj);
return objClone;
}

JSON对象实现深拷贝的一些问题,无法实现对对象中方法的深拷贝

3、通过jQuery的extend方法实现深拷贝

1
2
var array = [1,2,3,4];
var newArray = $.extend(true,[],array);12

4、Object.assign()拷贝

1
当对象中只有一级属性,没有二级属性的时候,此方法为深拷贝,但是对象中有对象的时候,此方法,在二级属性以后就是浅拷贝。1

5、lodash函数库实现深拷贝

1
lodash很热门的函数库,提供了 lodash.cloneDeep()实现深拷贝

最近开发的时候遇到了数组拷贝的问题,数组a是原数组,数组b是拷贝的数组,当操作数组b的时候,数组a也发生了变化, 然后想到了数组深拷贝和浅拷贝。

堆内存和栈内存?

JS中变量分基本数据类型和引用数据类型。

基本数据: String,Number,Boolean,Undefined,Null

引用数据类型:Object,Function,Array等。

栈(Stack)又名堆栈,它是一种运算受限的线性表。限定仅在表尾进行插入和删除操作的线性表。

如果是基础类型,那栈中存的是数据本身。

如果是对象类型,那栈中存的是堆中对象的引用。

栈内存的运行效率比堆内存高,栈内存的空间比堆内存小。

基本数据的值和地址都存在栈内存中。

引用类型的值存放在栈内存中,引用类型的地址存放在堆内存中。

深拷贝与浅拷贝

浅拷贝:复制变量的地址,所有地址对象都指向一个数据,而且所有地址对象都能修改这个数据。

深拷贝:复制变量的值。对于非基本类型的变量,则递归至基本类型变量后,再复制。数组中可以使用slice和concat方法来进行深拷贝。

1. DOM事件

DOM级别一共可以分为4个级别:DOM0级,DOM1级,DOM2级和DOM3级,
而DOM事件分为3个级别:DOM0级事件处理,DOM2级事件处理和DOM3级事件处理。

事件:指可以被 JavaScript 侦测到的行为。即鼠标点击、页面或图像载入、鼠标悬浮于页面的某个热点之上、在表单中选取输入框、确认表单、键盘按键等操作。事件通常与函数配合使用,当事件发生时函数才会执行。

事件名称:click/mouseover/blur(“不带on”)

事件处理程序(事件侦听器:**addEventListener**):响应某个事件的函数 。

事件处理程序函数名称:onclick/onmouseove/onblur

2. 事件流

事件流:指从页面中接收事件的顺序,也可理解为事件在页面中传播的顺序。

IE事件流是事件冒泡,Netscape的事件流是事件捕获。

“DOM2级事件”规定的事件流包括三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。首先发生的是事件捕获,为截获事件提供了机会。然后是实际的目标阶段接收到事件被触发。最后的阶段是冒泡阶段可以在这个阶段对事件做出响应。

3. 事件冒泡

冒泡: IE的事件流叫做事件冒泡(event bubbling),即事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点(文档)。即从下到上,从目标触发的元素逐级向上传播,直到window对象。

4. 阻止事件冒泡

  1. 给子级加 event.stopPropagation( )

  2. 在事件处理函数中返回 false

    但是这两种方式是有区别的。return false 不仅阻止了事件往上冒泡,而且阻止了事件本身(默认事件)。event.stopPropagation()则只阻止事件往上冒泡,不阻止事件本身。

  3. event.target==event.currentTarget,让触发事件的元素等于绑定事件的元素,也可以阻止事件冒泡;

  4. 阻止默认事件:event.preventDefault( )

5. 事件捕获

捕获: Netscape团队提出了另一种事件流叫做事件捕获。事件捕获的思想是不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件,事件捕获的用意在于在事件到达预定目标之前捕获它。

6. 阻止事件捕获

  1. 通过设置addEventListener的第三个参数可以决定事件是否在捕获阶段触发。

  2. stopPropagation()方法既可以阻止事件冒泡,也可以阻止事件捕获,也可以阻止处于目标阶段。

  3. 使用DOM3级新增事件stopImmediatePropagation()方法来阻止事件捕获,另外此方法还可以阻止事件冒泡。

1
2
3
4
5
6
7
element.addEventListener(event, function, useCapture)

event:必须。字符串,指定事件名。
注意: 不要使用 "on" 前缀。 例如,使用 "click" ,而不是使用 "onclick"。
function:当事件对象会作为第一个参数传入函数。 事件对象的类型取决于特定的事件。例如, "click" 事件属于 MouseEvent(鼠标事件) 对象。
useCapturetrue - 事件句柄在捕获阶段执行(即在事件捕获阶段调用处理函数)
false- false- 默认。事件句柄在冒泡阶段执行(即表示在事件冒泡的阶段调用事件处理函数)

typeof

  • 可以判断数据类型,它返回表示数据类型的字符串(返回结果只能包括number,boolean,string,function,object,undefined);
  • 可以使用typeof判断变量是否存在(如if(typeof a!=”undefined”){…});
  • Typeof 运算符的问题是无论引用的对象是什么类型 它都返回object
1
2
3
typeof {} // object
typeof [1,2] // object
typeof /\s/ //object

instanceof

原理 因为A instanceof B 可以判断A是不是B的实例,返回一个布尔值,由构造类型判断出数据类型

1
2
3
console.log(arr instanceof Array ); // true
console.log(date instanceof Date ); // true
console.log(fn instanceof Function ); // true

Object下的toString.call()方法

1
2
3
4
5
6
7
8
9
Object.prototype.toString.call();

console.log(toString.call(123)); //[object Number]
console.log(toString.call('123')); //[object String]
console.log(toString.call(undefined)); //[object Undefined]
console.log(toString.call(true)); //[object Boolean]
console.log(toString.call({})); //[object Object]
console.log(toString.call([])); //[object Array]
console.log(toString.call(function(){})); //[object Function]

根据对象的contructor判断:

1
2
3
4
console.log('数据类型判断' -  constructor);
console.log(arr.constructor === Array); //true
console.log(date.constructor === Date); //true
console.log(fn.constructor === Function); //true

对于前后端分离的项目,前后端的优化都是很重要的。后台优化服务器,排查SQL命令等。前后台优化都是相辅相成的,后台的响应速度快,前端的页面加载速度也要快速,用户体验才能更好。

​ 优化HTTP请求数,由于用户浏览的往往是局部网页,所以只加载用户可视范围内的资源,就会减少一些不必要的请求,也会减少浏览器加载资源的消耗。页面优化主要包括网络加载类、页面渲染类、CSS优化类、JavaScript执行类、缓存类、图片类、架构协议类等几类;

  1. 图片延迟加载

    延长加载也称懒加载。我们常用的是react-lazyload(Lazyload your Components, Images or anything matters the performance.) 来进行图片的预加载和延迟加载处理。前面有懒加载和预加载的文章。

  2. 定义图片大小限制

    加载的单张图片一般建议不超过30KB,避免大图片加载时间长而阻塞页面其他资源的下载,因此推荐10KB以内,如果用户上传的图片过大,建议设置告警系统,帮助我们观察了解整个网站的图片流量情况,做出进一步的改善。

  3. 预加载资源(图片和数据请求)

    但有一些需求是希望尽量少出现延迟加载带来的“等待”过程,这时就可以预加载资源。

  4. 资源合并

    可以把多个脚本合并到一个js文件内,然后统一引用它就能减少http请求。

    通用的CSS样式可以合并到一个文件里。

    通用的组件和方法可以提取出来。

  5. 引用优化

    引用css放在内,引用js放在结束标签前。

    css加载是异步的,更早的加载出样式就能更早呈现出页面。
    js放在尾部,防止浏览器加载js而阻塞页面,造成页面“白屏”现象。

  6. 单独域名存放资源

    如果有条件的话,我们还可以启用额外的服务器,域名来存放资源

    这样能减少主域名的HTTP请求数,让主服务器更快响应请求
    还能减少主域名的cookie请求

  7. 离线存储

    在移动端应用的比较多
    它和缓存不同,它设置好之后,离线也能访问,无论用户刷新或者新窗口,链接等等

  8. 本地存储localStorage

    本地存储数据一直是网页端的弱项,在没有HTML5的localStorage前,用cookie可以保存一点数据
    但付出的代价很大,cookie能保存的数据很少,并且它会伴随着每一次请求一起发送

    localStorage就好多了,默认5MB的大小,除非用户手动清除,否则一直不过期,就连IE8浏览器都支持

    这里要注意,localStorage和cookie一样受到跨域的限制
    可以使用domain控制

  9. CSS3替换js动画

    在js中,我们实现动画,就是利用定时器循环改变dom元素的属性来达到动画效果
    但是许多属性更改之后会造成浏览器重绘,增加性能消耗

    css3在动画效率上面有增强,浏览器会单独处理css3动画,不占用js主线程,还可以硬件加速

apply()、call()和bind()方法都是Function.prototype对象中的方法,而所有的函数都是Function的实例。这三个方法的用法非常相似,将函数绑定到上下文中,即用来改变函数中this的指向。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function Person(name){
this.name = name;
}

let obj = {name: 'lucy'};// 注意这是一个普通对象,它不是Person的实例

Person.prototype = {
constructor: Person,
showName: function(){
console.log(this.name);
}
}

let person = new Person('lily');
person.showName(); // lily

//使用apply
person.showName.apply(obj,[param1, param2, param3]); // lucy

//使用call
person.showName.call(obj,param1, param2, param3); // lucy

//使用bind
let bind = person.showName.bind(obj); // 返回一个函数
bind(); // lucy

apply和call都是直接执行函数调用。

bind是绑定,执行需要再次调用。

apply和call的区别是从第二个参数开始,apply接受数组作为参数,而call是接受逗号分隔的无限多个参数列表。

应用:

  1. 将伪数组转化为数组(含有length属性的对象,函数的参数的类数组对象arguments)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    //含有length属性的对象
    let obj = {
    0: "1ily",
    1: "women",
    2: 20,
    length: 3 // 一定要有length属性
    };
    console.log(Array.prototype.slice.call(obj)); // ["1ily", "women", 20]

    //函数的参数的类数组对象arguments
    function fun() {
    return Array.prototype.slice.call(arguments);
    }
    console.log(fun(1,2,3,4,5)); // [1, 2, 3, 4, 5]
  2. 利用call和apply做继承

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    function Animal(name){      
    this.name = name;
    this.showName = function(){
    console.log(this.name);
    }
    }

    function Cat(name){
    Animal.call(this, name);
    }

    // Animal.call(this) 的意思就是使用this对象代替Animal对象,那么
    // Cat中不就有Animal的所有属性和方法了吗,Cat对象就能够直接调用Animal的方法以及属性了
    var cat = new Cat("1ily");
    cat.showName(); //1ily
  3. 求数组中的最大值和最小值

    1
    2
    3
    let arr = [1,2,3,89,46]
    let max = Math.max.apply(null,arr)//89
    let min = Math.min.apply(null,arr)//1
  4. 数组的追加

    1
    2
    3
    4
    5
    let arr1 = [1,2,3]
    let arr2 = [8,9,10]
    let total = [].push.apply(arr1, arr2) //6
    // arr1 [1, 2, 3, 8,9,10]
    // arr2 [8,9,10]
  5. 总结

    三者都可以改变函数的this对象指向。

    三者第一个参数都是this要指向的对象,如果如果没有这个参数,默认指向全局window。

    三者都可以传参,但是apply是数组,而call是有顺序的传入。

    bind 是返回对应函数,便于稍后调用。apply 、call 则是立即执行 。

为什么form表单提交没有跨域问题,但ajax提交有跨域问题?

浏览器的同源策略的本质:一个域名的 JS ,在未经允许的情况下,不得读取另一个域名的内容。但浏览器并不阻止你向另一个域名发送请求。。

form 提交(submit函数)之后,是不会有任何数据返回的。没机会读任何东西,所以可以认为是无害的,不在同源策略之内。

而 AJAX 是可以读取响应内容的,因此浏览器的同源策略不允许这样的行为。

所以同源策略会限制 Ajax读取响应内容 不会限制 Form发送请求。