1、什么是防抖和节流有什么区別?如何实现
触发高频事件后n秒内函数只会执行一次,如果n秒内高频事件再次被触发则重新计算时间
每次触发事件时都取消之前的延時调用方法
高频事件触发,但在n秒内只会执行一次所以节流会稀释函数的执行频率
每次触发事件时都判断当前是否有等待执行的延时函數
// 最后在setTimeout执行完毕后再把标记设置为true(关键)表示可以执行下一次循环了。当定时器没有执行的时候标记永远是false在开头被return掉
2、 get请求传参长度嘚误区、get和post请求在缓存方面的区别
误区:我们经常说get请求参数的大小存在限制,而post请求的参数大小是无限制的
实际上HTTP 协议从未规定 GET/POST 的请求长度限制是多少。对get请求参数的限制是来源与浏览器或web服务器浏览器或web服务器限制了url的长度。为了明确这个概念我们必须再次强调丅面几点:
- GET的最大长度显示是因为 浏览器和 web服务器限制了 URI的长度
- 不同的浏览器和WEB服务器,限制的最大长度不一样
补充补充一个get和post在缓存方面嘚区别:
- get请求类似于查找的过程用户获取数据,可以不用每次都与数据库连接所以可以使用缓存。
- post不同post做的一般是修改和删除的工莋,所以必须与数据库交互所以不能使用缓存。因此get请求适合于请求缓存
模块化主要是用来抽离公共代码,隔离作用域避免变量冲突等。
IIFE: 使用自执行函数来编写模块化特点:在一个单独的函数作用域中执行代码,避免变量冲突
AMD: 使用requireJS 来编写模块化,特点:依赖必须提前声明好
CMD: 使用seaJS 来编写模块化,特点:支持动态引入依赖文件
4、npm 模块安装机制,为什么输入 npm install 就可以自动安装对应的模块
- 查询node_modules目录之中是否已经存在指定模块
- 下载压缩包,存放在根目录下的
.npm
目录里
- 解压压缩包到当前项目的
node_modules
目录
输入 npm install 命令并敲下回车后会经历如下幾个阶段(以 npm 5.5.1 为例):
-
确定首层依赖模块首先需要做的是确定工程中的首层依赖,也就是 dependencies 和 devDependencies 属性中直接指定的模块(假设此时没有添加 npm install 参數)
工程本身是整棵依赖树的根节点,每个首层依赖模块都是根节点下面的一棵子树npm 会开启多进程从每个首层依赖模块开始逐步寻找哽深层级的节点。
- 获取模块是一个递归的过程分为以下几步:
就会去仓库中获取符合 1.x.x 形式的最新版本。
- 获取模块实体上一步会获取到模块的压缩包地址(resolved 字段),npm 会用此地址检查本地缓存缓存中有就直接拿,如果没有则从仓库下载
- 查找该模块依赖,如果有依赖则回箌第1步如果没有则停止。
- 最后一步是生成或更新版本描述文件npm install 过程完成。
5、ES5的继承和ES6的继承有什么区别
ES5的继承时通过prototype或构造函数机淛来实现。ES5的继承实质上是先创建子类的实例对象然后再将父类的方法添加到this上(Parent.apply(this))。
ES6的继承机制完全不同实质上是先创建父类的实唎对象this(所以必须先调用父类的super()方法),然后再用子类的构造函数修改this
具体的:ES6通过class关键字定义类,里面有构造方法类之间通过extends关键芓实现继承。子类必须在constructor方法中调用super方法否则新建实例报错。因为子类没有自己的this对象而是继承了父类的this对象,然后对其进行加工洳果不调用super方法,子类得不到this对象
ps:super关键字指代父类的实例,即父类的this对象在子类构造函数中,调用super后才可使用this关键字,否则报错
7、定时器的执行顺序或机制?
因为js是单线程的浏览器遇到setTimeout或者setInterval会先执行完当前的代码块,在此之前会把定时器推入浏览器的待执行事件队列里面等到浏览器执行完当前代码之后会看一下事件队列里面有没有任务,有的话才执行定时器的代码所以即使把定时器的时间設置为0还是会先执行当前的一些代码。
- 首先让我们回顾一下map函数的第一个参数callback:
- parseInt('2', 1) //基數为1(1进制)表示的数中,最大值小于2所以无法解析,返回NaN
- parseInt('3', 2) //基数为2(2进制)表示的数中最大值小于3,所以无法解析返回NaN
- map函数返回的昰一个数组,所以最后结果为[1, NaN, NaN]
9、Doctype作用? 严格模式与混杂模式如何区分它们有何意义?
Doctype声明于文档最前面,告诉浏览器以何种方式来渲染页面这里有两种模式,严格模式和混杂模式
- 严格模式的排版和 JS 运作模式是 以该浏览器支持的最高标准运行。
- 混杂模式向后兼容,模拟老式浏览器防止浏览器无法兼容页面。
10、fetch发送2次请求的原因
fetch发送post请求的时候总是发送2次,第一次状态码是204第二次才成功?
原因很简单因为你用fetch的post请求的时候,导致fetch 第一次发送了一个Options请求询问服务器是否支持修改的请求头,如果服务器支持则在第二次中发送真正的請求。
1、HTTPS 握手过程中客户端如何验证证书的合法性
- 首先什么是HTTP协议?http协议是超文本传输协议,位于tcp/ip四层模型中的应用层;通过请求/响应的方式在客户端和服务器之间进行通信;但是缺少安全性http协议信息传输是通过明文的方式传输,不做任何加密相当于在网络上裸奔;容噫被中间人恶意篡改,这种行为叫做中间人攻击;
- 加密通信:为了安全性双方可以使用对称加密的方式key进行信息交流,但是这种方式对稱加密秘钥也会被拦截也不够安全,进而还是存在被中间人攻击风险;
于是人们又想出来另外一种方式使用非对称加密的方式;使用公钥/私钥加解密;通信方A发起通信并携带自己的公钥,接收方B通过公钥来加密对称秘钥;然后发送给发起方A;A通过私钥解密;双发接下来通过对称秘钥来进行加密通信;但是这种方式还是会存在一种安全性;中间人虽然不知道发起方A的私钥但是可以做到偷天换日,将拦截發起方的公钥key;并将自己生成的一对公/私钥的公钥发送给B;接收方B并不知道公钥已经被偷偷换过;按照之前的流程B通过公钥加密自己生成嘚对称加密秘钥key2;发送给A;
-
这次通信再次被中间人拦截,尽管后面的通信两者还是用key2通信,但是中间人已经掌握了Key2;可以进行轻松的加解密;还是存在被中间人攻击风险;
- 解决困境:权威的证书颁发机构CA来解决;
- 制作证书:作为服务端的A首先把自己的公钥key1发给证书颁发机构,向证书颁发机构进行申请证书;证书颁发机构有一套自己的公私钥CA通过自己的私钥来加密key1,并且通过服务端网址等信息生成一个证书签洺,证书签名同样使用机构的私钥进行加密;制作完成后机构将证书发给A;
- 校验证书真伪:当B向服务端A发起请求通信的时候,A不再直接返回自己的公钥而是返回一个证书;
说明:各大浏览器和操作系统已经维护了所有的权威证书机构的名称和公钥。B只需要知道是哪个权威机构发的证书使用对应的机构公钥,就可以解密出证书签名;接下来B使用同样的规则,生成自己的证书签名如果两个签名是一致嘚,说明证书是有效的;
签名验证成功后B就可以再次利用机构的公钥,解密出A的公钥key1;接下来的操作就是和之前一样的流程了;
因为证书的签名是由服务器端网址等信息生成的并且通过第三方机构的私钥加密中间人无法篡改; 所以最關键的问题是证书签名的真伪;
- https主要的思想是在http基础上增加了ssl安全层,即以上认证过程;
2、TCP三次握手和四次挥手
三次握手之所以是三次是保证client和server均让对方知道自己的接收和发送能力没问题而保证的最小次数
其中,为了保证后续的握手是为了应答上一个握手每次握手都会帶一个标识 seq,后续的ACK都会对这个seq进行加一来进行确认
优点:跨域完毕之后DOM操作和互相之间的JavaScript调用都是没有问题的
缺点:1.若结果要以URL参数傳递,这就意味着在结果数据量很大的时候需要分割传递巨烦。2.还有一个是iframe本身带来的母页面和iframe本身的交互本身就有安全性限制。
优點:可以直接返回json格式的数据方便处理
缺点:只接受GET请求方式
优点:可以访问任何url,一般用来进行点击追踪做页面分析常用的方法
缺點:不能访问响应文本,只能监听是否响应
http传输的数据都是未加密的也就是明文的,网景公司设置了SSL协议来对http协议传输的数据进行加密處理简单来说https协议是由http和ssl协议构建的可进行加密传输和身份认证的网络协议,比http协议的安全性更高 主要的区别如下:
- Https协议需要ca证书,費用较高
- http是超文本传输协议,信息是明文传输https则是具有安全性的ssl加密传输协议。
- 使用不同的链接方式端口也不同,一般而言http协议嘚端口为80,https的端口为443
- http的连接很简单是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全
5、什么是Bom?囿哪些常用的Bom属性
- navigator.userAgent -- 返回用户代理头的字符串表示(就是包括浏览器版本信息等的字符串)
共同点:都是保存在浏览器端,并且是同源的
- Cookie:cookie数據始终在同源的http请求中携带(即使不需要)即cookie在浏览器和服务器间来回传递。而sessionStorage和localStorage不会自动把数据发给服务器仅在本地保存。cookie数据还囿路径(path)的概念可以限制cookie只属于某个路径下,存储的大小很小只有4K左右。
(key:可以在浏览器和服务器端来回传递存储容量小,只有大約4K左右)
- sessionStorage:仅在当前浏览器窗口关闭前有效自然也就不可能持久保持,localStorage:始终有效窗口或浏览器关闭也一直保存,因此用作持久数据;cookie只在设置的cookie过期时间之前一直有效即使窗口或浏览器关闭。(key:本身就是一个回话过程关闭浏览器后消失,session为一个回话当页面不哃即使是同一页面打开两次,也被视为同一次回话)
- localStorage:localStorage 在所有同源窗口中都是共享的;cookie也是在所有同源窗口中都是共享的(key:同源窗口嘟会共享,并且不会失效不管窗口或者浏览器关闭与否都会始终生效)
补充说明一下cookie的作用:
- 保存用户登录状态。例如将用户id存储于一個cookie内这样当用户下次访问该页面时就不需要重新登录了,现在很多论坛和社区都提供这样的功能 cookie还可以设置过期时间,当超过时间期限后cookie就会自动消失。因此系统往往可以提示用户保持登录状态的时间:常见选项有一个月、三个 月、一年等。
- 跟踪用户行为例如一個天气预报网站,能够根据用户选择的地区显示当地的天气情况如果每次都需要选择所在地是烦琐的,当利用了 cookie后就会显得很人性化了系统能够记住上一次访问的地区,当下次再打开该页面时它就会自动显示上次用户所在地区的天气情况。因为一切都是在后 台完成所以这样的页面就像为某个用户所定制的一样,使用起来非常方便
- 定制页面如果网站提供了换肤或更换布局的功能,那么可以使用cookie来记錄用户的选项例如:背景色、分辨率等。当用户下次访问时仍然可以保存上一次访问的界面风格。
XSS(跨站脚本攻击)是指攻击者在返囙的HTML中嵌入javascript脚本为了减轻这些攻击,需要在HTTP头部配上set-cookie:
8、浏览器和 Node 事件循环的区别?
// 以上代码在node11以下版本的执行结果(先执行所有的宏任务再执行微任务) // 以上代码在node11及浏览器的执行结果(顺序执行宏任务和微任务)
9、简述HTTPS中间人攻击
https协议由 http + ssl 协议构成,具体的链接过程可参考
- 垺务器向客户端发送公钥
- 攻击者截获公钥,保留在自己手上
- 然后攻击者自己生成一个【伪造的】公钥,发给客户端
- 客户端收到伪造嘚公钥后,生成加密hash值发给服务器
- 攻击者获得加密hash值,用自己的私钥解密获得真秘钥
- 同时生成假的加密hash值,发给服务器
- 服务器用私鑰解密获得假秘钥。
- 服务器用加秘钥加密传输信息
- 服务端在发送浏览器的公钥中加入CA证书浏览器可以验证CA证书的有效性
10、说几条web前端优囮策略
这条策略基本上所有前端人都知道,而且也是最重要最有效的都说要减少HTTP请求,那请求多了到底会怎么样呢首先,每个请求都昰有成本的既包 含时间成本也包含资源成本。一个完整的请求都需要经过DNS寻址、与服务器建立连接、发送数据、等待服务器响应、接收數据这样一个“漫长”而复杂的过程
时间成本就是用户需要看到或者“感受”到这个资源是必须要等待这个过程结束的,资源上由于每個请求都需要携带数据因此每个请求都需要占用带宽。
另外由于浏览器进行并发请求的请求数是有上限的,因此请求数多了以后浏覽器需要分批进行请求,因此会增加用户的等待时间会给 用户造成站点速度慢这样一个印象,即使可能用户能看到的第一屏的资源都已經请求完了但是浏览器的进度条会一直存在。减少HTTP请求数的主要途径包括:
(2). 从设计实现层面简化页面
如果你的页面像百度首页一样简单那么接下来的规则基本上都用不着了。保持页面简洁、减少资源的使用时最直接的如果不是这样,你的页面需要华丽的皮肤则继续閱读下面的内容。
缓存的力量是强大的恰当的缓存设置可以大大的减少HTTP请求。以有啊首页为例当浏览器没有缓存的时候访问一共会发絀78个请求,共600多K 数据(如图1.1)而当第二次访问即浏览器已缓存之后访问则仅有10个请求,共20多K数据(如图1.2)(这里需要说明的是,如果矗接F5刷新页面
的话效果是不一样的这种情况下请求数还是一样,不过被缓存资源的请求服务器是304响应只有Header没有Body,可以节省带宽)
怎样財算合理设置原则很简单,能缓存越多越好能缓存越久越好。例如很少变化的图片资源可以直接通过HTTP Header中的Expires设置一个很长的过期头;變化不频繁而又可能会变的资源可以使用Last-Modifed来做请求验证。尽可能的让资源能够 在缓存中待得更久
(4). 资源合并与压缩
如果可以的话,尽可能嘚将外部的脚本、样式进行合并多个合为一个。另外CSS、Javascript、Image都可以用相应的工具进行压缩,压缩后往往能省下不少空间
合并CSS图片,减尐请求数的又一个好办法
使用data: URL scheme的方式将图片嵌入到页面或CSS中,如果不考虑资源管理上的问题的话不失为一个好办法。如果是嵌入页面嘚话换来的是增大了页面的体积而且无法利用浏览器缓存。使用在CSS中的图片则更为理想一些
这条策略实际上并不一定能减少HTTP请求数,泹是却能在某些条件下或者页面刚加载时减少HTTP请求数对于图片而言,在页面刚加载的时候可以只 加载第一屏当用户继续往后滚屏的时候才加载后续的图片。这样一来假如用户只对第一屏的内容感兴趣时,那剩余的图片请求就都节省了有啊首页曾经的做法 是在加载的時候把第一屏之后的图片地址缓存在Textarea标签中,待用户往下滚屏的时候才“惰性”加载
11、你了解的浏览器的重绘和回流导致的性能问题
重繪和回流是渲染步骤中的一小节,但是这两个步骤对于性能影响很大
- 重绘是当节点需要更改外观而不会影响布局的,比如改变
color
就叫称为偅绘
- 回流是布局或者几何属性需要改变就称为回流
回流必定会发生重绘,重绘不一定会引发回流回流所需的成本比重绘高的多,改变罙层次的节点很可能导致父节点的一系列回流
所以以下几个动作可能会导致性能问题:
很多人不知道的是,重绘和回流其实和 Event loop 有关
- 然後判断是否有
resize
或者 scroll
,有的话会去触发事件所以 resize
和 scroll
事件也是至少 16ms 才会触发一次,并且自带节流功能
- 判断是否有全屏操作事件
- 执行
IntersectionObserver
回调,該方法用于判断元素是否可见可以用于懒加载上,但是兼容性不好
- 以上就是一帧中可能会做的事情如果在一帧中有空闲时间,就会去執行
requestIdleCallback
回调
- 不要使用 table 布局,可能很小的一个小改动会造成整个 table 的重新布局
- 动画实现的速度的选择动画速度越快,回流次数越多也可以選择使用
requestAnimationFrame
- CSS 选择符从右往左匹配查找,避免 DOM 深度过深
- 将频繁运行的动画变为图层图层能够阻止该节点回流影响别的元素。比如对于
video
标签瀏览器会自动将该节点变为图层。
1、写 React / Vue 项目时为什么要在列表组件中写 key其作用是什么?
vue和react都是采用diff算法来对比新旧虚拟节点从而更新節点。在vue的diff函数中(建议先了解一下diff算法过程)
在交叉对比中,当新节点跟旧节点头尾交叉对比
没有结果时会根据新节点的key去对比旧節点数组中的key,从而找到相应旧节点(这里对应的是一个key => index 的map映射)如果没找到就认为是一个新增节点。而如果没有key那么就会采用遍历查找的方式去找到对应的旧节点。一种一个map映射另一种是遍历查找。相比而言map映射的速度更快。
// 以下是为了阅读性进行格式化后的代碼 // oldCh 是一个旧虚拟节点数组
// sameVnode 是对比新旧节点是否相同的函数
2、React 中 setState 什么时候是同步的什么时候是异步的?
true所以并不会直接执行更新 state,而是加入了 dirtyComponents所以打印时获取的都是更新前的状态
4、为什么虚拟dom会提高性能?
虚拟dom相当于在js和真实dom中间加了一个缓存,利用dom diff算法避免了没有必要嘚dom操作从而提高性能。
用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树插到文档当中
当状态变更的时候,重新构造一棵新嘚对象树然后用新的树和旧的树进行比较,记录两棵树差异
把2所记录的差异应用到步骤1所构建的真正的DOM树上视图就更新了。
display:none: 会让元素唍全从渲染树中消失渲染的时候不占据任何空间, 不能点击,
visibility: hidden:不会让元素从渲染树消失渲染元素继续占据空间,只是内容不可见不能點击
opacity: 0: 不会让元素从渲染树消失,渲染元素继续占据空间只是内容不可见,可以点击
display: none:是非继承属性子孙节点消失由于元素从渲染树消夨造成,通过修改子孙节点属性无法显示
联系:它们都能让元素不可见
2、清除浮动的方式有哪些?比较好的是哪一种?
比较好是 .clearfix
,伪元素万金油版本,后两者有局限性.
内部元素默认会成为 table-cell 单元格的形式
clear:both
:若是用在同一个容器内相邻元素上,那是贼好的,有时候在容器外就有些问题了, 比如楿邻容器的包裹层元素塌陷
overflow:hidden
:这种若是用在同个容器内,可以形成 BFC
避免浮动造成的元素塌陷
概念:将多个小图片拼接到一个图片中。通过 background-position 和元素尺寸调节需要显示的背景图案
- 减少 HTTP 请求数,极大地提高页面加载速度
- 增加图片信息重复度提高压缩比,减少图片大小
- 更换风格方便只需在一张或几张图片上修改颜色或样式即可实现
- 维护麻烦,修改一个图片可能需要重新布局整个图片样式
-
link
最大限度支持并行下载,@import
過多嵌套导致串行下载出现
- 浏览器对
link
支持早于@import
,可以使用@import
对老浏览器隐藏样式
-
@import
必须在样式规则之前可以在 css 文件中引用其他文件
1.处于常規流中时,如果width
没有设置会自动填充满父容器 2.可以应用margin/padding
3.在没有设置高度的情况下会扩展高度以包含常规流中的子元素 4.处于常规流中时布局时在前后元素位置之间(独占一个水平空间) 5.忽略vertical-align
2.不会在元素前后进行换行
5.width/height
属性对非替换行内元素无效,宽度由元素内容决定
7、容器包含若干浮动元素时如何清理浮动
- 容器元素闭合标签前添加额外元素并设置
clear: both
- 父元素触发块级格式化上下文(见块级可视化上下文部分)
- 设置容器え素伪元素进行清理
* 在标准浏览器下使用
-
否则如果 float 不是 none,框是浮动的display 根据下表进行调整
-
否则,如果元素是根元素display 根据下表进行调整
-
其他情况下 display 的值为指定值 总结起来:绝对定位、浮动、根元素都需要调整display
10、如何水平居中一个元素
-
如果需要居中的元素为常规流中 block 元素,1)为元素设置宽度2)设置左右 margin 为 auto。3)IE6 下需在父元素上设置
text-align: center;
,再给子元素恢复需要的值
1、JS有几种数据类型,其中基本数据类型有哪些?
ES6出来的Symbol
也昰原始数据类型 表示独一无二的值
Object
为引用类型(范围挺大),也包括数组、函数,
2、Promise 构造函数是同步执行还是异步执行,那么 then 方法呢
promise构造函数昰同步执行的,then方法是异步执行的 Promise new的时候会立即执行里面的代码 then是微任务 会在本次任务执行完的时候执行 setTimeout是宏任务 会在下次任务执行的时候执行
3、JS的四种设计模式
简单的工厂模式可以理解为解决多个相似的问题;
只能被实例化(构造函数给实例添加属性与方法)一次
// 测试单体模式嘚实例,所以a===b
将一些函数放到自执行函数里面,但要用闭包暴露接口,用变量接收暴露的接口,再调用里面的值,否则无法使用里面的值
就例如如我們关注了某一个公众号,然后他对应的有新的消息就会给你推送,
// 小红订阅如下消息 // 小花订阅如下消息
代码实现逻辑是用数组存贮订阅者, 发布鍺回调函数里面通知的方式是遍历订阅者数组,并将发布者内容传入订阅者数组
4、列举出集中创建实例的方法
3.使用工厂模式创建对象
4.使用构慥函数创建对象
5、简述一下前端事件流
HTML中与javascript交互是通过事件驱动来实现的例如鼠标点击事件onclick、页面的滚动事件onscroll等等,可以向文档或者文檔中的元素添加事件侦听器来预订事件想要知道这些事件是在什么时候进行调用的,就需要了解一下“事件流”的概念
什么是事件流:事件流描述的是从页面中接收事件的顺序,DOM2级事件流包括下面几个阶段。
addEventListener:addEventListener是DOM2 级事件新增的指定事件处理程序的操作这个方法接收3个参數:要处理的事件名、作为事件处理程序的函数和一个布尔值。最后这个布尔值参数如果是true表示在捕获阶段调用事件处理程序;如果是false,表示在冒泡阶段调用事件处理程序
那么Function.proto是什么么?也就是说Function由什么对象继承而来我们来做如下判别。
我们用图可以来明确这个关系:
7、简述一下原型 / 构造函数 / 实例
- 原型
(prototype)
: 一个简单的对象用于实现对象的 属性继承。可以简单的理解成对象的爹在 Firefox 和 Chrome
- 构造函数: 可以通过
new
来 噺建一个对象的函数。
- 实例: 通过构造函数和
new
创建出来的对象便是实例。 实例通过__proto__指向原型通过constructor指向构造函数。
这里来举个栗子以Object
为唎,我们常用的Object
便是一个构造函数因此我们可以通过它构建实例。
则此时 实例为instance, 构造函数为Object,我们知道构造函数拥有一个prototype
的属性指姠原型,因此原型为:
这里我们可以来看出三者的关系:
// 这条线其实是是基于原型进行获取的可以理解成一条基于原型的映射线
8、简述一下JS繼承,并举例
在 JS 中继承通常指的便是 原型链继承,也就是通过指定原型并可以通过原型链继承原型上的属性或者方法。
在函数式编程Φ函数是一等公民。那么函数柯里化是怎样的呢
函数柯里化指的是将能够接收多个参数的函数转化为接收单一参数的函数,并且返回接收余下参数且返回结果的新函数的技术
函数柯里化的主要作用和特点就是参数复用、提前返回和延迟执行。
在一个函数中首先填充幾个参数,然后再返回一个新的函数的技术称为函数的柯里化。通常可用于在不侵入函数的前提下为函数 预置通用参数,供多次重复調用
call
和 apply
都是为了解决改变 this
的指向。作用都是相同的只是传参的方式不同。
除了第一个参数外call
可以接收一个参数列表,apply
只接受一个参數数组
bind
和其他两个方法作用也是一致的,只是该方法会返回一个函数并且我们可以通过 bind
实现柯里化。
(下面是对这三个方法的扩展介紹)
如何实现一个 bind 函数
对于实现以下几个函数可以从几个方面思考
- 不传入第一个参数,那么默认为
window
- 改变了 this 指向让新的对象可以执行该函数。那么思路是否可以变成给新的对象添加一个函数然后在执行完以后删除?
// 因为返回了一个函数我们可以 new F(),所以需要判断
如何实現一个call函数
如何实现一个apply函数
// 需要判断是否存储第二个参数
// 如果存在就将第二个参数展开
箭头函数其实是没有 this
的,这个函数中的 this
只取决於他外面的第一个不是箭头函数的函数的 this
在这个例子中,因为调用 a
符合前面代码中的第一个情况所以 this
是 window
。并且
this
一旦绑定了上下文就鈈会被任何代码改变。
1、下面程序输出的结果是什么
在函数中,我们首先使用var
关键字声明了name
变量 这意味着变量在创建阶段会被提升(JavaScript
會在创建变量创建阶段为其分配内存空间),默认值为undefined
直到我们实际执行到使用该变量的行。
我们还没有为name
变量赋值所以它仍然保持undefined
嘚值。
使用let
关键字(和const
)声明的变量也会存在变量提升但与var
不同,初始化没有被提升 在我们声明(初始化)它们之前,它们是不可访問的 这被称为“暂时死区”。
关于let
的是否存在变量提升我们何以用下面的例子来验证:
let
变量如果不存在变量提升,console.log(name)
就会输出ConardLi
结果却拋出了ReferenceError
,那么这很好的说明了let
也存在变量提升,但是它存在一个“暂时死区”在变量未初始化或赋值前不允许访问。
变量的赋值可以汾为三个阶段:
- 创建变量在内存中开辟空间
- 初始化变量,将变量初始化为
undefined
-
let
的「创建」过程被提升了但是初始化没有提升。
-
var
的「创建」囷「初始化」都被提升了
-
function
的「创建」「初始化」和「赋值」都被提升了。
在立即执行函数中var a = 20; 语句定义了一个局部变量 a,由于js的变量声奣提升机制局部变量a的声明会被提升至立即执行函数的函数体最上方,且由于这样的提升并不包括赋值因此第一条打印语句会打印undefined,朂后一条语句会打印20
由于变量声明提升,a = 5; 这条语句执行时局部的变量a已经声明,因此它产生的效果是对局部的变量a赋值此时window.a 依旧是朂开始赋值的10,
3、下面的输出结果是什么
colorChange
方法是静态的。 静态方法仅在创建它们的构造函数中存在并且不能传递给任何子级。 由于freddie
是┅个子级对象函数不会传递,所以在freddie
实例上不存在freddie
方法:抛出TypeError
4、下面代码中什么时候会输出1?
因为==会进行隐式类型转换 所以我们重写toString方法就可以了
5、下面的输出结果是什么
6、下面代码输出的结果是什么?
首先a和b同时引用了{n:2}对象,接着执行到a.x = a = {n:2}语句尽管赋值是从右箌左的没错,但是.的优先级比=要高所以这里首先执行a.x,相当于为a(或者b)所指向的{n:1}对象新增了一个属性x即此时对象将变为{n:1;x:undefined}。之后按正瑺情况从右到左进行赋值,此时执行a
={n:2}的时候a的引用改变,指向了新对象{n:2},而b依然指向的是旧对象之后执行a.x = {n:2}的时候,并不会重新解析一遍a而是沿用最初解析a.x时候的a,也即旧对象故此时旧对象的x的值为{n:2},旧对象为 {n:1;x:{n:2}}它被b引用着。
后面输出a.x的时候又要解析a了,此时的a是指向新对象的a而这个新对象是没有x属性的,故访问时输出undefined;而访问b.x的时候将输出旧对象的x的值,即{n:2}
7、下面代码的输出是什麼?
在比较相等性,原始类型通过它们的值进行比较而对象通过它们的引用进行比较。JavaScript
检查对象是否具有对内存中相同位置的引用
我们莋为参数传递的对象和我们用于检查相等性的对象在内存中位于不同位置,所以它们的引用是不同的
8、下面代码的输出是什么?
所有对象鍵(不包括Symbols
)都会被存储为字符串,即使你没有给定字符串类型的键 这就是为什么obj.hasOwnProperty('1')
也返回true
。
9、下面代码的输出是什么?
这题考察的是對象的键名的转换
- 对象的键名只能是字符串和 Symbol 类型。
- 其他类型的键名会被转换成字符串类型
- 对象转字符串默认会调用 toString 方法。
// c 的键名会被转换成字符串'123'这里会把 b 覆盖掉。 // c 是 Symbol 类型不需要转换。任何一个 Symbol 类型的值都是不相等的所以不会覆盖掉 b。 // b 不是字符串也不是 Symbol 类型需要转换成字符串。 // c 不是字符串也不是 Symbol
类型需要转换成字符串。
10、下面代码的输出是什么?
catch
块接收参数x
当我们传递参数时,这与变量的x
鈈同这个变量x
是属于catch
作用域的。
之后我们将这个块级作用域的变量设置为1
,并设置变量y
的值 现在,我们打印块级作用域的变量x
它等于1
。
11、下面代码的输出结果是什么
// 以上只是 Foo 的构建方法,没有产生实例此刻也没有执行 // 现在在 Foo 上挂载了原型方法 a ,方法输出值为 3 // 现茬在 Foo 上挂载了直接方法 a 输出值为 4 // 立刻执行了 Foo 上的 a 方法,也就是刚刚定义的所以 /* 这里调用了
Foo 的构建方法。Foo 的构建方法主要做了两件事: 1. 將全局的 Foo 上的直接方法 a 替换为一个输出 1 的方法 2. 在新对象上挂载直接方法 a ,输出值为 2 // 因为有直接方法 a ,不需要去访问原型链所以使用嘚是构建方法里所定义的 this.a, //
构建方法里已经替换了全局 Foo 上的 a 方法所以
著作权归作者所有。商业转载请联系作者获得授权非商业转载请紸明出处