one question everyDay in my life

这篇文章是用来记录由魏老师天天分享的技术问题,感谢珠峰教育和魏老师的支持
复制代码

1.传统布局和flex布局有什么区别
  (1) 性能上flex容许子元素不设置宽高,而是由算法动态去计算,性能会比设定好宽高的稍慢 但在这个时代大致没有影响
  (2) 传统布局+flex布局!=全部布局,除了传统布局和flex布局外还有grid布局,多列布局等等n多种方法
  (3) 传统布局,基于盒模型,依赖 display属性 、position属性 、float属性,而FLEX用来为盒状模型提供最大的灵活性,不强制要求子元素的大小,任何一个容器均可以指定为 Flex 布局,能够简便、完整、响应式地实现各类页面布局。
题外扩展:
  (1)不要盒模型和布局弄混淆了,部局是DOM元素在文档中的位置排布,而模型指得是DOM元素的宽高大小的计算,模型通常由content-box,border-box,padding-box,-webkit-box等,默认为content-box
  (2)flex布局成为一个新的W3C标准规范
  (3)flex在移动端兼容性:ios8不兼容,android4.4不兼容
  (4)-webkit-box:在移动端开发中,全部的浏览器基本上都支持-webkit-box 点击查看-webkit-box
  (5)文档流:文档流指元素在文档中的位置由元素在html里的位置决定,块级元素独占一行,自上而下排列;内联元素从左到右排列
  (6)脱离文档流的方式:
    浮动,经过设置float属性
    绝对定位,经过设置position:absolute
    固定定位,经过设置position:fixed
  (7)定位浮动知识:点击这里送你去快活快活
php


2.你对vue的源码了解吗,给我讲讲双向绑定原理怎么实现的,详细描述何时监听变化的,何时触发变化的?
  (1) 一个vue对象实例化的过程当中会生成一个observer对象,Observer经过Object.defineProperty里面的getter和setter实现数据的变化
  (2)vue.js对模板作编译会解析生成一个个指令对象,每一个指令对象都会关联一个watcher,经过wahcher监听数据变化触发setter方法
题外扩展:
  (1) 能获取子组件内容但不能获取子组件的data里面的属性: this.refs.子组件.shoppingaddress也获取不到,是由于异步的缘由,子组件的没渲染出来,加一个this.nextTick
  (2)计算属性在模板中没有使用,那么能打印出来吗
html


3.如何比较两个颜色的类似程度
  (1) 首先将颜色拆分红r/g/b三个值,若是是字符串的颜色如#aabbff或者rgb(255,128,100)能够用正则表达式取出对应的r/g/b值。对于16进制字符串, 可使用parseInt(‘0xaa’)转10进制整数。
  (2) 而后对于两个颜色,可使用距离 Math.sqrt( (r1-r2) (r1-r2) +(g1-g2)(g1-g2)+(b1-b2)*(b1-b2) )进行比较, 距离近则类似。 固然能够用Math.hypot( r1-r2, g1-t2, b1-b2) 来简化上述运算。vue


4.一个单页面应用,有6张页面,F、E、A、B、C、D。 页面ABCD构成了一个冗长的用户验证过程。目前A、B、C对应用户验证过程的第1步,第2步,第3步。 页面F是首页,E是某张业务相关页面。用户到达页面E后, 系统发现用户没有认证,触发验证流程,到达页面A,而后开始A->B->C->D流程。 页面D是验证结果页面(验证成功页面)。 请问,若是到达页面D后, 如何让用户点击返回能够返回页面F,而忽略中间流程(注:用户可能根本没有到达过F,好比微信分享直接进入了E)
  这个问题初一看是对单页面路由架构的考察。也是一个很好的引入问题,能够考察很是多方面。 好比说:如何实现页面切换动画? A、B、C都是表单的话,如何缓存用户输入完成的表单数据?……回到问题,由于history api提供了push/pop/replace三种操做,不管是其中的任何一种都没法实现上述的效果。 一个路由系统,首先要监听浏览器地址的变化,而后根据变化渲染不一样的页面。java

  1. 在页面到达D后,关闭对路由变化页面渲染的监听。
  2. 路由系统要进行屡次POP,能够用history.go(-n)实现
  3. 路由栈清空到只剩下一张页面时,将这张页面替换为F。
  4. PUSH一张页面D。 若是在HTML上有一个相似「轮播图」的设计,就是每一张页面是一张轮播图,将轮播图设置成只有「F」和「D」。
  5. 恢复路由监听。 这个问题的另外一个考点是,在上述完整的计算过程中,须要知道当前历史记录中的页面数,页面数能够经过localStorage实现,在ls中维护一个变量,每次push的时候+1,并写入history.state。 POP的时候读取history.state将变量重置。

