offer收割机-Web前端面试宝典【精编版-1】

291 阅读37分钟

image

过了八月,马上到了金九银十的跳槽季,找工作的时候曾一周面试了十多家公司,从一开始被面试官虐的体无完肤到最后对答如流,过程中经历了多少挑灯夜战的晚上,恶补前端知识,努力的付出终会有回报,最终获得了心仪的offer,所有的一切都是值得的。整理一下面试过程中遇到的高频题目,这些都是精简后的真题,就算不能百分百涵盖,至少也囊括7、8%了,如果面试过程中的你能回答出百分之八十左右的题目,不出大的纰漏offer基本上也就稳收囊中了,剩下的百分之二十是面试官测试你的技术极限在哪里,也会因此来决定你的薪资。

不要觉得面试题目多,难度大,很多知识掌握不了就抱着侥幸心态觉得问不到,我想说的是任何行业都适用二八定律,努力成为你自己行业那百分之二十拔尖的人。总而言之就是发展自己才是硬道理,发展的好会遭人嫉妒,这是正常的,否则你得多普通呀。不仅要在专业方面需要进步,当专业发展遇到天花板时说明需要扩维了。学习不要太功利,往往看似无用的东西反而在某个时候会有大用处。只有感到痛苦我们才能成长,舒适区只有两个: 妈妈的子宫和死去的状态,前一个地方我们回不去,后一个地方我们不想去,只能挣扎着克服一切克服的了抑或克服不了的事儿,坚持坚持就过去了,等回头看时都是p大的事儿,大家一起加油!

在线访问手册: hanxueqing.github.io/Web-Front-e… github地址: github.com/Hanxueqing/…

H5/C3/ES6/JS

Html5

h5、css3、es6的新属性

h5新特性:

  1. 拖拽释放(Drag and drop) API
  2. 语义化更好的内容标签(header,nav,footer,aside,article,section)
  3. 音频、视频API(audio,video)
  4. 画布(Canvas) API
  5. 地理(Geolocation) API
  6. 本地离线存储 localStorage 长期存储数据,浏览器关闭后数据不丢失;
  7. sessionStorage 的数据在浏览器关闭后自动删除
  8. 表单控件:calendar、date、time、email、url、search
  9. 新的技术:webworker, websocket, Geolocation

移除的元素:

  1. 纯表现的元素:basefont,big,center,font, s,strike,tt,u;

  2. 对可用性产生负面影响的元素:frame,frameset,noframes

H5的特点

  1. 更简洁,但是在实际开发中要注意书写规范

  2. 标签的语义化

  3. 语法更宽松

BFC

BFC(块级格式化上下文),一个创建了新的BFC的盒子是独立布局的,盒子内元素的布局不会影响盒子外面的元素,在同一个BFC中的两个相邻的盒子在垂直方向发生margin重叠的问题。

BFC是指浏览器中创建了一个独立的渲染区域,该区域内所有元素的布局不会影响到区域外元素的布局,这个渲染区域只对块级元素起作用。

触发BFC的方式(以下任意一条就可以)

  1. html根元素

  2. float属性不为none

  3. position不为visible,为absolute或fixed。

  4. display为inline-block, table-cell, table-caption, flex, inline-flex

  5. overflow不为visible

BFC布局与普通文档流布局区别

(注:字体加粗的触发BFC的条件都支持)

普通文档流布局规则

1. 浮动的元素是不会被父级计算高度

2. 非浮动元素会覆盖浮动元素的位置(兄弟关系的元素)。

3. 给子级添加margin-top会传递给父级

4.两个相邻元素上下margin会重叠

BFC布局规则

1.浮动的元素会被父级计算高度(高度坍塌)

(1)给父级添加浮动float

(2)给父级添加overflow:hidden; 触发了BFC(子元素浮动,父元素没有高度时使用,该方法不能和定位一起使用)

(3)给父级添加display:inline-block;

(4)给父级添加定位position:absolute/fixed;

2. 非浮动元素不会覆盖浮动元素位置

(1)非浮动元素float的值不为none

(2)非浮动元素加overflow:hidden触发了BFC。

(3)非浮动元素display:inline-block;

(4)给非浮动元素或者浮动元素添加position定位,添加的元素会覆盖在未添加的元素上面

3. 给子级添加margin-top不会传递给父级

(1)给父级添加浮动float

(2)给父级添加overflow:hidden;

(3)给子级添加display:inline-block;

(4)给父级添加定位position:absolute/fixed;

Form中的获取数据的两个方式get和post的区别?

  1. get是从服务器上获取数据,post是向服务器传送数据。

  2. get是把参数数据队列加到提交表单的ACTION属性所指的URL中,值和表单内各个字段一一对应,在URL中可以看到。post是通过HTTP post机制,将表单内各个字段与其内容放置在HTML HEADER内一起传送到ACTION属性所指的URL地址。用户看不到这个过程。get是显式提交,post是隐式提交。

  3. 对于get方式,服务器端用Request.QueryString获取变量的值,对于post方式,服务器端用Request.Form获取提交的数据。

  4. get传送的数据量较小,不能大于2KB。post传送的数据量较大,一般被默认为不受限制。

  5. get安全性非常低,post安全性较高。但是执行效率却比Post方法好。

什么时候使用get什么时候使用post?

  1. get方式的安全性较Post方式要差些,包含机密信息的话,建议用Post数据提交方式;

  2. 在做数据查询时,建议用Get方式;而在做数据添加、修改或删除时,建议用Post方式;

Html中form里的action方法的get与post方法区别是什么?

get:

  1. 通过GET提交数据,用户名和密码将明文出现在URL上,因为登录页面有可能被浏览器缓存,GET请求请提交的数据放置在HTTP请求协议头

  2. 或者其他人查看浏览器的历史纪录,那么别人就可以拿到你的账号和密码了,除此之外,使用GET提交数据还可能会造成Cross-site request forgery攻击,所以不安全

  3. GET请求有长度限制

post:

  1. POST数据放在body(POST提交的数据则放在实体数据),POST请求数据不能被缓存下来

  2. POST请求参数不会被保存在浏览器历史或 web 服务器日志中。

  3. POST请求没有长度限制

参考:form里面的action和method(post和get的方法)使用

