一、h5语义化标签
title:
简短、描述性、唯一(提升搜索引擎排名)
hn:
h1~h6分级标题,用于创建页面信息的层级关系。对于搜索引擎而言,如果标题与搜索词匹配,这些标题就会被赋予很高的权重,尤其是h1。
header:
页眉通常包括网站标志、主导航、全站链接以及搜索框,也适合对页面内部一组介绍性或导航性内容进行标记。
nav:
标记导航,仅对文档中重要的链接群使用。html5规范不推荐对辅助性页脚链接使用nav,除非页脚再次显示*全局导航、或者包含招聘信息等重要链接。
<nav>
<ul>
<li>HTML 5</li>
<li>CSS3</li>
<li>JavaScript</li>
</ul>
</nav>
main:
页面主要内容,一个页面只能使用一次。如果是web应用,则包围其主要功能。
article:
包含像报纸一样的内容= =||是这么理解的,表示文档、页面、应用或一个独立的容器。article可以嵌套article,只要里面的article与外面的是部分与整体的关系。
section:
具有相似主题的一组内容,比如网站的主页可以分成介绍、新闻条目、联系信息等条块。
aside:
指定附注栏,包括引述、侧栏、指向文章的一组链接、广告、友情链接、相关产品列表等。如果放在main内,应该与所在内容密切相关。
footer:
页脚,只有当父级是body时,才是整个页面的页脚。
<footer>
COPYRIGHT@小北
</footer>
small:
指定细则,输入免责声明、注解、署名、版权。
二、三栏布局,左右顶宽高,中间自适应
1.float布局
兼容性好,但需要清除浮动
bfc
#css样式
*{
padding: 0;
margin: 0;
}
.wrapper{
width: 100%;
height: 300px;
}
.right{
float: right;
width: 300px;
height: 300px;
background-color: red;
}
.left{
float: left;
width: 300px;
height: 300px;
background-color: gray;
}
.center{
background-color: green;
height: 300px;
}
#html布局
<div class="wrapper">
<div class="left"></div>
<div class="right"></div>
<div class="center">
<h1>float布局</h1>
1.三栏布局的中间
2.三栏布局的中间
</div>
</div>
优点:
实现简单,做好清除浮动即可;兼容性比较好;网页缩放时能够正常布局
缺点:
浮动元素脱离文档流;如果没有做好清除浮动,父元素容易出现高度塌陷;无法优先加载重要内容
2.绝对定位布局
快捷,但有效性不行
# css样式
.wrapper{
position: relative;
width: 100%;
height: 300px;
}
.right{
position: absolute;
right: 0;
width: 300px;
height: 300px;
background-color: red;
}
.left{
position: absolute;
left: 0;
width: 300px;
height: 300px;
background-color: gray;
}
.center{
position: absolute;
right: 300px;
left: 300px;
background-color: green;
height: 300px;
}
#html:
<div class="wrapper">
<div class="left"></div>
<div class="center">
<h1>float布局</h1>
1.三栏布局的中间
2.三栏布局的中间
</div>
<div class="right"></div>
</div>
优点:
绝对定位布局简单方便,不容易出问题;能够优先加载重要内容;网页缩放时能保持正常布局。
缺点:
只适用固定高度的布局。绝对定位元素脱离文档流,父元素会出现高度塌陷,只能通过给父元素设置固定高度才能解决这一问题。
3.flex布局
比较完美的
.wrapper{
display: flex;
}
.right{
flex: 0 0 300px;
height: 300px;
background-color: blue;
}
.left{
flex: 0 0 300px;
height: 300px;
background-color: red;
}
.center{
flex: 1;
height: 300px;
background-color: yellow;
}
优点:
绝对定位和相对定位都会引起元素脱离文档流,flex布局能够避免这个问题。flex布局实现简单,可以实现各种布局。flex是一个比较完美的方案。目前移动端的布局也都是用flexbox。 flex布局是未来的趋势。
缺点:
不能兼容IE9及以下浏览器。
4.表格布局
兼容性高,ie8兼容性也可以
.wrapper{
width: 100%;
height: 300px;
display: table;
}
.wrapper div{
display: table-cell;
}
.left{
width: 300px;
background: red;
}
.right{
width: 300px;
background: blue;
}
.center{
background: yellow;
}
优点:
实现容易,兼容性好;网页缩放时能保持正常布局
缺点:
一栏超出高度,其他栏也跟着变高,可能并不是我们期待的效果;无法设置栏间距;不能优先加载重要内容
5.网格布局
新知识点符合珊栏布局的思想
.wrapper{
display: grid;
width: 100%;
grid-template-rows: 100px;
grid-template-columns: 300px auto 300px;
}
.left{
background: red;
}
.right{
background: blue;
}
.center{
background: yellow;
}
优点:
绝对定位和相对定位都会引起元素脱离文档流,flex布局能够避免这个问题。grid布局实现简单,可以实现各种布局。目前虽然只支持高版本的浏览器,相信不久的将来会流行起来。
缺点:
A.一栏超出高度,其他栏也跟着变高。可能并不是我们期待的效果。
B.只支持高版本的浏览器,IE不支持(IE11支持,但需提供前缀),其他大部分需要较新版本的浏览器才支持。浏览器支持情况如下:
6、圣杯模式
- 三栏包括在一个容器中,空容器的左padding、右padding分别设置为左栏宽度值、右栏宽度值。
- 三栏全部设置为左浮动。左右两栏加上负margin让其跟中间栏div并排为一行。
(浮动元素可以设置负值,如果浮动元素B前面有浮动元素A,如果设置margin-left之后,上一行能够容纳下B,那B会跑到上一行。但因为浮动元素有条规则:浮动元素高度不会超过之前的浮动元素。所以如果B的margin-left的绝对值即使很大,也不可能超过A的高度。) - 设置完B后,左栏、右栏和中间栏都有重叠,设置左右为相对定位,并设置left、right值去除重叠。
#css
.left-sidebar,.right-sidebar,.main{
float:left;
min-height: 50px;
}
.main{
width:100%;
}
.mainContent{
margin-left:190px;
margin-right:300px;
background: #2894ff;
}
.left-sidebar{
margin-left:-100%;
width:190px; b
ackground: #c2ff68;
}
.right-sidebar{
width:300px;
margin-left:-300px;
background: #ffff37;
}
.clear{
clear:both;
}
#html
<div class='container'>
<div class='main'>
<div class='mainContent'>
主体内容区域:<br/>
这是通过浮动方案来解决的。<br/>这是通过浮动方案来解决的。<br/>
</div>
</div>
<div class='left-sidebar'>左边栏</div>
<div class='right-sidebar'>右边栏</div>
<div class="clear"></div>
</div>
7、双飞翼布局
#css
.container{
padding-left:190px;
padding-right:300px;
}
.left-sidebar,.right-sidebar, .main{
float:left;
min-height: 50px;
}
.main{
box-sizing:border-box;
width:100%;
background:#2894ff;
}
.left-sidebar{
margin-left:-100%;
width:190px;
position:relative;
left:-190px;
background: #c2ff68;
}
.right-sidebar{
width:300px;
margin-left:-300px;
position:relative;
right:-300px;
background: #ffff37;
}
.clear{
clear:both;
}
#html
<div class='container'>
<div class='main'>
主要内容
</div>
<div class='left-sidebar'>左边栏</div>
<div class='right-sidebar'>右边栏</div>
<div class="clear"></div>
</div>
优点:
能够先加载重要内容;允许任何列是最高的;DOM结构简单
缺点:
同双飞翼布局相比,圣杯布局的样式复杂些;圣杯布局在缩放的时候,如果中间宽度小于左侧宽度,布局混乱,如下图所示。
三、css盒模型
具体的计算及类型(IE和标准)我就不一一说啦
如何获得盒模型宽高
1、style样式
dom.style.width/height
2、常规的(只计算conten的值))
IE dom.currentStyle.width/height
w3c dom.getComputedStyle(dom,null).width/height
封装一下
function getStyle(elem, prop){
if(window.getComputedStyle){
return window.getComputedStyle(elem, null)['prop'];
}else{
return elem.currentStyle['prop'];
}
}
3、通过定位原理
dom.getBoundingCilentKect().width/height
4、offset(包含margin和padding)
div.offsetWidth/offsetHeight
BFC 块级格式化上下文
触发的场景
- position 定位
- display 转换内部
- float 浮动
- overflow不为visible其他皆可
应用场景
1、兄弟结构元素垂直方向的margin重叠
#css
.wrapper{
width: 100%;
background-color: red;
overflow: hidden;
}
p{
margin: 5px auto 25px;
background-color: pink;
}
#html
<div class="wrapper">
<p>1</p>
<p>2</p>
<p>3</p>
</div>
兄弟元素中margin取最大的25px作为最终渲染
margin重叠解决
了解一下即可,日常开发中其实可以不需要解决之类问题
<div class="wrapper">
<p>1</p>
<div style="overflow: hidden;">
<p>2</p>
</div>
<p>3</p>
</div>
2、父子级中垂直方向的margin子级会和父级元素结合在一起取最大值(塌陷)
#css
.wrapper{
width: 100px;
height: 100px;
background-color: red;
margin-top: 100px;
}
.content{
width: 100px;
height: 50px;
margin-top: 110px;
background-color: gray;
}
#html
<div class="wrapper">
<div class="content"></div>
</div>
3、BFC不于folat重叠
但没有创建BFC的时候,右边的溢出的部分会填充到左侧,我们是不希望有这样效果的,BFC的创建正好可以解决这个问题
#css
.wrapper{
width: 100%;
}
.left{
float: left;
width: 100px;
height: 100px;
background-color: red;
}
.right{
height: 120px;
background: gray;
overflow: auto; #创建BFC解决
}
#html
<div class="wrapper">
<div class="left"></div>
<div class="right"></div>
</div>
4、BFC子元素即使float也可以参与高度计算
#css
.left{
float: left;
}
html
<div class="wrapper">
<div class="float">aaaa</div>
</div>
但上述样式不多解释就知道父级高度并没有撑开,怎么做呢,创建BFC,父级添加float、overflo、position等都可以
四、DOM事件类
DOM事件级别的绑定与解绑
DOM0
#绑定
dom.onclick = function(){}
#解绑
dom.onclick = null
DOM2
W3C标准
#绑定 test函数、当false时冒泡事件,当true时捕获事件
dom.addEventListener('click',test,false)
function test(){}
# 解绑
dom.removeEventListener('click',test,false)
IE
#绑定
dom.attachEvent(onclick, test);
function test() {}
# 解绑
dom.detachEvent(onClick, test);
封装一下
function addEvent(elem, type, handle){
if(elem.addEventListener){
elem.addEventListener(type, handle, false)
}else if(elem.attachEvent){
elem.attchEvent('on' + type, function(){
handle.call(elem);
})
}else{
elem['on' + type] = handle
}
}
DOM3
DOM3和DOM2绑定的规则一样,只是这时候多了很多事件,如mousemove、keydown等事件
dom.attachEvent(onclick, test);
事件模型
捕获和冒泡
取消冒泡事件
捕获的取消就不讲了,定义的时候最后一个参数false就行啦,封装一下,IE和w3c标准
function stopBubble(event){
if(event.stopPropagation){
event.stopPropagation();
}else{
event.cancelBubble = true
}
}
# stopBubble(e)
事件流
捕获 -> 目标阶段 -> 冒泡
描述DOM事件捕获的具体流程
window -> document -> html标签 -> body -> … ->目标元素
var div = document.getElementsByClassName('wrapper')[0];
window.addEventListener('click', function(){
console.log('window captrue')
}, true)
document.addEventListener('click', function(){
console.log('document captrue');
}, true)
document.documentElement.addEventListener('click',function(){
console.log('html captrue');
}, true)
document.body.addEventListener('click', function(){
console.log('body captrue');
}, true)
div.addEventListener('click', function(){
console.log('div captrue')
}, true)
触发的顺序就是标题上的意思,就不做过多解释啦
Event对象常见的应用
- event.preventDefault() 阻止默认事件
- event.stopPropagation() 阻止冒泡事件
- event.stopImmediatePropagation()
事件响应优先级,但同一个dom绑定多个事件的时候,调用一个事件之后但不希望在触发其他事件,就可以在里面加这一句话 - event.currentTarget
- event.target 事件委托
target事件委托与currentTarget区别实例
ul.onclick = function(e){
var event = e || window.event;
var target = event.target || event.srcElement;
console.log(target.innerText);
console.log(event.currentTarget.tagName) #ul
}
target点击事件为触发相应的li事假,而currentTarget只会触发父级的
自定义事件
# 创建事件对象
var evt = document.createEvent('Event');
# 定义事件类型
evt.initEvent('customEvent', true, true);
# 在元素上监听事件
var obj = document.getElementById('testBox');
obj.addEventListener('customEvent', function(){
console.log('customEvent 事件触发了');
}, false);
createEvent
方法创建了一个空事件 evt
,然后使用initEvent
方法定义事件的类型为约定好的自定义事件,再对相应的元素进行监听,接着,就是使用dispatchEvent
触发事件了。
五、http协议类
HTTP请求方法
GET、HEAD、POST、PUT、DELETE、CONNECT、OPTIONS、TRACE
http协议主要特点
- 简单快速 - url
- 灵活:根据请求头设置不同数据类型
- 无连接:不需要连接,结束自动截掉
- 无状态:第二次访问也不会有状态,犹如初恋
http报文组成
请求报文
- 请求行
- 请求头
- 空行
- 请求体
响应报文
- 状态行
- 响应头
- 空行
- 相应体
POST和GET的区别
- GET在浏览器回退时是无害的,而POST会再次提交请求
- GET产生的URL地址可以被收藏,而POST不可以
- GET请求会被浏览器主动缓存,而POST不会,除非手动设置
- GET请求只能进行url编码,而POST支持多种编码方式
- GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留
- GET请求在URL中传送的参数是有长度限制的,而POST没有限制
- 对参数的数据类型,GET只接受ASCLL字符,而POST没有限制
- GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息
- GET参数通过URL传递,POST放在Request body中
http状态码
- 1xx:指示信息-表示请求已接收,继续处理
- 2xx:成功-表示请求已被成功接收
- 3xx:重定向-要完成请求必须进行跟进一步的操作
- 4xx:客户端错误-请求有语法错误或者请求无法实现
- 5xx:服务器错误-服务器未能实现合法的请求
http持久连接
http在使用普通模式下,Keep-Alive模式时,每一个请求/应答客户和服务器都要新建一个连接,完成之后立刻断开连接(http协议无连接的协议)。
当使用Keep-Alive模式(持久连接、连接重用)时,Keep-Alive功能使客户端到服务器端的连接持续有效,当出现对服务器的后续请求是,Keep-Alive功能避免了建立或者重新建立连接
持续连接只在http1.1版本下支持
管线化
在持久化情况下
正常情况下:
请求1->响应1->请求2->响应2->请求3->响应3
在管道化情况下
请求1->请求2->请求3->响应1->响应2->响应3->
打包所有的请求集合在一起然后在请求
管线化特点
- 管线化机制通过持久连接完成,仅http/1.1支持此技术
- 只有GET和HEAD请求可以进行管线化,而POST则有所限制
- 初次创建连接时不应启动管线机制,因为对方(服务器)不一定支持HTTP/1.1版本的协议
六、原型链类
- 创建对象有几种方法
- 原型、构造函数、实例、原型链
- instanceof原理
- new运算符
创建对象几种方法
第一类字面量对象
# 第一种
var obj1 = {name: 'obj1'}
# 第二种
var obj2 = new Object({name: 'obj2'})
第二类显示构造函数
var Obj = function(){this.name='obj'}
var obj = new Obj();
第三类定义父级原型的构造
var Farther = {name: 'farther'}
var Son = Object.create(Farther)
原型链原理
以jQuer源码中为例
window.jQuer = window.$ = jQuery;
function jQuery(str){
return new jQuery.prototype.init(str);
}
jQuery.prototype.init = function(str){
....
return this;
}
jQuery.prototype.css = function(){
...
return this;
}
jQuery.protoype.html = function(){
...
return this;
}
jQuery.prototype.init.prototype = jQuery.prototype
instanceof原理与constructor
function Person(){}
var person = new Person()
console.log(person instanceof Person) # true
console.log(person instanceof Object) # true
console.log(obj.constructor === Obj) # true
console.log(obj.constructor === Object) #false
分析:
- person对象 是不是 Person构造函数构造出的对象
- 只有person所在的原型链
instanceof
都会返回true -
constructor
判定直接构成函数的父级笔记严谨
七、面向对象
声明与实例化
# 类的声明
function Animal(){
this.name = 'name';
}
# ES6中的class的声明
class Animal2(){
constructor(){
this.name = name;
}
}
# 实例化
new Animal();
new Animal2();
继承
1、借助构造函数实现继承
Parent.prototype.lasName = 'aaa'
function Parent(){
this.name = 'parent';
}
Child.prototype.newName = 'bbb'
function Child(){
Parent.call(this);
this.type = 'child'
}
特点
- call/apply能改函数运行的上下文,从而实现继承,但是
Parent
如果定义原型上的方法,Child
是无法取到的 -
Child
只能取到Parent
函数中的属性,但是取不到Parent
的原型属性
2、正则共享原型
Parent.prototype.lasName = 'aaa'
function Parent(){
this.name = 'parent';
}
Child.prototype.newName = 'bbb'
function Child(){
this.type = 'child';
}
Child.prototype = new Parent();
var child = new Child()
var parent = new Parent()
parent.name = 'ccc'
特点:
1、改变Child
原型从而继承parent
原型上的属性
2、Child上定义的原型,在实例上就取不到了,因为已经改变了自身的原型指向。
3、此继承了Parent
上的原型方法和函数自身属性,child
修改原型上面属性时,Parent
原型上方法会随之也改变,这是我们不希望的。
3、复合方法
function Parent(){
this.name = 'parent';
}
function Child(){
Parent.call(this);
this.type = 'child'
}
Child.prototype = new Parent;
特点:
1、Parent
被new执行了两次,没有必要的事
2、Child
改变原型的方法,Parent
不会随之改变
4、圣杯模式
Parent.prototype.lasName = 'aaa'
function Parent(){
this.name = 'parent';
this.play = [1,2]
}
Child.prototype.newName = 'bbb'
function Child(){
Parent.call(this);
this.type = 'child';
}
function inherit (Target, Origin){
function F(){};
F.prototype = Origin.prototype;
Target.prototype = new F;
Target.prototype.constructor = Target
Target.prototype.uber = Object.prototype;
}
inherit(Child, Parent);
var a = new Child()
Child.prototype.lasName='ccc'
特点Child
定义的自身原型无效的,实例无法访问
5、Es6语法
Parent.prototype.lasName = 'aaa'
Parent.prototype.set = 'set'
function Parent(){
this.name = 'parent';
this.play = [1,2]
}
Child.prototype.newName = 'bbb'
function Child(){
Parent.call(this);
this.type = 'child';
}
function inherit (Target, Origin){
Object.assign(Target,Parent.prototype)
}
inherit(Child, Parent);
var a = new Child()
Child.prototype.lasName='ccc'
特点assign
是es6语法糖,将Parent.prototype
可枚举的属性全部添加到Target
中
克隆
思路
1、遍历对象for(var prop in obj);
2、判断是不是原始值;
3、判断数组还是对象;
4、简历相应的数组或对象
function deepClone(origin, target){
var target = target || {},
toStr = Object.prototype.toString,
arrStr = "[object Array]";
for(var prop in origin){
if(origin.hasOwnProperty(prop)){
if(origin[prop] !== 'null' && type(origin[prop]) == 'object'){
if(toStr.call(origin[prop]) === arrStr){
target[prop] = [];
}else{
target[prop] = {};
}
deepClone(origin[prop], target[prop]);
}else{
target[prop] = origin[prop]
}
}
}
return target;
}
八、通信类
- 什么同源策略及限制
- 前后端如何通信
- 如何创建Ajax
- 跨域通信的几种方式
什么是同源策略及限制
同源策略限制从一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的关键的安全机制
注:
源:协议、域名、端口
非同源策略下的限制
- Cookie、LocalStorage和IndexDB无法读取
- DOM无法获取
- Ajax请求不能发送
前后端如何通信
- Ajax
- WebSocket
- CORS
如何创建Ajax
定外卖的思想
- 手机、电脑----------------->浏览器
- 美团外卖、饿了么等-------->ajax对象
- 打开APP 商家、商品-------->ajax.open(method,url,true)
- 下单(……)--------------->ajax.send()
- 监听外卖信息-------------->onreadystatechage 4
- 开门、验货、处理---------->status == 200 403 503
封装一下
function ajaxFunc(method, url, data,callback, flag){
var xhr = null;
#创建ajax对象
if(window.XMLHttpRequest){
xhr = new XMLHttpRequest();
}else{# ie兼容
xhr = new ActiveXObject('Microsoft.XMLHttp')
}
#兼容小写,统一转换成大写‘GET’、‘POST’
method = method.toUpperCase();
if(method == 'GET'){
xhr.open(method, url + '?' + data, flag);
xhr.send();
}else if(method == 'POST'){
xhr.open(method, url, flag);
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr.send(data);
}
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
if(xhr.status == 200 || xhr.status == 304){
callback(xhr.reponseText);
}else{
console.log('error')
}
}
}
}
跨域通信的几种方式
- JSONP
- 服务中转站
- doucument.domain(针对基础域名相同的情况下)
- Hash:url中#之后的参数
- postMessage: h5
- WebSocket
- CORS
JSONP
src
不受同源策略的限制。
var oScript = document.createElement('script');
oScript.src = 'https://......?wd=' + value + '&cb=doJson';
document.body.appendChild(oScript)
function doJson(data){console.log(data)}
CORS通信
fetch('/some/url',{
method: 'get'
}).then(function(response){
}).catch(function(err){
#出错啦:等价于then的第二个参数,这样比较直观
})
九、安全类
- CSRF
- XSS
CSRF
基本概念和缩写
跨站请求伪造
攻击原理
防御措施
- Token验证
- Referer验证
- 隐藏令牌
十、渲染机制类
- 什么是DOCTYPE及作用
- 浏览器渲染过程
- 重排Reflow
- 重绘Repaint
- Layout
什么是DOCTYPE及作用
DTD文档类型定义
DTD就是就是告诉浏览器我是什么文档类型,让浏览器知道需要用什么解析
DOCTYPE定义
用来声明文档类型和DTD规定的,主要用途便是文件的合法性验证。如果文件代码不合法,那么浏览器解析时便会出差错(直接告诉浏览器什么时候DTD,告诉浏览器是什么文档类型)
作用
- HTML5
- HTML4.0.1 Strict 严格模式
- HTML4.0.1 Transitional 普通模式
浏览器的渲染过程
重排Reflow
dom结构中的各个元素都有自己的盒子(模型),这些都需要浏览器根据各种样式来设计并根据计算结果将元素放到它改出现的位置,这个过程称之为reflow
- 当你操作dom,增加、删除、修改DOM结点时,导致Reflow或、Repaint
- 当你移动DOM的位置,或是搞个动画的时候
- 当你修改CSS样式的时候
- 当你Reaize窗口的时候(移动端没有这个问题),或者滚动的时候
- 当你修改页面的默认字体时
- 当你窗口大小、字体大小、内容改变、输入框输入文字
- 当你激活伪类,如:hover
- 当你操作class属性
- 当你计算offsetWidth和offsetHeight
# 让浏览器开启gpu加速,监听tranform属性加速
div {
width: 100px;
height: 100px;
}
div:hover {
will-change: tranform
}
div:active {
transform: scale(2, 3)
}
重绘Repaint
当各种盒子的位置、大小以及其他属性,列如颜色、字体大小等都确定下来后,浏览器于是便把这些元素都按照各自的特性绘制一遍,于是页面的内容出现啦
- DOM改动
- CSS改动
js运行机制(js单线程)
console.log('A')
setTimeout(function(){
console.log('B')
})
while(1){
}
只会输出A,在单线程队列中只要还未加载完,异步setTimeout
是不会进行的
十一、页面性能类
提升页面性能的方法有哪些?
- 资源压缩合并,减少HTTP请求
- 非核心代码异步加载——>异步加载方式——>异步加载的区别
- 利用浏览器缓存——>缓存的分类——>缓存的原理
- 使用CDN
- 与解析DNS
异步加载
1.异步加载方式
- 动态脚本加载
- defer
- async
2.异步加载的区别
- defer是在HTML解析完之后才会执行,如果是多个,按照加载的顺序依次执行
- async是在加载完之后立即执行,如果是多个,执行顺序和加载顺序无关