5.一个无序数组中,怎么找到和最大的子序列?
  (1)最简单也最暴力的解法:首先列出 全部的子序列,而后找出其中和最大的 便可; 实现思路:一个 记录当前最大值的变量maxSum;一个 子序列开始和结束的游标
变量;一个 当前子序列的和 的暂存变量,咱们称之为 currentSum 或者
tmpSum(下文中 使用currentSum)
找到全部的 子序列 咱们能够经过两层循环的方式来解决
node

第一层循环 i 从 0~ length-1; </br>
第二层循环 j 从 i ~ length - 1; </br>
这样的循环里 就能够找到全部的子序列了 </br>
下一步 咱们是要计算出全部子序列的和 </br>
最简单的办法 就是 第三层循环从 i ~ j 累加求出和 而后求出来的每一个和 和 maxSum</br> 去比较,若是比maxSum 大 就替换</br>
伪代码:maxSum = maxSum < currentSum ? currentSum : maxSum; </br>
三层循环结束后 maxSum就是咱们要 求的解 </br>
return maxSum便可 </br>
这个算法的时间复杂度是O(n^3);</br>
2)简化解法:咱们在第二层循环中,咱们已经知道 当前的</br> i/j以前的方法是在第三层的循环中 计算 i ~ j 的和 </br>
如今 咱们在第二层中 在进入第二层以前 咱们重置一下currentSum </br>
第一次循环 是 i ~ i 当前咱们就把 i 的值 记录到 currentSum去跟 maxSum 对比 而后 maxSum = maxSum < currentSum ? currentSum : maxSum;</br> 
第二次循环 是 i ~ i + 1 咱们就把 当前的 i+1 累加到currentSum 这时候的</br> currentSum就是 i ~ i+1 的值,再去跟maxSum去比 而后 maxSum = maxSum < currentSum ? currentSum : maxSum; </br>
以此类推 </br>
第二层的循环中 就能够 计算出 以当前 i 开头的子序列中 最大的子序列是多少 </br>
如今咱们看回 到 第一层循环 i 的取值 是从 0 ~ length-1 那么咱们是否是 能够找到 i 从 0 ~ length-1 全部的子序列中和最大的</br>
伪代码思路: </br>
第一层 i (0 ~ length-1) </br>
currentSum 清零 </br>
第二层 j(i ~ length-1) </br>
currentSum 累加 </br>
maxSum = maxSum < currentSum ? currentSum : maxSum; </br>
return maxSum; </br>
算法的时间复杂度是 O(n^2)</br>
3)demo数组:[-2, 1, -3, 4, -1, 2 , 1, -5, 4] </br>
首先 咱们能够简单的简化一下 这个数组 把 相邻 的 同</br> 正负的数字合起来,由于同符号的连续数 必定会同时存在在最大子序列里 </br>
好比 [-1, -2, -3, 1, 2, 13] 那跟 [-6, 16] 是没有区别的 </br>
[-2, 1, -3, 4, -1, 2, 1, -5, 4] ==> [-2, 1, -3, 4, -1, 3, -5, 4] </br>
而后 咱们从头开始看 </br>
-2 这是第一个元素 </br>
那么 咱们认为 当前的 最大子序列和 就是 -2 </br>
而后 发现了一个正数 1 </br>
那咱们能够肯定 -2 必定不包含在 咱们的最大子序列中 </br>
也就是说 数组开头 若是是负数 能够忽略过去 </br>
如今 咱们的数组 变成了 [1, -3, 4, -1, 3, -5, 4] </br>
同理 结尾的若是是 负数 也不须要考虑 </br>
如今咱们的数组 变成了 [1, -3, 4, -1, 3, -5, 4] </br>
咱们继续,如今 第一个元素是 1 最大和 是1 </br>
而后下一个数是 -3 </br>
那么 -3 对 1 这个数 起到了阻断做用 也就是说 -3 把 前边全部正数</br> 积累的能量都磨平了 甚至还变成了一个负数 </br>
那么 -3 咱们称之为 一个阻断 </br>
当前的 最大和 仍是 1 </br>
如今 咱们到了 4 </br>
那么如今的最大值 就是4 </br>
咱们继</br>
下个数字是 -1 以前最大的和是 4 </br>
加起来以后是 3 影响并不大 </br>
咱们继续带着他 向后看 </br>
下一个 是个正数 3 </br>
也就是 4 -1 3 这样的状况 </br>
咱们是否是能够认为 这个 -1 虽然下降了 和 可是 他链接了左右的正数</br> 让咱们当前的最大值 变成了 6 更新最大值 继续看 </br>
下一个是-5 </br>
同理 以前的 6+ -5 和 仍是1 也没有阻断 咱们去看看 后边
有没有一个大数 拯救咱们 </br>
后边 一个数 是 4 </br>
加上咱们刚才记录的 1 和是5 最后仍是没有挑战成功 因此 最大的和 仍是以前的 6</br>
公式:nums是咱们的源数组 nums[i] 就是咱们的当前元素 currentMax[i] 记录 咱们以 i 结尾的子序列里 最大的一个子序列 那么 </br>
currentMax[i] = max(currentMax[i - 1] + nums[i], nums[i]) </br>
这个公式被称之为 状态转移公式 咱们的这种解法 称之为 动态规划解法 简称:PD </br>
而后咱们去遍历 currentMax 这个数组 里边的最大值 就是咱们要找的 最大值</br>
伪代码: </br>
var maxSubArray = function(nums) { </br>
// 初始化源数组,初始化An为结束的最大值 </br>
let A = nums; </br>
let dp = []; </br>
let maxSum = A[0]; </br>
dp[0] = A[0]; </br>
for(let i = 1; i < A.length; i++) { </br>
//状态转移公式 </br>
dp[i] = max(A[i], dp[i-1] + A[i]) </br>
maxSum = dp[i] > maxSum ? dp[i] : maxSum; </br>
} </br></br>
return maxSum;</br>
}</br>
function max(a, b) { </br>
return a > b ? a : b; </br>
}</br>
复制代码