www.cnblogs.com/May-day/p/5…

####渐进增强和优雅降级的概念以及区别

**渐进增强 progressive enhancement:**针对低版本浏览器进行构建页面,保证最基本的功能,然后再针对高级浏览器进行效果、交互等改进和追加功能达到更好的用户体验。

**优雅降级 graceful degradation:**一开始就构建完整的功能,然后再针对低版本浏览器进行兼容。

**区别:**优雅降级是从复杂的现状开始,并试图减少用户体验的供给,而渐进增强则是从一个非常基础的,能够起作用的版本开始,并不断扩充,以适应未来环境的需要。降级(功能衰减)意味着往回看;而渐进增强则意味着朝前看,同时保证其根基处于安全地带。

####如何处理HTML5新标签的浏览器兼容问题?

支持HTML5新标签:

IE8/IE7/IE6支持通过 document.createElement 方法产生的标签,可以利用这一特性让这些浏览器支持 HTML5 新标签,浏览器支持新标签后,还需要添加标签默认的样式(当然最好的方式是直接使用成熟的框架、使用最多的是html5shim框架):

<!--[if lt IE 9]>
<script> src="http://html5shim.googlecode.com/svn/trunk/html5.js"</script>
<![endif]-->

####如何区分HTML和HTML5

DOCTYPE声明新增的结构元素、功能元素

文档碎片

如果我们要在一个ul中添加100li,如果不使用文档碎片,那么我们就需要使用append经常100次的追加,这会导致浏览器一直不停的渲染,是非常消耗资源的。但是如果我们使用文档碎片了,我们可以先将100li添加到文档碎片中,然后直接把这个文档碎片追加到ul中即可。所以文档碎片其实就是一个临时的仓库。

如下代码在document.body中添加5span

for(var i=0;i<5;i++)
{ 
    var op = document.createElement("span"); 
    var oText = document.createTextNode(i); 
    op.appendChild(oText); 
    document.body.appendChild(op); 
} 

对于少量的更新,一条条循环插入的运行也还可以。但是,当要向document中添加大量数据(比如1w条),如果像上面的代码一样,逐条添加节点,整个过程会十分缓慢,性能极差。 也可以建一个新的节点,例如div,先将span添加到div上,然后再将div添加到body:

var oDiv = document.createElement("div"); 
for(var i=0;i<10000;i++)
{ 
    var op = document.createElement("span"); 
    var oText = document.createTextNode(i); 
    op.appendChild(oText); 
    oDiv.appendChild(op);  
} 
document.body.appendChild(oDiv); 

但这样会在body中多添加一个div节点。用文档碎片就不会产生这种节点,引入createDocumentFragement()方法,它的作用是创建一个文档碎片,把要插入的新节点先插入它里面,然后再一次性地添加到document中。 代码如下:

//先创建文档碎片
var oFragmeng = document.createDocumentFragment(); 
for(var i=0;i<10000;i++)
{ 
    var op = document.createElement("span"); 
    var oText = document.createTextNode(i); 
    op.appendChild(oText); 
    //先附加在文档碎片中
    oFragmeng.appendChild(op);  
} 
//最后一次性添加到document中
document.body.appendChild(oFragmeng); 

浏览器内核

IE: trident内核

Firefox:gecko内核

Safari:webkit内核

Opera:以前是presto内核,Opera现已改用Google Chrome的Blink内核

Chrome:Blink(基于webkit,Google与Opera Software共同开发)

什么是viewport?

viewport是视口,在浏览器中用来显示网页的某一块区域,以合理的比例显示内容。

###CSS3

css3新特性

  1. CSS3实现圆角(border-radius),盒子阴影(box-shadow),文本阴影(text-shadow)

  2. 渐变(gradient):线性渐变、径向渐变,旋转(transform)

  3. 有关制作动画的三个属性:transform、transition、animation

  4. transform:translate移动、scale缩放、rotate旋转、skew倾斜

  5. animation动画 keyframes关键帧

  6. 增加了更多的CSS选择器 多背景 rgba

  7. 在CSS3中唯一引入的伪元素是 ::selection.

  8. 媒体查询,多栏布局

  9. border-color、border-image、border-radius

  10. filter:blur()虚化图片

####css3的优势

  1. 让页面效果看起来非常炫酷,用户体验更高。
  2. 有利于开发和维护,还能提高网站的性能,增加网站的可访问性,可用性。
  3. 使网站能适配更多的设备,利于seo网站优化,提高网站的搜索排名。

css sprites如何使用

将导航背景图片,按钮背景图片等有规则的合并成一张背景图,即将多张图片合为一张整图,然后用“background-position”来实现背景图片的定位技术。

CSS元素类型

块状元素,内联元素(行内元素),内联块元素(行内块元素)

空元素有哪一些

br hr

标准盒模型和怪异盒模型

CSS中Box model是分为两种:W3C标准和IE标准盒子模型

在标准模式下,一个块的总宽度 = width + margin+padding+border

在怪异模式下,一个块的总宽度=width+margin(即width已经包含了padding和border的值)

大多数浏览器采用W3C标准模型,而IE中则采用Micreosoft自己的标准。

怪异模式是"部分浏览器在支持W3C标准的同时还保留了原来的解析模式",怪异模式主要表现在IE内核的浏览器。

当不对doctype进行定义时,会触发怪异模式。

标准盒模型和怪异盒模型的转化

怪异盒模型:box-sizing:border-box

标准盒模型:box-sizing:content-box

####列出display的值,说明他们的作用

display属性与属性值 (18个属性值)

属性值:block/inline/inline-block/none/list-item/table-header-group/table-footer-group/table

作用:块状元素和内联元素之间的转换。

  1. Block块状显示:类似在元素后面添加换行符,也就是说其他元素不能在其后面并列显示。或者就是让元素竖排显示。

  2. inline内联显示:在元素后面删除换行符,多个元素可以在一行内并列显示。或者就是让元素横排显示。

  3. 当元素设置了float属性后,就相当于该元素具备块状元素显示的特点;可以加宽高。

  4. Inline-block行内块元素显示:元素的内容以块状显示,行内的其他元素显示在同一行。(只有行内块这一个元素类型支持vertical-align属性)

    img,input垂直对齐方式{vertical-align:top/bottom/middle;}

  5. none 此元素不会被显示。隐藏且不占位