6. 请你说说函数防抖和函数节流的应用场景和原理?
函数节流场景:
  (1)例如:实现一个原生的拖拽功能(若是不用H5 Drag和Drop API),咱们就须要一路监听mousemove事件,在回调中获取元素当前位置,而后重置dom的位置。不加以控制,每移动必定像素而出发的回调数量是会很是惊人的,回调中又伴随着DOM操做,继而引起浏览器的重排和重绘,性能差的浏览器可能会直接假死。
  (2)这时,咱们就须要下降触发回调的频率,好比让它500ms触发一次或者200ms,甚至100ms,这个阀值不能太大,太大了拖拽就会失真,也不能过小,过小了低版本浏览器可能会假死,这时的解决方案就是函数节流【throttle】。
  (3)函数节流的核心就是:让一个函数不要执行得太频繁,减小一些过快的调用来节流。
函数去抖场景:
  (1)对于浏览器窗口,每作一次resize操做,发送一个请求,很显然,咱们须要监听resize事件,可是和mousemove同样,每缩小(或者放大)一次浏览器,实际上会触发N屡次的resize事件,这时的解决方案就是节流【debounce】。
  (2)函数去抖的核心就是:在必定时间段的连续函数调用,只让其执行一次*
总体函数总结一下:
  (1)对于按钮防点击来讲的实现:一旦我开始一个定时器,只要我定时器还在,无论你怎么点击都不会执行回调函数。一旦定时器结束并设置为null,就能够再次点击了。
  (2)对于延时执行函数来讲的实现:每次调用防抖动函数都会判断本次调用和以前的时间间隔,若是小于须要的时间间隔,就会从新建立一个定时器,而且定时器的延时为设定时间减去以前的时间间隔。一旦时间到了,就会执行相应的回调函数。