####position的值:relative和absolute分别是相对谁进行定位的

position:定位属性,检索对象的定位方式;

定位都脱离层。

语法:position:static /absolute/relative/fixed

absoluted/relative/fixed 对定位有效,对层级属性Z-index有效。

取值:

  1. static:默认值,无特殊定位,对象遵循HTML原则;
  2. absolute:绝对定位,将对象从文档流中拖离出来,使用left/right/top/bottom等属性相对其最接近的一个并有定位设置的父元素(祖先元素|祖先元素不包含叔叔级的)进行绝对定位;如果不存在这样的父对象,则依据根元素(html)浏览器进行定位,而其层叠通过z-index属性定义,可以脱离当前的大容器,并且不占位。float脱离半层。
  3. relative :相对定位,该对象的文档流位置不动,将依据right,top,left,bottom(相对定位)等属性在正常文档流中偏移位置;其层叠通过z-index属性定义

一般给父层级添加relative属性值

相对于自身位置进行偏移

元素设置:margin:0 auto时:

(1)box为相对定位时(相对自身),居中

(2)box为绝对定位时(相对于根元素HTML),向左

  1. fixed:(固定定位)未支持,对象定位遵从绝对定位方式(absolute);但是要遵守一些规范(IE6浏览器不支持此定位);跟绝对定位一样的都是脱离文档流,不占位,并且永远相对于当前浏览器的可视窗口进行位置偏移。

absolute是不是一定需要设置相对定位

absolute不一定必须找到position单词修饰的祖先元素,如果找不到就相对浏览器进行定位。

####解释下浮动和它的工作原理

语法:float:none/left/right;

float浮动脱离半层

float:定义网页中其它文本如何环绕该元素显示

嵌套浮动元素的父级要有高度,不然会高度塌陷,解决办法,添加overflow:hidden;(注意不能与定位一起使用)

浮动的目的:就是让竖着的东西横着来

有三个取值:

left:元素活动浮动在文本左面

right:元素浮动在右面

none:默认值,不浮动。

清除浮动的方法

  1. 在结尾处添加空div标签clear:both;

  2. 万能清除浮动法(给浮动元素的父元素添加)class名.clear,哪个需要清除就给哪个元素命名为clear ;一个元素可以取多个class名,中间用空格隔开)。

:after{content:".";clear:both;display:block;height:0;overflow:hidden;visibility:hidden;}
.clear{zoom:1}
  1. 父级div定义height

  2. 父级div定义overflow:hidden

  3. 父级div定义overflow:auto

  4. 父级div也一起浮动

  5. 父级div定义display:table

  6. 结尾处加br标签clear:both

参考:几种常用的清除浮动方法

www.cnblogs.com/nxl0908/p/7…

为何要清除浮动

浮动框不属于文档流中的普通流,当元素浮动之后,不会影响块级元素的布局,只会影响内联元素布局。此时文档流中的普通流就会表现得该浮动框不存在一样的布局模式。当包含框的高度小于浮动框的时候,此时就会出现“高度塌陷”

####CSS3动画的实现种类

CSS3属性中有关于制作动画的三个属性:

Transition(过渡),Transform(转换),Animation(动画)

####animation和transition的区别

**相同点:**都是随着时间改变元素的属性值。

**不同点:**transition需要触发一个事件(hover事件target事件或click事件等)才会随时间改变其css属性; 而animation在不需要触发任何事件的情况下也可以显式的随着时间变化来改变元素css的属性值,从而达到一种动画的效果,css3的animation就需要明确的动画属性值。

transition触发后只能运动一次,animation可以设定运动的循环次数。

Animation-->在这个动画之前,先看Keyframes关键帧,支持animation动画的只有webkit内核的浏览器

如何让chrome支持小于12px的文字?(css3中的缩放形式)

<style>
	p span{
		font-size:10px;
		-webkit-transform:scale(0.8);
		display:block;
	}
</style>
<p><span>haorooms博客测试10px</span></p>

####iframe的优缺点

优点:

  1. 解决加载缓慢的第三方内容如图标和广告等的加载问题

  2. Security sandbox

  3. 并行加载脚本

缺点:

  1. iframe会阻塞主页面的Onload事件

  2. 即时内容为空,加载也需要时间

  3. 没有语意

回流和重绘

  1. 当render tree中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建。这就称为回流。每个页面至少需要一次回流,就是在页面第一次加载的时候。

  2. 当render tree中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的,比如background-color。则就叫称为重绘。

注:回流必将引起重绘,而重绘不一定会引起回流

回流就相当于整个页面发生了DOM上的变化 重绘就是样式发生了改变

区别:

他们的区别很大: 回流必将引起重绘,而重绘不一定会引起回流。比如:只有颜色改变的时候就只会发生重绘而不会引起回流 当页面布局和几何属性改变时就需要回流 比如:添加或者删除可见的DOM元素,元素位置改变,元素尺寸改变——边距、填充、边框、宽度和高度,内容改变

CSS选择符

类型选择符,id选择符,class选择符,通配符,群组选择符,包含选择符,伪类选择符(伪类选择符CSS中已经定义好的选择器,不能随便取名),伪对象选择符(设置在对象后发生的内容,用来和content属性一起使用 )

CSS选择符的权重

css中用四位数字表示权重,权重的表达方式如:0,0,0,0

类型选择符的权重为0001

class选择符的权重为0010

id选择符的权重为0100

子选择符的权重为0000

属性选择符的权重为0010

伪类选择符的权重为0010

伪元素选择符的权重为0001

包含选择符的权重:为包含选择符的权重之和

群组选择符的权重:为各自的权重

内联样式的权重为1000

继承样式的权重为0000

*通配符选择器权重为0

垂直水平居中

CSS2有定位

让一个元素实现垂直水平居中:

(1)给当前元素后面添加空标签,代码不换行,必须写在一行上。

(2)给当前元素和span添加display:inline-block;转换为内联块元素。

(3)给span添加height属性,与父级同高;

(4)给当前元素和span添加vertical-align:middle;实现垂直居中。

(5)给父元素添加text-align:center;实现水平居中。

让一个图片实现垂直水平居中:

(1)给当前元素后面添加空标签,代码不换行,必须写在一行上。

(2)给span添加display:inline-block;转换为内联块元素。(图片本身就是内联块元素,不需要再转换)

(3)给span添加height属性,与父级同高;

(4)给当前元素和span添加vertical-align:middle;实现垂直居中。

(5)给父元素添加text-align:center;实现水平居中。

水平居中:text-align:center:(1)对文本有效;(2)对内联元素、内联块元素有效。

当前元素为有宽度的块元素时,可以用margin:0 auto;实现水平居中。

垂直居中:vertical-align:middle;只对内联块元素有效。

用定位让元素或图片实现垂直水平居中:

(1)给父元素定位:position:relative;

(2)给子元素加绝对定位:position:absolute;

(3)给子元素添加坐标属性:top:0;left:0;right:0;bottom:0;margin:auto;

相当于给元素提供了四个参照范围

让一个元素在当前浏览器窗口垂直水平居中:

第一种方法:(1)给元素添加固定定位:position:fixed;

(2)给元素添加坐标属性:top:0;left:0;right:0;bottom:0;margin:auto;

第二种方法:(1)给元素添加固定定位position:fixed;

(2)top:50%;left:50%;

(3)margin:-元素高度/2px 0 0 -元素宽度/2px;

中心点的移动:从左上移到中心点

c3有弹性盒布局

给父元素添加:

display:flex;

justify-content: center;

align-items: center;

利用位移(定位加位移)

父元素:

positive:relative;

子元素:

position: absolute;

top:50%;

left:50%;

transform: translate(-元素宽度/2px,-元素高度/2px);

####两种引入外部样式表link和import之间的区别

差别1:本质的差别:link属于XHTML标签,而@import完全是CSS提供的一种方式。

差别2:加载顺序的差别:当一个页面被加载的时候(就是被浏览者浏览的时候),link引用的CSS会同时被加载,而@import引用的CSS会等到页面全部被下载完再被加载。所以有时候浏览@import加载CSS的页面时开始会没有样式(就是闪烁),网速慢的时候还挺明显。

差别3:兼容性的差别:@import是CSS2.1提出的,所以老的浏览器不支持,@import只有在IE5以上的才能识别,而link标签无此问题。

差别4:使用dom(document object model文档对象模型 )控制样式时的差别:当使用javascript控制dom去改变样式的时候,只能使用link标签,因为@import不是dom可以控制的。

简述src与href的区别

src用于替换当前元素,href用于在当前文档和引用资源之间确立联系。

src是source的缩写,指向外部资源的位置,指向的内容将会嵌入到文档中当前标签所在位置;在请求src资源时会将其指向的资源下载并应用到文档内,例如js脚本,img图片和frame等元素。

<script src =”js.js”></script>

当浏览器解析到该元素时,会暂停其他资源的下载和处理,直到将该资源加载、编译、执行完毕,图片和框架等元素也如此,类似于将所指向资源嵌入当前标签内。这也是为什么将js脚本放在底部而不是头部。

href是Hypertext Reference的缩写,指向网络资源所在位置,建立和当前元素(锚点)或当前文档(链接)之间的链接,如果我们在文档中添加

<link href="common.css" rel="stylesheet"/>

那么浏览器会识别该文档为css文件,就会并行下载资源并且不会停止对当前文档的处理。这也是为什么建议使用link方式来加载css,而不是使用@import方式。

伪类选择器

1、结构性伪类选择器

①:first-child选择某个元素的第一个子元素;IE6不支持:first-child选择器

②:last-child选择某个元素的最后一个子元素;

③:nth-child()选择某个元素的一个或多个特定的子元素;

④:nth-last-child()选择某个元素的一个或多个特定的元素,从这个元素的最后一个元素开始算;

⑤:nth-of-type()选择指定类型的元素;

nth-of-type类似于:nth-child,不同的是他只计算选择指定的元素类型。

⑥:nth-last-of-type()选择指定的元素,从元素的最后一个开始计算;

⑦:first-of-type选择一个上级元素下的第一个同类元素;

⑧:last-of-type选择一个上级元素的最后一个同类元素;

⑨:only-child选择的元素是它的父元素的唯一 一个子元素;

⑩:only-of-type选择一个元素是它的上级元素的唯一一个相同类型的子元素;

伪元素选择器

::first-line ::first-letter ::before ::after

CSS3新增:::selection 用来改变浏览网页选中文本的默认效果

CSS3中主要用两个"::"和一个":"来区分伪类和伪元素

####用CSS写一个三角形

aside{

width:1px;

height:1px;

border:50px solid #fff;

border-bottom-color:red;

}

设置宽高为0或者1像素,需要哪个方向的三角形,就给哪个方向的边框单独加颜色。

font属性的简写

说明:font的属性值应按以下次序书写(各个属性之间用空格隔开)

顺序: font-style font-weight font-size / line-height font-family

####css实现文字溢出显示省略号

  1. 容器宽度:width:value;

  2. 强制文本在一行内显示:white-space:nowrap;

  3. 溢出内容为隐藏:overflow:hidden;

  4. 溢出文本显示省略号:text-overflow:ellipsis;

圣杯布局

left:左浮动;

right:右浮动;

center:overflow:hidden;

顺序:左右中,左浮右浮。

####自适应两栏布局

html,body{height:100%;}高度写百分比时,需要声明body和html的高度为100%。

布局:一左一右,左半部分浮动,非浮动元素会覆盖浮动元素的位置。

给右半部分添加overflow:hidden;

自适应,非浮动元素不加宽度。

只适用于pc端,比较简单的布局。

CSS3兼容

各主流浏览器都定义了私有属性,以便让用户体验 CSS3 的新特性。例如,

  • Webkit 类型浏览器(如 Safari、Chrome)的私有属性是以-webkit-前缀开始,
  • Gecko 类型的浏览器(如 Firefox)的私有属性是以-moz-前缀开始,
  • Konqueror 类型的浏览器的私有属性是以-khtml-前缀开始,
  • Opera 浏览器的私有属性是以-o-前缀开始,
  • 而 Internet Explorer 浏览器的私有属性是以-ms-前缀开始(目前只有 IE 8+ 支持-ms-前缀)。