节流
  防抖动和节流本质是不同的。防抖动是将屡次执行变为最后一次执行,节流是将屡次执行变成每隔一段时间执行。android

  • underscore 节流函数,返回函数连续调用时,func 执行频率限定为 次 / wait
  • @param {function} func 回调函数
  • @param {number} wait 表示时间窗口的间隔
  • @param {object} options 若是想忽略开始函数的的调用,传入{leading: false}。
  • 若是想忽略结尾函数的调用,传入{trailing: false}
  • 二者不能共存,不然函数不能执行
  • @return {function} 返回客户调用函数
_.throttle = function(func, wait, options) { 
        var context, args, result; 
        var timeout = null; 
        // 以前的时间戳 
        var previous = 0; 
        // 若是 options 没传则设为空对象 
        if (!options) options = {}; 
        // 定时器回调函数 
        var later = function() { 
            // 若是设置了 leading,就将 previous 设为 0 
            // 用于下面函数的第一个 if 判断 
            previous = options.leading === false ? 0 : _.now(); 
            // 置空一是为了防止内存泄漏,二是为了下面的定时器判断 
            timeout = null; 
            result = func.apply(context, args); 
            if (!timeout) context = args = null; 
        }; 
    return function() { 
        // 得到当前时间戳 
        var now = _.now(); 
        // 首次进入前者确定为 true 
        // 若是须要第一次不执行函数 
        // 就将上次时间戳设为当前的 
        // 这样在接下来计算 remaining 的值时会大于0 
        if (!previous && options.leading === false) previous = now; 
        // 计算剩余时间 
        var remaining = wait - (now - previous); 
        context = this; 
        args = arguments; 
        // 若是当前调用已经大于上次调用时间 + wait 
        // 或者用户手动调了时间 
        // 若是设置了 trailing,只会进入这个条件 
        // 若是没有设置 leading,那么第一次会进入这个条件 
        // 还有一点,你可能会以为开启了定时器那么应该不会进入这个 if 条件了 
        // 其实仍是会进入的,由于定时器的延时 
        // 并非准确的时间,极可能你设置了2秒 
        // 可是他须要2.2秒才触发,这时候就会进入这个条件 
        if (remaining <= 0 || remaining > wait) { 
            // 若是存在定时器就清理掉不然会调用二次回调 
            if (timeout) { 
            clearTimeout(timeout); 
            timeout = null; 
        } 
        previous = now; 
        result = func.apply(context, args); 
        if (!timeout) context = args = null; 
        } else if (!timeout && options.trailing !== false) { 
            // 判断是否设置了定时器和 trailing 
            // 没有的话就开启一个定时器 
            // 而且不能不能同时设置 leading 和 trailing 
            timeout = setTimeout(later, remaining); 
        } 
        return result; 
      }; 
    };
复制代码

7.电商网站A和电影票网站B合做,A的用户,能够经过A网站下单购买电影票,以后跳转跳转到B(不须要登陆)去选座位,若是A、B是同域名,好比,好比a.domain.com,b.d,om,b.domain.com能不能共享cookie?
  若是不一样域如何处理?这实际上是个单点登陆的问题,同一级域名下设置cookie设在一级域名下, 同级域名下的cookie皆共享
缺点:
  1,同一域名限制
  2,登陆的规则一致,若是不一样域的话就是跨域问题,跨域问题能够用jsonp解决
  三、两者的区别
ios


i、做用域程序员

  相同浏览器的不一样页面间能够共享相同的 localStorage(页面属于相同域名和端口),可是sessionStorage只能在同源(相同域名相同学口)同学口访问,可是当sessionStorage在同一窗口下转到同源页面仍是能够访问的,由于这时候仍是同源同学口,不要单纯理解为两个不一样的页面之间不能访问相同sessionStorage。好比你在A网页设置了一个sessionStorage的值,而后你同时在新的窗口下打开B网页,这时候你尝试在B网页获得A网页设置的sessionStorage是不能够的,可是当你在A网页跳转到B网页的时候,这时候你会发现B网页能够访问A网页中的sessionStorage。因此sessionStorage针对的是同源同学口,不是同源同页面。web

ii、生命周期正则表达式

  localStorage生命周期是永久,这意味着除非用户本身清除localStorage信息或者用户清除浏览器缓存,不然这些信息将永远存在。   sessionStorage生命周期为当前窗口或标签页,一旦窗口或标签页被永久关闭了,那么全部经过sessionStorage存储的数据也就被清空了 总结:

cookie与sessionStorage、localStorage的区别

一、cookie能够在浏览器端与服务器端之间通讯,是服务器端获取用户信息、保持一种持久客户端状态的关键。而sessionStorage、localStorage

虽然也能够保存会话数据,可是不能与服务器端进行信息交换。

二、cookie的容量比较小。而sessionStorage、localStorage有较大的容量

三、试用的浏览器范围不一样。因为sessionStorage与localStorage是HTML5标准推出的,因此在IE7以及IE7如下的版本不适用。替代方案是采用IE的userData.(有兴趣能够了解一下)

三者的异同

特性 Cookie localStorage sessionStorage 数据的生命期 通常由服务器生成,可设置失效时间。若是在浏览器端生成Cookie,默认是关闭浏览器后失效 除非被清除,不然永久保存 仅在当前会话下有效,关闭页面或浏览器后被清除 存放数据大小 4K左右 通常为5MB 与服务器端通讯 每次都会携带在HTTP头中,若是使用cookie保存过多数据会带来性能问题 仅在客户端(即浏览器)中保存,不参与和服务器的通讯 易用性 须要程序员本身封装,源生的Cookie接口不友好 源生接口能够接受,亦可再次封装来对Object和Array有更好的支持

8. 说说什么是xss攻击?如何攻击,如何防护?
  (1)XSS 跨网站指令码(英语:Cross-site scripting,一般简称为:XSS)是一种网站应用程式的安全漏洞攻击,是代码注入的一种。它容许恶意使用者将程式码注入到网页上,其余使用者在观看网页时就会受到影响。这类攻击一般包含了 HTML 以及使用者端脚本语言。
  (2)XSS 分为三种:反射型,存储型和 DOM-based
  (3)如何攻击:
    XSS 经过修改 HTML 节点或者执行 JS 代码来攻击网站。
    例如经过 URL 获取某些参数
    上述 URL 输入可能会将 HTML 改成

,这样页面中就凭空多了一段可执行脚本。这种攻击类型是反射型攻击,也能够说是 DOM-based 攻击。 也有另外一种场景,好比写了一篇包含攻击代码 的文章,那么可能浏览文章的用户都会被攻击到。这种攻击类型是存储型攻击,也能够说是 DOM-based 攻击,而且这种攻击打击面更广。 如何防护 最广泛的作法是转义输入输出的内容,对于引号,尖括号,斜杠进行转义 这里写图片描述 内容安全策略 (CSP) 是一个额外的安全层,用于检测并削弱某些特定类型的攻击,包括跨站脚本 (XSS) 和数据注入攻击等。不管是数据盗取、网站内容污染仍是散发恶意软件,这些攻击都是主要的手段。 咱们能够经过 CSP 来尽可能减小 XSS 攻击。CSP 本质上也是创建白名单,规定了浏览器只可以执行特定来源的代码。 一般能够经过 HTTP Header 中的 Content-Security-Policy 来开启 CSP 只容许加载本站资源,只容许加载 HTTPS 协议图片,容许加载任何来源框架 这里写图片描述 这里写图片描述 这里写图片描述 参考网站 content-security-policy.com/ eggjs.org/zh-cn/core/…


9. CSRF攻击是什么?如何防范?
  CSRF概念:CSRF跨站点请求伪造(Cross—Site Request Forgery),跟XSS攻击同样,存在巨大的危害性,你能够这样来理解: 参考网站 www.phpddt.com/reprint/csr… juejin.im/post/5b6b08…


10. 若是发如今某个用户的电脑上,网站的静态资源打不开了,如何肯定是CDN的问题仍是那个用户机器、浏览器的问题?
一、本身的电脑访问cdn地址,排除是否cdn问题;
二、让用户换浏览器再试,排除用户浏览器的问题(禁用全部插件、清除缓存);
三、已经排除cdn和用户浏览器的问题,那就是用户的机器(或者所在的网络)有问题。有多是用户所在的公司网络禁止下载某些资源。
四、推荐一个本地网络诊断的工具的工具https://cdn.dns-detect.alicdn.com/https/doc.html 能够检 能够检查dns和本地ip地址是否正常