什么是Css Hack?ie6,7,8的hack分别是什么?

针对不同的浏览器写不同的CSS code的过程,就是CSS hack。

#test       {   
        width:300px;   
        height:300px;   
        background-color:blue;      /*firefox*/
        background-color:red\9;      /*all ie*/
        background-color:yellow;    /*ie8*/
        +background-color:pink;        /*ie7*/
        _background-color:orange;       /*ie6*/    }  
        :root #test { background-color:purple\9; }  /*ie9*/
    @media all and (min-width:0px){ #test {background-color:black;} }  /*opera*/
    @media screen and (-webkit-min-device-pixel-ratio:0){ #test {background-color:gray;} }       /*chrome and safari*/

###ES6

ES6的新特性

(1)let关键字:let关键字声明变量,只要遇到大括号,就形成作用域。

(2)const:const声明的变量,一旦被声明,就没有办法被修改。

(3)箭头函数

(4)变量的解构赋值

(5)字符串模板:使用反引号`表示,使用${变量|函数}嵌入代码

(6)数组的一些方法:

  • Array.from():将伪数组转成真数组。(极少数的地方会用)
  • copyWithin()了解:复制指定内容覆盖指定内容(指定数组的下标6、7替换下标2及以后)
  • find() 功能:查找符合条件的第一个元素。
  • findIndex(); 功能:查找符合条件的第一个元素的下标。

(7)Object.assign 合并对象

(8)第七种数据类型Symbol

(9)Set和Map集合

(10)Promise对象

(11)class定义类

箭头函数中this的指向

箭头函数:箭头函数本身是没有this和arguments的,在箭头函数中引用this实际上是调用的是定义时的上一层作用域的this。 这里强调的是上一层作用域,是因为对象是不能形成独立的作用域的。

例如:

    var obj = {
    	say: function() {
    		var f1 = ()=>{
    		    console.log("1111",this);	
    		}
    		f1();
    	}
    }
    var o = obj.say;

o();//f1执行时,say函数指向window,所以f1中的this指向window obj.say();//f1执行时,say函数指向obj,所以f1中的this指向obj;

    var ojb = {
        pro: {
            getPro: ()=>{
                console.log(this);
            }
        }
    }

obj.pro.getPro();//this指向的是window,因为箭头函数定义时,getPro的上一级是pro,是一个对象,不能形成单独的作用域,故指向window。

参考:箭头函数中的this指向

blog.csdn.net/sinat_36263…

ES6新增了那些关于数组的方法

  1. Array.from 这个东西就是把一些集合,或者长的像数组的伪数组转换成真的数组,比如arguments,js选择器找到dom集合,还有对象模拟的数组。

    var obj = {'0' : 1,length : 1}
    Array.from(obj / arguments / 伪数组) //返回的是一个数组
    

    Array.from还有第二个参数,是一个回调函数,功能类似map Array.from( [1, 2, 3], item => item * 2 )

  2. Array.of

    Array.of(1, 2, 3, 4) //把参数合并成一个数组返回,如果参数为空,则返回一个空数组
    
  3. find/findIndex

    //find 返回数组中第一个符合条件的元素, findIndex返回索引
    
     [1, 2, 3, 4, 5].find(function(item){ return item > 3 }
    
  4. fill

    数组中的每一个元素替换成指定值
    
    let arr = [1, 2, 3, 4] arr.fill(5) //arr全变成了5
    
    指定范围替换
    
    arr.fill(6, 1, 3)  //arr=[1,6,6,4]
    
  5. entries/keys/values

    let arr=['a', 'b', 'c'] for(let key of arr.keys()){} //取键 
    
    for(let value of arr.values()){} //取值
    
    for(let [key, value] of arr.entries()){} //都取
    
  6. includes

    var a = function(){} 
    
    [1, 2, 3, 4, a].includes(a) //true
    

var和let的区别

let关键字声明变量,只要遇到大括号,就形成作用域。

我们把let关键字,形成的作用域,叫做块级作用域。

let命令的特性

(1)let不存在变量提升

var命令会发生”变量提升“现象,即变量可以在声明之前使用,值为undefined。

let命令不存在变量提升的行为,它所声明的变量一定要在声明后使用,否则报错。

(2)暂时性死区

只要块级作用域内存在let命令声明变量之前,该变量都是不可用的。

这在语法上,称为“暂时性死区” temporal dead zone,简称 TDZ。

(3)不允许重复声明

let不允许在相同作用域内,重复声明同一个变量。

const命令

const声明的变量不得改变值,这意味着,const一旦声明变量,就必须立即初始化,不能留到以后赋值。

const声明一个只读的常量。一旦声明,常量的值就不能改变。

const声明的常量可以更改值吗

const a = [1, 2, 3];
        a.push(4); 
        console.log(a)// 输出[1,2,3,4]

const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。因此,将一个对象声明为常量必须非常小心。

const foo = {};

// 为 foo 添加一个属性,可以成功
foo.prop = 123;
foo.prop // 123

// 将 foo 指向另一个对象,就会报错
foo = {}; // TypeError: "foo" is read-only

上面代码中,常量foo储存的是一个地址,这个地址指向一个对象。不可变的只是这个地址,即不能把foo指向另一个地址,但对象本身是可变的,所以依然可以为其添加新属性。

参考:let 和 const 命令

es6.ruanyifeng.com/#docs/let

let const var 的区别?

1、let是es6新增的声明变量的方式 ,其特点是: (1)作用域是块级作用域(在ES6之前,js只存在函数作用域以及全局作用域)

 if(1){
  let a=1;
  console.log(a)
}

(2)不存在变量声明提前;

 console.log(b); //ReferenceError: b is not defined

 let b=2;

(3) 不能重复定义

 let a=1;
 let a=2;

console.log(a);//Identifier 'a' has already been declared

(4)存在暂时性死区:可以这样来理解

var a=1;
if(1){
 console.log(a); 
  let a=2;
}

① 在一个块级作用域中,变量唯一存在,一旦在块级作用域中用let声明了一个变量,那么这个变量就唯一属于这个块级作用域,不受外部变量的影响;

② 无论在块中的任何地方声明了一个变量,那么在这个块级作用域中,任何使用这个名字的变量都是指这个变量,无论外部是否有其他同名的全局变量;

③ 暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。

④ 暂时性死区的意义:让我们标准化代码。将所有的变量的声明放在作用域的最开始。

2、 const一般用来声明常量,且声明的常量是不允许改变的,只读属性,因此就要在声明的同时赋值。const与let一样,都是块级作用域,存在暂时性死区,不存在变量声明提前,不允许重复定义

const A=1;(重新给常量A赋值会报错 )A=3;// Uncaught TypeError: Assignment to constant variable.(错误:赋值给常量)

3、 var 是es6之前 js用来声明变量的方法,其特性是: (1)var的作用域是函数作用域,在一个函数内利用var声明一个变量,则这个变量只在这个函数内有效

function test(){
 var a=1;

console.log(a);//函数未调用 输出的是undefined  函数调用输出的是1 

}

console.log(a);//ReferenceError:a is not defined

(2)存在变量声明提前(虽然变量声明提前,但变量的赋值并没有提前,因此下面的程序不会报错,但a的值为undefined)

function test(){
 console.log(a);//undefined
 var a=3/a=3(隐式声明)

}

参考:let,const以及var三者的区别,面试经常会问哦!

blog.csdn.net/lydia11111/…

第七种数据类型Symbol

www.jianshu.com/p/f40a77bbd…

Symbol函数会生成一个唯一的值

可以理解为Symbol类型跟字符串是接近的

但每次生成唯一的值,也就是每次都不相等,

至于它等于多少,并不重要,这对于一些字典变量,比较有用

每个Symbol实例都是唯一的。因此,当你比较两个Symbol实例的时候,将总会返回false

应用场景:可以使用Symbol来作为对象属性名(key)

在这之前,我们通常定义或访问对象的属性时都是使用字符串,而现在,Symbol可同样用于对对象属性的定义和访问,当使用了Symbol作为对象的属性key后,Symbol类型的key是不能通过Object.keys()或者for…in来枚举的,它被包含在对象自身的属性名集合(property names)之中。所以,利用该特性,我们可以把一些不需要对外操作和访问的属性使用Symbol来定义。也正因为这样一个特性,当使用JSON.stringfy()将对象转换成JSON字符串的时候,Symbol属性也会被排除在输出内容之外,我们可以利用这一特点来更好的设计我们的数据对象,让"对内操作"和"对外选择性输出"变得更加优雅。

数组的解构赋值

请实现该缺少的内部代码,交换两个数的值

ES5:交换

        var res = (function(x,y){
            let temp = x;
            x= y;
            y = temp;
            return {
                x:x,
                y:y
            }
        })(1,2)
        console.log(res.x)
        console.log(res.y)

ES6:数组解构赋值

        var res = (function (x, y) {
                [y,x] = [x,y]
                return {
                    x: x,
                    y: y
                }
            })(1, 2)
        console.log(res.x)
        console.log(res.y)

参考:数组的扩展

es6.ruanyifeng.com/?search=%E6…

ES6中构造函数内super关键字的使用

super关键字用于访问和调用一个对象的父对象上的函数,是用来继承属性的 ES6 初始化对象的属性,在构造函数中使用时,super关键字将单独出现,并且必须在使用this关键字之前使用。

##JavaScript

TypeScript

TypeScript 是 JavaScript 的一个超集,支持 ECMAScript 6 标准。

const hello : string = "Hello World!" console.log(hello)

让js语言更加的严谨

JS里面新建函数的方法?

  1. 通过new
 var func = new Function("参数""函数体")

 var func = new Function(“num1”,“num2”,“return num1 + num2”)

 var result =  func(1,1console.log(result)//2
  1. 通过函数表达式
var sum = function(num1,num2){

         return num1 + num2

}

var result = sum(10,20console.log(result)
  1. 通过函数声明
function sum(num1,num2){

 //上面的逻辑

} 

DOM流

什么是事件流

事件流:描述的就是从页面中接受事件的顺序。

分有事件冒泡与事件捕获两种。

DOM事件流的三个阶段

  1. 事件捕获阶段

  2. 处于目标阶段

  3. 事件冒泡阶段

参考:DOM事件流

www.cnblogs.com/cmyoung/p/6…

阻止事件冒泡和默认行为的方法

event.preventDefault() 阻止默认行为

event.stopPropagation() 阻止冒泡

非W3C标准:

e.cancelBubble=true 阻止事件冒泡

return false 阻止默认行为

return false 代表即阻止事件冒泡,又阻止了默认行为

事件委托的实现流程

  1. 找到要添加事件的节点:父节点、或者祖父节点

  2. 把事件添加在找到的父节点上

  3. 通过target触发对象,找到复合条件的元素节点,进行后续操作

IE:window.event.srcElement 标准:event.targe

window.onload = function(){ 
    var oUl = document.getElementById('ull');
     var aLi = document.getElementsByTagName('li');

  oUl.onmouseover = function(ev){ 
     var event = ev||window.event;  // 获取event对象
     var target = ev.target || ev.srcElement; // 获取触发事件的目标对象
    
     if(target.nodeName.toLowerCase() == 'li'){  //判断目标对象是不是li
         target.style.background = 'red';
     }

  }
代码中加了一个标签名的判断,主要原因是如果不加判断,当你鼠标移入到父级oUL上面的时候,整个列表就会变红,这不是我们想要的结果,所以要判断一下。

target.nodeName 弹出的名字是大写的,所以需要转换大小写再比较。

事件委托的实现是利用事件冒泡的机制

事件冒泡:由里向外逐渐触发

事件捕获:由外向里逐渐触发

事件监听器

  1. 能够给一个事件,添加多个函数

  2. 精确地删除某一个事件某一个函数(通过函数名)

  3. 会对称重复添加的函数,自动去重

*事件监听器是用来解决传统事件绑定的问题,一般情况下(99%)用传统的事件绑定

数据类型

####Javascript的基本数据类型

Number:数字(整数,浮点数float)

Array:数组

Object:对象

布尔类型:Boolean a==b

特殊类型:Null、Undefined、NaN

console.log(1+”2"+"2") //122
console.log(“1"-'2'+'2') //-12
console.log(‘A'+"B"+"2") //ab2
console.log(“A”-"B"+2)//NaN

JavaScript创建对象的几种方式

工厂方式,构造函数方式,原型模式,混合构造函数原型模式,动态原型方式

null和undefined的区别

null == undefined //true

null !==undefined

undefined:

(1)变量被声明了,但没有赋值时,就等于undefined。

(2) 调用函数时,应该提供的参数没有提供,该参数等于undefined。

(3)对象没有赋值的属性,该属性的值为undefined。

(4)函数没有返回值时,默认返回undefined。

null是空值,解除一段关系,把对象赋值成一个空指针,避免内存泄漏,不用的DOM对象一般指向为空,例如:arr = null,不占用内存。

数据类型不同:

typeof (undefined):undefined

typeof(null):object

补充:0和null的区别

0是一个数值,是自然数,有大小

0不等于null

call和apply和bind的区别

三者都是强制改变this的指向

在说区别之前还是先总结一下三者的相似之处: 1、都是用来改变函数的this对象的指向的。 2、第一个参数都是this要指向的对象。 3、都可以利用后续参数传参。

传参数的区别:

参数形式不同,call(obj, pra, pra)后面是单个参数。apply(obj, [args])后面是数组。

Object.call(this,obj1,obj2,obj3) call需要一个一个传参

Object.apply(this,arguments) apply可以传递数组

call与apply都属于Function.prototype的一个方法,所以每个function实例都会有call/apply属性。fn.call,fn是函数,函数下面有一个call方法,fn调用。两者传递参数不同,call函数第一个参数都是要传入当前对象的对象,apply不是,apply传入的是一个参数数组,也就是将多个参数组合成为一个数组传入。Call传入则是直接的参数列表,Call方法可以将一个函数的对象上下文从初始的上下文改成由thisObj指定的新对象。

image

call和apply立即修改this,Bind绑定的时候不会立马去执行,当函数调用的时候bind才会执行。

参考:javascript中apply、call和bind的区别

www.cnblogs.com/cosiray/p/4…

break和continue的区别

break 终止当前循环的

continue 跳过这次循环,直接进入下一次循环。

声明提升

程序运行之前,有一个预编译。

预编译,会将代码从头看到尾,将需要的资源(内存)准备好。

然后才开始运动。

代码运行结果

var a;
alert(typeof a);
alert(b); //报错
var c = null; 
alert(typeof c) 

运行结果:Undefined/报错:b is not defined

var a;
alert(typeof a);
alert(typeof b); //undefined 
var c = null; 
alert(typeof c) 

运行结果:Undefined/undefined/object

typeof:即使你没有定义这个变量也不会报错 只会弹出undefined

alert(typeof typeof undefined) // string

运行结果:string

var bb = 1;
        function aa(bb) {
            bb = 2;
            alert(bb);
        }
        aa(bb); //2
        alert(bb);//1 没有更改全局变量

运行结果:2 1

var bb = 1;
        function aa() {
            bb = 2;
            alert(bb);
        }
        aa(bb); //2
        alert(bb);//1 没有更改全局变量

运行结果:2 2

如果外部不传入bb,相当于你又声明了一个bb,所以结果是2 2

function Foo() {
            var i = 0;
            return function () {
                console.log(i++);
            }
        }
        var f1 = Foo();
        f2 = Foo();
        f1();//0 f1执行 打印i的值 i++是后+
        f1();//1 f1维护自己的i
        f2();//0 f2是一个新的函数 拿到的是新的i变量

运行结果:0 1 0

已知前端页面中,有如下table。请添加用户交互效果,当用户点击该表格的某一行的时候,该行背景色设置为红色的代码。

<table id="t1">
		<tr></tr>
		<tr></tr>
		...
		<tr></tr>
</table>

用jq来实现:让当前点击的对象改变背景颜色

        var trDoms = document.getElementsByTagName("tr")
        for (var i = 0; i < trDoms.length; i++) {
            trDoms[i].onclick = function () {
                this.style.background = "red"
                console.log(i)
            }
        }

运行结果:3 3 3

想实现打印结果为:0 1 2,应如何操作?

第一种方法:获取对应索引

        var trDoms = document.getElementsByTagName("tr")
        for (var i = 0; i < trDoms.length; i++) {
            trDoms[i].index = i;
            trDoms[i].onclick = function () {
                this.style.background = "red"
                console.log(this.index)
            }
        }

第二种方法:闭包

        var trDoms = document.getElementsByTagName("tr")
        for (var i = 0; i < trDoms.length; i++) {
            trDoms[i].onclick = function (i) {
                return function(){
                    console.log(i)
                }
            }(i)
        }

第三种方法:var改成let作用域

				var trDoms = document.getElementsByTagName("tr")
        for (let i = 0; i < trDoms.length; i++) {
            trDoms[i].onclick = function () {
                console.log(i)
            }
        }

请问以下结果会输出什么

        for (var i = 0; i < 5; i++) {
            console.log(i);
        }// 0 1 2 3 4

        for (var i = 0; i < 5; i++) {
            setTimeout(function () {
                console.log(i);
            }, 1000 * i);
        } // 异步打印55

        for (var i = 0; i < 5; i++) {
            (function (i) {
                setTimeout(function () {
                    console.log(i);
                }, 1000 * i);
            })(i);
        } // 异步打印 0 1 2 3 4 (闭包写法)

        for (var i = 0; i < 5; i++) {
            (function () {
                setTimeout(function () {
                    console.log(i);
                }, 1000 * i);
            })(i);
        } // 打印55(function不传ii就是全局的i)

        for (var i = 0; i < 5; i++) {
            setTimeout((function () {
                console.log(i);
            })(i), 1000 * i);
        } // 0 1 2 3 4
        var myObject = {
            foo: "bar",
            func: function () {
                var self = this;
                console.log("outer func:",this.foo);//bar
                console.log("outer func:",self.foo);//bar
                (function () {
                    console.log("inner func",this.foo); //undefined 即时函数中this指向window
                    console.log("inner func",self.foo);	//bar		
                })();
            }
        }
        myObject.func();
        (function () {
            var a = b = 1; //声明局部变量 外部访问不到局部变量
        })();
        console.log(typeof a); //undefined 外部访问不到内部声明的局部变量
        console.log(typeof b); //number
function DateDemo() {
            for (var i = 0, j = 0; i < 10, j < 6; i++ , j++) {
                k = i + j; 
            }
            return k;
        }
        var t = DateDemo()
        console.log(t)//10:5+5

设计模式

设计模式的分类

总体来说设计模式分为三大类:

创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式

结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

其实还有两类:并发型模式和线程池模式。

闭包

闭包是什么

闭包是指有权访问另一个函数作用域中变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量,利用闭包可以突破作用链域,将函数内部的变量和方法传递到外部。

闭包的特性

  1. 函数内再嵌套函数
  2. 内部函数可以引用外层的参数和变量
  3. 参数和变量不会被垃圾回收机制回收

闭包的作用

闭包用的多的两个作用:读取函数内部的变量值;让这些变量值始终保存着(在内存中)。

同时需要注意的是:闭包慎用,不滥用,不乱用,由于函数内部的变量都被保存在内存中,会导致内存消耗大。

闭包的优点

  1. 既不声明全局变量

  2. 又能进行累加

  3. 希望一个变量常驻在内存当中,避免全局变量污染

  4. 可以声明私有成员

闭包的缺点

滥用闭包函数会造成内存泄露,因为闭包中引用到的包裹函数中定义的变量都永远不会被释放,所以我们应该在必要的时候,及时释放这个闭包函数。

用一个案例解释闭包的作用

执行say667()后,say667()闭包内部变量会存在,而闭包内部函数的内部变量不会存在。使得Javascript的垃圾回收机制GC不会收回say667()所占用的资源,因为say667()的内部函数的执行需要依赖say667()中的变量。这是对闭包作用的非常直白的描述。

  function say667() {
    // Local variable that ends up within closure
     var num = 666;
     var sayAlert = function() { alert(num);
    }
     num++;
     return sayAlert;
  }
 var sayAlert = say667();
 sayAlert() //执行结果应该弹出的667  

参考:

什么是闭包(closure),为什么要用它?

www.cnblogs.com/lqzweb/p/62…

什么是闭包?写一个简单的闭包?

答:我的理解是,闭包就是能够读取其他函数内部变量的函数。在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁.

image

闭包的主要应用场景

  1. 匿名自执行函数
  2. 缓存
  3. 封装
  4. 实现类和继承

参考; javascript中的闭包主要在哪些地方用到

zhidao.baidu.com/question/74…

为什么要使用匿名函数自执行函数,好处是什么

定义变量时需要加上var,否则会默认添加到全局对象的属性上,或者别的函数可能误用这些变量;或者造成全局对象过于庞大,影响访问速度(因为变量的取值是需要从原型链上遍历的), 实际中有的函数只调用一次使用自执行函数也是很好的。

我们创建了一个匿名的函数,并立即执行它,由于外部无法引用它内部的变量,因此在执行完后很快就会被释放,关键是这种机制不会污染全局对象。

哪些操作会造成内存泄漏

内存泄漏指任何对象在您不再拥有或需要它之后仍然存在。

垃圾回收器定期扫描对象,并计算引用了每个对象的其他对象的数量。如果一个对象的引用数量为 0(没有其他对象引用过该对象),或对该对象的惟一引用是循环的,那么该对象的内存即可回收。

  1. setTimeout 的第一个参数使用字符串而非函数的话,会引发内存泄漏。

  2. 闭包

  3. 控制台日志

  4. 循环(在两个对象彼此引用且彼此保留时,就会产生一个循环)

参考:

Js内存泄漏及解决方案

www.cnblogs.com/carekee/art…

JS哪些操作会造成内存泄漏?

www.jianshu.com/p/763ba9562…

Javascript中的垃圾回收机制

在Javascript中,如果一个对象不再被引用,那么这个对象就会被GC回收。如果两个对象互相引用,而不再被第3者所引用,那么这两个互相引用的对象也会被回收。因为函数a被b引用,b又被a外的c引用,这就是为什么 函数a执行后不会被回收的原因。

JS 垃圾回收机制的原理

内存泄漏:不再用到的内存,没有及时释放,就叫做内存泄漏。解决内存的泄露,垃圾回收机制会定期找出那些不再用到的内存(变量),然后释放其内存。

现在各大浏览器通常采用的垃圾回收机制有两种方法:标记清除,引用计数。

  1. 标记清除:js中最常用的垃圾回收方式就是标记清除。当变量进入环境时,例如,在一个函数中声明一个变量,就将这个变量标记为"进入环境",从逻辑上讲,永远不能释放进入环境变量所占用的内存,因为只要执行流进入相应的环境,就可能会用到它们。而当变量离开环境时,则将其标记为"离开环境"。

    function test(){
    var a = 10; *//*被标记*"*进入环境*"*
    var b = "hello"; *//*被标记*"*进入环境*"*
    test(); *//*执行完毕后之后,*a**b*又被标记*"*离开环境*"*,被回收
    
  2. 引用计数:语言引擎有一张"引用表",保存了内存里面所有资源(通常是各种值)的引用次数。如果一个值的引用次数是0,就表示这个值不再用到了,因此可以将这块内存释放。如果一个值不再需要了,引用数却不为0,垃圾回收机制无法释放这块内存,从而导致内存泄漏。

    const arr = [1,2,3,4];
    console.log("hello world")
    

上面的代码中,数组[1,2,3,4]是一个值,会占用内存。变量arr是仅有的对这个值的引用,因此引用次数为1。尽管后面的代码没有用到arr,它是会持续占用内存。

如果增加一行代码,解除arr对[1,2,3,4]引用,这块内存就可以被垃圾回收机制释放了。

let arr = [1,2,3,4];

console.log("hello world");

arr = null;

上面代码中,arr重置为null,就解除了对[1,2,3,4]的引用,引用次数变成了0,内存就可以释放出来了。

因此,并不是说有了垃圾回收机制,程序员就轻松了。你还是需要关注内存占用:那些很占空间的值,一旦不再用到,你必须检查是否还存在对它们的引用。如果是的话,就必须手动解除引用

JavaScript this指针、闭包、作用域

this:指向调用上下文

闭包:内层作用域可以访问外层作用域的变量

作用域:定义一个函数就开辟了一个局部作用域,整个js执行环境有一个全局作用域