11. 请说说在hybrid端实现相似原生般流畅的体验,要注意哪些事项?
  1.资源加载,采用预加载,优先加载到内存中,作到无缝切换,使用原生loading
  2.离线加载静态资源,不走网络请求
  3.尽可能作到局部更新,动画使用transform,will-change来提升性能
  4.使用webkit over scrolling加速滚动条的滚动,去掉user selection,去掉高亮等,手势交互原生实现,复杂交互如日期选择器等调用原生组件
  5.遵循APP UI设计规范
  6.考虑文件diff更新,或主动后台请示,在某个时刻跟新,两个版本直接的兼容问题
  7.APP方法加上安全性的考虑


12.事件触发的三个阶段
  document 往事件触发处传播,遇到注册的捕获事件会触发 传播到事件触发处时触发注册的事件 从事件触发处往 document 传播,遇到注册的冒泡事件会触发 事件触发通常来讲会按照上面的顺序进行,可是也有特例,若是给一个目标节点同时注册冒泡和捕获事件,事件触发会按照注册的顺序执行。

// 如下会先打印冒泡而后是捕获
node.addEventListener('click',(event) =>{
	console.log('冒泡')
},false);
node.addEventListener('click',(event) =>{
	console.log('捕获 ')
},true)
复制代码

注册事件
  一般咱们使用 addEventListener 注册事件,该函数的第三个参数能够是布尔值,也能够是对象。对于布尔值 useCapture 参数来讲,该参数默认值为 false 。useCapture 决定了注册的事件是捕获事件仍是冒泡事件。对于对象参数来讲,可使用如下几个属性 capture,布尔值,和 useCapture 做用同样 once,布尔值,值为 true 表示该回调只会调用一次,调用后会移除监听 passive,布尔值,表示永远不会调用 preventDefault通常来讲,咱们只但愿事件只触发在目标上,这时候可使用 stopPropagation 来阻止事件的进一步传播。一般咱们认为 stopPropagation 是用来阻止事件冒泡的,其实该函数也能够阻止捕获事件。stopImmediatePropagation 一样也能实现阻止事件,可是还能阻止该事件目标执行别的注册事件。

node.addEventListener('click',(event) =>{
	event.stopImmediatePropagation()
	console.log('冒泡')
},false);
// 点击 node 只会执行上面的函数,该函数不会执行
node.addEventListener('click',(event) => {
	console.log('捕获 ')
},true)
复制代码

事件代理 若是一个节点中的子节点是动态生成的,那么子节点须要注册事件的话应该注册在父节点上

<ul id="ul">
	<li>1</li>
    <li>2</li>
	<li>3</li>
	<li>4</li>
	<li>5</li>
</ul>
<script>
	let ul = document.querySelector('##ul')
	ul.addEventListener('click', (event) => {
		console.log(event.target);
	})
</script>z
复制代码

事件代理的方式相对于直接给目标注册事件来讲,有如下优势 节省内存 不须要给子节点注销事件 点击查看详解


12.问题以下:

  1. 什么是重绘(Repaint)和回流(Reflow)?
  2. 哪些动做可能会致使重绘(Repaint)和回流(Reflow)的发生?
  3. 重绘(Repaint)和回流(Reflow)和Event loop的关系?
  4. 如何减小重绘(Repaint)和回流(Reflow)?

13. 2*8最有效率的计算方法
  2<<3 讲一个数左移动n位就至关于乘以2的n次方,一个数要乘以8只须要将他左移动三位就好了,而位运算cpu直接支持,因此运算效率最高,因此2乘以8最有效的是 2<<3
例如11 >> 2,则是将数字11右移2位
右移一位至关于除2,右移n位至关于除以2的n次方。
14.发布订阅模式和观察者模式有什么区别

其实订阅模式有一个调度中心,对订阅事件进行统一管理。而观察者模式能够随意注册
事件,调用事件,虽然实现原理都雷同,设计模式上有必定的差异,实际代码运用中差异在
于:订阅模式中,能够抽离出调度中心单独成一个文件,能够对一系列的订阅事件进行统一管理。
复制代码

15.apply和call方法有什么区别

区别:
   call, apply方法区别是,从第二个参数起,call方法参数将依次传递给借用的方法做参数
而apply直接将这些参数放到一个数组中再传递, 最后借用方法的参数列表是同样的.
应用场景:
   当参数明确时可用call, 当参数不明确时可用apply给合arguments
复制代码

16.js对象和hashMap

数组:也叫线性连续表
怎么将一组值相差范围巨大,个数波动范围很大的下标放入特定的数组空间呢?
用哈希算法、也叫散列算法
哈希算法的目的就是将不定的输入转换成特定范围的输出,而且要求输出尽可能均匀分布
1,除法散列法
最直观的一种,小茄上文使用的就是这种散列法,公式:
index = key % 16
2,平方散列法
求index是很是频繁的操做,而乘法的运算要比除法来得省时(对如今的CPU来讲,估计我
们感受不出来),因此咱们考虑把除法换成乘法和一个位移操做。公式:
index = (key * key) >> 28
3,斐波那契(Fibonacci)散列法
复制代码

17.vue组件中data必须是一个函数的缘由

vue组件中data值不能为对象,由于对象是引用类型,组件可能会被多个实例同时引用。
若是data值为对象,将致使多个实例共享一个对象,其中一个组件改变data属性值,其它
实例也会受到影响。

  上面解释了data不能为对象的缘由,这里咱们简单说下data为函数的缘由。data为函数,
经过return 返回对象的拷贝,导致每一个实例都有本身独立的对象,实例之间能够互不影
响的改变data属性值。
复制代码

18.原型链

实例的__protpo__指向的是原型对象。
   实例的构造函数的prototype也是指向的原型对象。 
   原型对象的construor指向的是构造函数。
   对象的__proto__它的是原型,而原型也是一个对象,也有__proto__属性,原型的
   __proto__又是原型的原型,就这样能够一直经过__proto__想上找,这就是原型链,当向上
   找找到Object的原型的时候,这条原型链就算到头了
复制代码

19.闭包

一般,函数的做用域及其全部变量都会在函数执行结束后被销毁。可是,在建立了一个闭
包之后,这个函数的做用域就会一直保存到闭包不存在为止
function makeAdder(x) { 
  return function(y) { 
    return x + y; 
  }; 
} 
闭包建立的第二种形式:传入一个全局变量
建立闭包的常见方式,就是在一个函数内部建立另外一个函数。

//经过闭包能够得到函数fn的局部变量user
function fn(){
    var user='zhang';
    return function(){
        return user;
    }//经过匿名函数返回局部变量user
}
console.log(fn()());//zhang  经过box()()来直接调用匿名函数返回值

var b=fn();
console.log(b());//zhang 另外一种调用匿名函数返回值
经过闭包能够实现函数内的局部变量的累加:

function fn(){
    var num=100;
    return function(){
        num++;
        return num;
    }
}
var b=fn();//得到函数
//你会发现局部变量num并无在内存当中消失
console.log(b());//调用匿名函数
console.log(b());//第二次调用匿名函数,实现累加
因为在闭包所在的做用域返回的局部变量不会被销毁,因此会占用内存。过分的使用闭
包会迫使性能降低,所以建议你们在有必要的状况下再使用闭包。做用域链的机
制会致使一个问题,在循环中里的匿名函数取得的任何变量都是最后一个值

function fn(){
    var arr=[];
    //i为fn函数中的局部变量。
    for(var i=0;i<3;i++){
        arr.push(function(){
            return i;
        });
    }
    return arr;

}
var b=fn();
for(var i=0;i<b.length;i++){
    console.log(b[i]());//3
}
以上示例输出的结果均是3,也就是循环结束后i的值。这是由于在for循环的过程中,
数组中的匿名函数并无自我执行。当在调用匿名函数的时候,经过闭包得到的i已是3了
,因此每次输出的都是3。 若是想要输出的结果为0,1,2能够作以下的调整:

function fn(){
    var arr=[];
    for(var i=0;i<3;i++){
        arr.push((function(num){
            //将当即执行函数返回的匿名函数放到数组中。
            //由于num为函数的参数,所以有本身独立的做用域
            return function(){
                return num;
            };
        })(i));
    }
    return arr;
}
var b=fn();
for(var i=0;i<b.length;i++){
    console.log(b[i]());//0,1,2
}
经过匿名函数的当即执行,将当即执行后返回的函数直接赋值给数组arr。每次循环即将
i的值传递给num,又由于num在函数中,因此有本身的独立做用域,所以num获得的值为
每次循环传递进来的i值,即0,1,2

接下来看一下关于闭包当中的this对象:
this对象指的是什么,这个要看函数所运行的环境。若是函数在全局范围内调用
,函数内的this指向的是window对象。对象中的方法,经过闭包若是运行的环境
为window时,则this为window。由于闭包并非该对象的方法。

var color="red";
function fn(){
    return this.color;
}
var obj={
    color:"yellow",
    fn:function(){
        return function(){//返回匿名函数
            return this.color;
        }
    }
}
console.log(fn());//red  在外部直接调用this为window
var b=obj.fn();//b为window下的变量,得到的值为obj对象下的fn方法返回的匿名函数
console.log(b());//red  由于是在window环境下运行,因此this指缶的是window

//能够经过call或apply改变函数内的this指向
console.log(b.call(obj));//yellow
console.log(b.apply(obj));//yellow
console.log(fn.call(obj));//yellow
经过变量能够得到上一个做用域中的this指向

var color="red";
function fn(){
    return this.color;
}
var obj={
    color:"yellow",
    fn:function(){
        var _this=this;//将this赋值给变量_this
        return function(){
            return _this.color;//经过_this得到上一个做用域中的this指向
        }
    }
}
console.log(fn());//red
var b=obj.fn();
console.log(b());//yellow
能够经过构造方法传参来访问私有变量
function Desk(){
    var str="";//局部变量str,默认值为""
    this.getStr=function(){
        return str;
    }
    this.setStr=function(value){
        str=value;
    };
}
var desk=new Desk();
//为构造函数的局部变量写入值。
desk.setStr("zhangPeiYue");
//获取构造函数的局部变量
console.log(desk.getStr());//zhangPeiYue
闭包常见的做用
一、模拟块级做用域(匿名自执行函数)
if(){}for(){}等没有做用域,因此在其块内声明的变量,在外部是可使用的。

//javaScript没有块级做用域的概念
function fn(num){
    for(var i=0;i<num;i++){}
    console.log(i);//在for外部i不会失败
}
fn(2);

if(true){
    var a=13;
}
console.log(a);//在if定义的变量在外部能够访问
经过匿名自执行函数能够模拟块级做用域

 (function(){
        //i在外部就不认识啦
        for(var i=0;i<count;i++){}
  })();
  console.log(i);//报错,没法访问
因为外部没法访问自执行函数内的变量,所以在函数执行完后会马上被销毁,在外部没法
访问。从而可有效避免在多人开发时因为全局变量过多而形成的命名冲突问题。另外因为
做用域链的机制,局部变量要比全局变量的访问速度更快,能够提高程序的运行速度!

二、对结果进行缓存
写一个用于实现全部参数和的函数:

var fn=function(){
    var sum=0;
    for(var i=0;i<arguments.length;i++){
        sum+=arguments[i];
    }
    return sum;
}
console.log(fn(1,2));//3
以上函数接收一些number类型的参数,并返回这些参数之和。因为每次传递的参数相同,
因此返回的结果是同样的。这样势必会形成了一种浪费,在此时我们能够经过缓存机制来提
高这个函数的性能。

var fn=(function(){
    var cache={}//将结果缓存到该对象中
    return function(){
        var str=JSON.stringify(arguments);
        if(cache[str]){//判断缓存中是否存在传递过来的参数,存在直接返回结果,
        无需计算
            return cache[str];
        }else{//进行计算并返回结果
            var sum=0;
            for(var i=0;i<arguments.length;i++){
                sum+=arguments[i];
            }
            return cache[str]=sum;
        }
    }
})()
上面的示例将计算后的结果缓存到局部变量cache当中,在调用这个函数时,先在缓存中
查找,若是找不到,则进行计算,而后将结果放到缓存中并返回,若是找到了,直接返回
查找到的值。
复制代码
相关文章
相关标签/搜索