web前端开发经典面试题整理

目录
文章目录隐藏
  1. 1.CSS 的盒子模型
  2. 2.AJAX 的 readyState 几种状态
  3. 3.常见的 post 提交数据方式对应的 content-type 取值
  4. 4.清除浮动的几种方法
  5. 5.Webpack 解析 ES6 用的插件
  6. 6.移动端上 click 事件在某些浏览器有没有遇到延迟的问题
  7. 7.HTTP 的缓存机制
  8. 8.实现三栏布局(左右两边固定宽度,中间自适应)
  9. 9.== 和 === 的区别
  10. 10.transition 和 animation 比较
  11. 89.数组降维
  12. 90.栈的压入和弹出
  13. 91.利用栈模拟队列
  14. 92.连续最长不重复字符串
  15. 93.求一个数组当中,连续子向量的最大和。
  16. 94.给定一个编码字符,按编码规则进行解码,输出字符串
  17. 95.['1', '2', '3'].map(parseInt) 的运行结果
  18. 96.自定义事件
  19. 97.以下递归函数存在栈溢出的风险,请问如何优化?
  20. 98.请实现一个计算最大公约数的函数
  21. 99.数组去重(如果数组中有 NaN)
  22. 100. 用 JavaScript 实现斐波那契数列函数,返回第 n 个斐波那契数。 f(1) = 1, f(2) = 1 等
  23. 101.根据包名,在指定空间中创建对象
  24. 102.封装函数 f,使 f 的 this 指向指定的对象
  25. 103.dom 节点查找
  26. 104.关系型数组转换成树形结构对象
  27. 105.JS 如何判断一组数字是否连续
  28. 106.创建子类 Child,使用原型和构造函数的方式继承父类 People 的方法,并调用 say 函数说出姓名和年龄。

  每到年底,想起来金三银四跳槽季,心里就痒痒:那么多高薪机会,我要不要看看?听了太多“别人公司的福利”、“别人公司的薪资”、“别人公司的发展机会”,于是,很多人摩拳擦掌准备拿了年终奖就另择高枝换个更好的工作。可是,你真的准备好了吗?然而对于前端开发的小伙伴来说,在面试之前刷一下前端面试题还是有必要的,所以忙里偷闲为大家整理一下有关前端面试题,希望对小伙伴们有帮助。

web 前端开发经典面试题整理

1.CSS 的盒子模型

  包含元素内容 content、内边距 padding、边框 border、外边距 margin

  box-sizing:border-box;content-box;inherit;

  1)content-box:总宽度=margin+border+padding+width,即为标准模型;

  2)border-box:总宽度=margin+width,即为 IE 模型;

  3)inherit:继承父元素的 border-sizing 属性。

2.AJAX 的 readyState 几种状态

  0——初始化

  1——载入

  2——载入完成

  3——解析

  4——完成

3.常见的 post 提交数据方式对应的 content-type 取值

  4 种

  1)application/x-www-form-urlencoded:数据被编码为名称/值对,这是标准的编码格式。

  2)multipart/form-data:数据被编码为一条消息,页上的每个控件对应消息中的一个部分。

  3)application/json:告诉服务端消息主体是序列化后的 JSON 字符串.

  4)text/xml

4.清除浮动的几种方法

  1)父级 div 定义伪类:after 和 zoom

.clearfloat::after {display:block; clear:both; content:" "; visibility:hidden; height:0;} .clearfloat {zoom:1}

  2)在结尾处添加空 div 标签 clear:both

  3)父级 div 定义 height

  4)父级 div 定义 display:table

  5)父级 div 也一起浮动

  6)父级 div 定义 overflow:auto/hidden(必须定义 width 或 zoom:1,同时不能定义 height,浏览器会自动检查浮动区域的高度)

5.Webpack 解析 ES6 用的插件

babel-loader

6.移动端上 click 事件在某些浏览器有没有遇到延迟的问题

1)静止缩放

<meta name="viewport" content="width=device-width user-scalable='no' ">

<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1">

2)引入 fastclick.js

window.addEventListener(function(){
FastClick.attach(document.body);
1},false);

7.HTTP 的缓存机制

  1)强制缓存:当所请求的数据在缓存数据库中尚未过期时,不与服务器进行交互,直接使用缓存数据库中的数据。

  Expire/Cache-Control

  2)协商缓存:从缓存数据库中取出缓存的标识,然后向浏览器发送请求验证请求的数据是否已经更新,如果已更新则返回新的数据,若未更新则使用缓存数据库中的缓存数据。

  etag/last-modifield

8.实现三栏布局(左右两边固定宽度,中间自适应)

  1)浮动布局

  左右两边固定宽度,并分别设置 float:left 和 float:right。(但这会带来高度塌陷的问题,所以要清除浮动。清除浮动的方式有:

  a.给父级盒子设置 height;

  b.给父级盒子设置 overflow:hidden;

  c.在父级盒子结束前的盒子添加 clear:both;

  d.父级盒子也设置浮动;

  e.父级 div 定义伪类:after 和 zoom,

.clear::after{display:block;clear:both;content:"";visibility:hidden;height:0;} 
.clear{zoom:1;}

  2)绝对定位布局

  左中右三个盒子都设置 position:absolute;然后分别设置定位

  3)flex 布局

  父盒子设置 display:flex;位于中间的子盒子设置 flex:1

  4)表格布局

  父盒子设置 display:table;左中右三个盒子设置 display:table-cell;左右两个盒子分别设置宽度;

  5)网格布局

  父盒子设置 display:grid;grid-template-columns:300pxauto300px;

9.== 和 === 的区别

  ===为恒等符:当等号两边的值为相同类型的时候,直接比较等号两边的值,值相同则返回 true,若等号两边的值类型不同时直接返回 false。

  ==为等值符:当等号两边的值为相同类型时比较值是否相同,类型不同时会发生类型的自动转换,转换为相同的类型后再作比较。

  a、如果一个是 null、一个是 undefined,那么相等。

  b、如果一个是字符串,一个是数值,把字符串转换成数值再进行比较。

  c、如果任一值是 true,把它转换成 1 再比较;如果任一值是 false,把它转换成 0 再比较。

  d、如果一个是对象,另一个是数值或字符串,把对象转换成基础类型的值再比较。对象转换成基础类型,利用它的 toString 或者 valueOf 方法。js 核心内置类,会尝试 valueOf 先于 toString;例外的是 Date,Date 利用的是 toString 转换。那些不是 JavaScript 语言核心中的对象则通过各自的实现中定义的方法转换为原始值。

  e、任何其他组合,都不相等。

web 前端开发经典面试题整理

10.transition 和 animation 比较

transition

  1)语法

  transition 是一个复合属性,可设置四个过渡属性,简写方式如下:

transition{ transition-property transition-duration transition-timing-function transition-delay}

  transition-property:是用来指定当元素其中一个属性改变时执行 transition 效果,值有 none(没有属性改变)、all(默认值,所有属性改变),indent(某个属性名,一条 transition 规则,只能定义一个属性的变化,不能涉及多个属性,如果要设置多个属性时,需分别设置,中间以逗号隔开)【当其值为 none 时,transition 马上停止执行,当指定为 all 时,则元素产生任何属性值变化时都将执行 transition 效果】;

  transition-duration:过度时间,是用来指定元素转换过程的持续时间,单位为 s(秒)或 ms(毫秒);

  transition-timing-function:时间函数,根据时间的推进去改变属性值的变换速率,值 ease(逐渐变慢)、linear(匀速)、ease-in(加速)、ease-out(减速)、ease-in-out(加速然后减速)、cubic-bezier:(自定义一个时间曲线);

  transition-delay:延迟,指定一个动画开始执行的时间,也就是当改变元素属性值后多长时间开始执行 transition 效果,单位为 s(秒)或 ms(毫秒);

  2)触发方式

  伪类触发::hover,:focus,:checked,:active

  js 触发:toggleClass

  3)以下情况下,属性值改变不能产生过渡效果

  a.background-image,如 url(a.jpg)到 url(b.jpg)(与浏览器支持相关,有的浏览器不支持)等

  b.float 浮动元素

  c.height 或 width 使用 auto 值

  d.display 属性在 none 和其他值(block、inline-block、inline)之间变换

  e.position 在 static 和 absolute 之间变换

  transition 示例:

<head> 
   <style>     
   #box2{
            height: 100px;
            width: 100px;
            background: blue;
        }
        #box2:hover{
            transform: rotate(180deg) scale(.5, .5);
            background: red;
            transition: background 2s ease, transform 2s ease-in 1s;}
    </style>
</head>
<body>
 <div id="box1">BOX1</div>
 <div id="box2">BOX2</div>
</body>

animation

1)语法

animation{ animation-name animation-duration animatino-timing-function animation-delay animation-iteration-count animation-direction animtion-play-state animation-fill-mode}

  animation-name:用来调用@keyframes 定义好的动画,与@keyframes 定义的动画名称一致;

  animation-duration:指定元素播放动画所持续的时间;

  animation-timing-function:和 transition 中的 transition-timing-function 中的值一样。根据上面的@keyframes 中分析的 animation 中可能存在多个小动画,因此这里的值设置是针对每一个小动画所在所在时间范围内的属性变换速率;

  animation-delay:定义在浏览器开始执行动画之前的等待的时间、这里是指整个 animation 执行之前的等待时间,而不是上面所说的多个小动画;

  animation-iteration-count:定义动画的播放次数,通常是整数,默认是 1,若为 infinity,动画将无限多次的播放;

  animation-direction:主要用来设置动画播放次数,其主要有两个值:normal:默认值,动画每次训话都是按顺序播放;alternate:动画播放在第偶数次向前播放,第奇数次想反方向播放(animation-iteration-count 取值大于 1 时设置有效)

  animation-play-state:属性用来控制元素动画的播放状态。主要有两个值:running:可以通过该值将暂停的动画重新播放,这里的重新播放不是从元素动画的开始播放,而是从暂停的那个位置开始播放;paused:暂停播放。

  animation-fill-mod:默认情况下,动画结束后,元素的样式将回到起始状态,animation-fill-mode 属性可以控制动画结束后元素的样式。主要具有四个属性值:none(默认,回到动画没开始时的状态。),forwards(动画结束后动画停留在结束状态),backwords(动画回到第一帧的状态),both(根据 animation-direction 轮流应用 forwards 和 backwards 规则)。

  13)reduce()方法需要两个参数,第一个是执行化简操作的函数,化简函数的任务就是用某种方法把两个值组合或化简为一个值,并返回化简后的值。

var total = [0, 1, 2, 3].reduce(function(sum, value) {
  return sum + value;
}, 0);
// total is 6

  reduceRight()则从右到左执行对应的化简函数

  14)map()方法对数组中的每一项运行给定的函数,返回每次函数调用的结果组成的数组,

  map 方法还可以接受第二个参数,表示回调函数执行时 this 所指向的对象。

  15)forEach()方法对数组中的每一项运行给定的函数,这个方法没有返回值。本质上和 for 循环迭代数组一样。如果需要有返回值,一般使用 map 方法。

  forEach()方法除了接受一个必须的回调函数参数,第二个参数还可以接受一个可选的上下文参数(改变回调函数里面的 this 指向)

array.forEach(callback(currentValue, index, array){
    //do something
}, this)

  16)filter()方法对数组中的每一项运行给定的函数,返回该函数会返回 true 的项组成的数组。该方法常用于查询符合条件的所有数组项。

  filter()方法还可以接受第二个可选的上下文参数(改变回调函数里面的 this 指向)

var arr= [1,10,20,30]
var brr = arr.filter((item)=>{
    return item>10;
})
//[20,30]

  17)some()方法对数组中的每一项运行给定函数,如果该函数对任一项返回 true,则返回 true。并且当且仅当数值中的所有元素调用判定函数都返回 false,它才返回 false

  注意:在空数组上调用 some()方法会返回 false

const isBiggerThan10 = (element, index, array) => {
  return element > 10;
}

[2, 5, 8, 1, 4].some(isBiggerThan10);  
// false

[12, 5, 8, 1, 4].some(isBiggerThan10); 
// true

  18)every()方法对数组中的每一项运行给定函数,如果函数对每一项都返回 true,则返回 true;只要有一项返回 false,则返回 false

  19)fill()方法,用一个固定值填充一个数组中起始索引到终止索引内的全部元素

arr.fill(value, start, end)
var numbers = [1, 2, 3]
numbers.fill(1);
// results in [1, 1, 1]

  20)find()方法返回数组中满足提供的测试函数的第一个元素的值

function isBigEnough(element) {
    return element >= 15;
}
[12, 5, 8, 130, 44].find(isBigEnough); // 130

  21)findIndex()方法返回数组中满足提供的测试函数的一个元素的索引

function isBigEnough(element) {
  return element >= 15;
}
[12, 5, 8, 130, 44].findIndex(isBigEnough); 
//'3'

  22)includes()方法用来判断一个数组是否包含一个指定的值,如果是,则返回 true,如果没有则返回 false

let a = [1, 2, 3];
a.includes(2); 
// true 
a.includes(4); 
// false

  23)toLocaleString()方法返回一个字符串表示数组中的元素。数组中的元素将使用各自的 toLocaleString 方法转成字符串,这些字符串将使用一个特定语言环境的字符串(例如一个逗号”,”)隔开

var number = 1337;
var date = new Date();
var myArr = [number, date, "foo"];
var str = myArr.toLocaleString(); 
console.log(str); 
// 输出 "1,337,2019/2/15 下午 8:32:24,foo"

  24)copyWithin(target,start,end)方法浅复制数组的一部分到同一数组的另一个位置

  25)Array.isArray()方法用于确定传递的值是否是一个 Array

Array.isArray([]) => true;
Array.isArray({}) => false;

  26)Array.of()

Array.of(7);       // [7] 
Array.of(1, 2, 3); // [1, 2, 3]

Array(7);          // [ , , , , , , ]
Array(1, 2, 3);    // [1, 2, 3]

  27)Array.from()

  对伪数组或可迭代对象(包括 arguments Array,Map,Set,String…)转换成数组对象

  语法 Array.from(arrayLike,mapFn,thisArg)

  arrayLike

  想要转换成数组的伪数组对象或可迭代对象。

  mapFn(可选参数)

  如果指定了该参数,新数组中的每个元素会执行该回调函数。

  thisArg(可选参数)

  可选参数,执行回调函数 mapFn 时 this 对象。

  返回值

  一个新的数组实例

89.数组降维

方法一:

function flattenDeep(arr) {
        arr = "" + arr;  // 或者 arr = arr.toString();

        arr = arr.split(",");
        arr = arr.map(Number)
        return arr;
    }
flattenDeep([1, [[2],[3, [4]], 5]]);

方法二:

function flattenDeep(arr) {
    if(!Array.isArray(arr))
        return [arr];
    return arr.reduce((prev,cur) => {        
        return [...prev, ...flattenDeep(cur)];
    },[]);
}

flattenDeep([1, [[2], [3, [4]], 5]]);fang

方法三:

function flattenDeep(arr){
    while(arr.some(item=>Array.isArray(item)){
        arr = [].concat(...arr);
    }
    return arr;
}

90.栈的压入和弹出

  输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列 1,2,3,4,5 是某栈的压入顺序,序列 5,4,3,2,1 或 3,2,1 是该压栈序列对应的一个弹出序列,但 4,3,5,1,2 就不可能是该压栈序列的弹出序列。

function IsPopOrder(pushV,popV){
    if(pushV.length === 0) return false;
    var stack = []; // 模拟栈
    for(var i = 0, j = 0; i < pushV.length;){
        stack.push(pushV[i]);
        i += 1;
        // 压入栈的值需要被弹出
        while(j < popV.length && stack[stack.length-1] === popV[j]){
            stack.pop();
            j++;
            if(stack.length === 0) break;
        }
    }
    return stack.length === 0;
}

91.利用栈模拟队列

思路:

  • 对栈 A 添加数据。
  • 如果栈 B 为空,循环将栈 A 中内容弹出放入栈 B,并弹出栈 B 最后一项
  • 如果栈 B 不为空,则直接弹出栈 B 的最后一项
var stackA = [];
var stackB = [];

function push(node){
    stackA.push(node);
}
function pop(){
    if(!stackB.length){
        while(stackA.length){
            stackB.push(stackA.pop());
        }
    }
    return stackB.pop();
}

92.连续最长不重复字符串

  在一个字符串中找出连续的不重复的最大长度的字符串,解决这类问题的思路:

  • 利用循环叠加字符串,直到出现重复为止
  • 每一次叠加,记录下来最大长度的字符串
// 连续最长不重复字符串
function getMaxLenStr(str) {
    var cur = [];
    var maxLenStr = '';
    for(var i = 0; i < str.length; i++) {
        if(!cur.includes(str[i])) {
            cur.push(str[i]);
        } else {
            cur = []; // 置为空
            cur.push(str[i]);
        }
        // 存储最大长度的字符串
        if(maxLenStr.length < cur.length) {
            maxLenStr = cur.join('');
        }        
    }
    return maxLenStr;
}
getMaxLenStr('ababcabcde'); // abcde

93.求一个数组当中,连续子向量的最大和。

function FindGreatestSumOfSubArray(arr) {
    let sum = arr[0];
    let max = arr[0];
    for(let i = 1; i < arr.length; i++) {
        if(sum < 0) {
            sum = arr[i];
        }else{
            sum += arr[i];
        }
        // 记录最大值
        if(max < sum) {
            max = sum;
        }
    }
    return max;
}

94.给定一个编码字符,按编码规则进行解码,输出字符串

  编码规则:coount[letter],将 letter 的内容 count 次输出,count 是 0 或正整数,letter 是区分大小写的纯字母。

  实例:

  • const s= 3[a]2[bc]; decodeString(s); // 返回 ‘aaabcbc’
  • const s= 3[a2[c]]; decodeString(s); // 返回 ‘accaccacc’
  • const s= 2[ab]3[cd]ef; decodeString(s); // 返回 ‘ababcdcdcdef’

  思路:

  使用栈这种数据结构,如果 push 的内容为‘]’,则循环 pop 字符,直到碰到’[‘,然后将 pop 出来的字符串按规则整理后,重新 push 进栈中,最后将栈内的内容拼接成字符串输出即可。

function decodeString(str) {
    let stack = []; // 存储字符串的栈
    for (let i = 0; i < str.length; i++) {
        let cur = str[i];
        if (cur !== ']') {
            stack.push(cur);
        } else { // 弹出
            let count = 0;
            let loopStr = [];
            let popStr = '';
            while ((popStr = stack.pop()) !== '[') {
                loopStr.unshift(popStr);
            }
            count = stack.pop();
            // 添加结果
            let item = '';
            for (let i = 0; i < count; i++) {
                item += loopStr.join('');
            }
            stack.push(...(item.split('')));
        }
    }
    return stack.join('');
}

95.['1', '2', '3'].map(parseInt) 的运行结果

  答案为:[1,NaN,NaN]

  解析:

arr.map(functioncallback(currentValue[,index[,array]]){//Returnelementfornew_array}[,thisArg])

  这个 callback 一共可以接收三个参数,其中第一个参数代表当前被处理的元素,而第二个参数代表该元素的索引。

  而 parseInt 则是用来解析字符串的,使字符串成为指定基数的整数。

  -parseInt(string,radix)

  接收两个参数,第一个表示被处理的值(字符串),第二个表示为解析时的基数。

  parseInt('1',0)//radix 为 0 时,且 string 参数不以“0x”和“0”开头时,按照 10 为基数处理。这个时候返回 1

  parseInt('2',1)//基数为 1(1 进制)表示的数中,最大值小于 2,所以无法解析,返回 NaN

  -parseInt('3',2)//基数为 2(2 进制)表示的数中,最大值小于 3,所以无法解析,返回 NaN

  map 函数返回的是一个数组,所以最后结果为[1,NaN,NaN]

96.自定义事件

var content = document.querySelector('.content');
    // 自定义事件
    var evt = new Event('custom');
    var customEvt = new CustomEvent('customEvt', {
        // 通过这个属性传递参数
        detail: {
            name: 'tom',
            age: 12
        }
    });
    content.addEventListener('custom', (e) => {
        console.log('自定义事件被触发,无参数...');
        console.log(e);
    });
    content.addEventListener('customEvt', (e) => {
        console.log('自定义事件被触发,有参数...');
        console.log(e);
        console.log(e.detail);
    });
    // 点击时触发这个自定义事件
    content.addEventListener('click', (e) => {
        content.dispatchEvent(evt);
        content.dispatchEvent(customEvt);
    });

97.以下递归函数存在栈溢出的风险,请问如何优化?

function factorial(n){
    return n*factorial(n-1)
}

答案:

function factorial(n){
    return n > 1 ? n * factorial(n-1) : 1;
}

98.请实现一个计算最大公约数的函数

function greatestCommonDivisor(a,b){
//在这里编写代码
}
greatestCommonDivisor(8, 12) //4
greatestCommonDivisor(8, 16) //8
greatestCommonDivisor(8, 17) //1

解答:

function greatestCommonDivisor(a,b){
  var num=0;  
      while(b!=0){       
           num=a%b;  
           a=b;  
           b=num;  
      }  
      return a;
}

99.数组去重(如果数组中有 NaN)

Array.prototype.uniq = function () {
   var resArr = [];
   var flag = true;
      
   for(var i=0;i<this.length;i++){
       if(resArr.indexOf(this[i]) == -1){
           if(this[i] != this[i]){   

               //排除 NaN
              if(flag){
                   resArr.push(this[i]);
                   flag = false;
              }
           }else{
                resArr.push(this[i]);
           }
       }
   }
    return resArr;
}

100. 用 JavaScript 实现斐波那契数列函数,返回第 n 个斐波那契数。 f(1) = 1, f(2) = 1 等

function fibonacci(n) {
    if(n ==1 || n == 2){
        return 1
    }
    return fibonacci(n - 1) + fibonacci(n - 2);
}

101.根据包名,在指定空间中创建对象

输入描述:

namespace({a: {test: 1, b: 2}}, 'a.b.c.d')

输出描述:

{a: {test: 1, b: {c: {d: {}}}}}
function namespace(oNamespace, sPackage) {
    var properties = sPackage.split('.');
    var parent = oNamespace;
    for (var i = 0, lng = properties.length; i < lng; ++i) {
        var property = properties[i];
        if (Object.prototype.toString.call(parent[property])!== '[object Object]') {
            parent[property] = {};
        }
        parent = parent[property];
    }
    return oNamespace;
}

102.封装函数 f,使 f 的 this 指向指定的对象

function bindThis(f, oTarget) {
    return function(){
        var parames = Array.prototype.slice.call(arguments);
        return f.apply(oTarget,parames); //注意这里需要返回 f 的执行结果
    }

103.dom 节点查找

查找两个节点的最近的一个共同父节点,可以包括节点自身

输入描述:

oNode1 和 oNode2 在同一文档中,且不会为相同的节点

function commonParentNode(oNode1, oNode2) {
    if(oNode1.contains(oNode2)){
        return oNode1;
    }else if(oNode2.contains(oNode1)){
        return oNode2;
    }else{
        return commonParentNode(oNode1.parentNode,oNode2);
    }
}

104.关系型数组转换成树形结构对象

关系型数组:

var obj = [
    { id:3, parent:2 },
    { id:1, parent:null },
    { id:2, parent:1 },
]

期望结果:

o = {
  obj: {
    id: 1,
    parent: null,
    child: {
      id: 2,
      parent: 1,
      child: {
          id: ,3,
          parent: 2
      }
    }
  }
}

实现源码:

function treeObj(obj) {
  obj.map(item => {
    if (item.parent !== null) {
      obj.map(o => {
        if (item.parent === o.id) {
          if (!o.child) {
            o.child = [];
          }
          o.child.push(item);
          o.child = o.child;
        }
      });
    }
  });
  return obj.filter(item => item.parent === null)[0]
}

或者:

function treeObj(obj) {
  return obj.sort((a, b) => b.parent - a.parent)
      .reduce((acc, cur) => (acc ? { ...cur, child: acc } : cur));
}

105.JS 如何判断一组数字是否连续

// 当出现连续数字的时候以‘-’输出
[1, 2, 3, 4, 6, 8, 9, 10]

期望结果:

["1-4", 6, "8-10"]

实现代码:

判断是否连续:

var arrange = function(arr){
    var result = [],temp = [];
    arr.sort(function(source, dest){
        return source - dest;
    }).concat(Infinity).reduce(function(source, dest){
        temp.push(source);
        if(dest-source > 1){
            result.push(temp);
            temp = [];
        }
        return dest;
    });
    return result;
};

格式化实现:

var formatarr = function(arr) {
    var newArr = []
    var arr1 = arrange(arr)
    for (var i in arr1) {
        var str = '';
        if (arr1[i].length > 1) {
            str = arr1[i][0] + '-' + arr1[i][arr1[i].length - 1];
            newArr.push(str)
        } else {
            newArr.push(arr1[i][0]);
        }
   }
   return newArr;
}

106.创建子类 Child,使用原型和构造函数的方式继承父类 People 的方法,并调用 say 函数说出姓名和年龄。

父类:

function People(name,age){
     this.name=name;
     this.age=age;
     this.say=function(){
         console.log("我的名字是:"+this.name+"我今年"+this.age+"岁!");
     };
}

原型继承:

function Child(name, age){
    this.name = name;
    this.age = age;
}
Child.prototype = new People();
var child = new Child('Rainy', 20);
child.say()

构造函数继承:

function Child(name, age){
    People.call(this)
    this.name = name;
    this.age = age;
}
var child = new Child('Rainy', 20);
child.say()

组合继承:

function Child(name, age){
    People.call(this);
    this.name = name;
    this.age = age;
}
Child.prototype = People.prototype;
var child = new Child('Rainy', 20);
child.say()

组合继承优化:

function Child(name, age){
    People.call(this);
    this.name = name;
    this.age = age;
}
Child.prototype = Object.create(People.prototype);
Child.prototype.constructor = Child;
var child = new Child('Rainy', 20);
child.say()

结束语

以上就是今天为大家带来的 web 前端开发经典面试题整理,内容有点多,但是干货却是满满,大家可以收藏下来。每次复习都有一种温故而知新的感觉,学会的可以时常复习一下,毕竟时间长了人都是会遗忘的,大佬也可以适当的补充更好的方案帮助小伙帮们学到更多知识。

  animation 示例:

<style>
        .box{height:100px;width:100px;border:15px solid black;
            animation: changebox 10s ease-in-out   3 alternate paused;
            }
        .box:hover{
            animation-play-state: running;
        }
        @keyframes changebox {
            10% {  background:red;  }
            50% {  width:80px;  }
            70% {  border:15px solid yellow;  }
            100% {  width:180px;  height:180px;  }
        }
</style>
<body>
 <div class="box"></div>
</body>

11.事件冒泡的事件捕获

  事件冒泡,事件会从最内层的元素开始发生,一直向上传播,直到 document 对象;

  事件捕获则跟事件冒泡相反,事件会从 document 对象开始发生,直到最具体的元素;

12.GET 和 POST 的区别

  1.发送方式:GET 请求数据放在 url 上,即 HTTP 的协议头中;而 POST 把数据放在 HTTP 的包体中。

  2.大小的限制:GET 传的参数有长度的限制,因为浏览器对 url 的长度有限制;而 POST 理论上是没有限制的。

  3.安全性:GET 请求可被缓存,请求保存在浏览器的历史记录中;POST 则不能被缓存。与 POST相比,GET 的安全性较差,因为发送的数据是 URL 的一部分。

13.var 的变量提升的底层原理是什么?

  JS 引擎的工作方式是

  1)先解析代码,获取所有被声明的变量;

  2)然后再执行。

  也就是分为预处理和执行这两个阶段。

  变量提升:所有用 var 声明变量的语句都会被提升到代码头部。另外 function 也可看作变量声明,也存在变量提升的情况。

14.垂直水平居中的方式?

  1)定位

  父元素设置为:position:relative;

  子元素设置为:position:absolute;

  距上 50%,据左 50%,然后减去元素自身宽度的距离就可以实现

width: 100px;
height: 100px;
position: absolute;
left: 50%;
top: 50%;
margin: -50px 0 0 -50px;

2)flex 布局

display: flex; //flex 布局
justify-content: center; //使子项目水平居中
align-items: center; //使子项目垂直居中

3)tabel-cell

display: table-cell;
vertical-align: middle;//使子元素垂直居中
text-align: center;//使子元素水平居中

15.如何判断一个对象是否为数组

  1)Array.prototype.isPrototypeOf(obj)方法,判定 Array 是不是在 obj 的原型链中,如果是,则返回 true,否则 false;

  2)objinstanceofArray;

  3)Object.prototype.toString.call(obj);(==="[objectArray]")

  4)Array.isArray(obj);

16.行内元素和块级元素有哪些?img 属于什么元素?

1)块元素(block element)

address - 地址
blockquote - 块引用
center - 举中对齐块
dir - 目录列表
div - 常用块级容易,也是 css layout 的主要标签
dl - 定义列表
fieldset - form 控制组
form - 交互表单
h1 - 大标题
h2 - 副标题
h3 - 3 级标题
h4 - 4 级标题
h5 - 5 级标题
h6 - 6 级标题
hr - 水平分隔线
isindex - input prompt
menu - 菜单列表
noframes - frames 可选内容,(对于不支持 frame 的浏览器显示此区块内容
noscript - 可选脚本内容(对于不支持 script 的浏览器显示此内容)
ol - 排序表单
p - 段落
pre - 格式化文本
table - 表格
ul - 非排序列表

2)内联元素(inline element)

a - 锚点
abbr - 缩写
acronym - 首字
b - 粗体(不推荐)
bdo - bidi override
big - 大字体
br - 换行
cite - 引用
code - 计算机代码(在引用源码的时候需要)
dfn - 定义字段
em - 强调
font - 字体设定(不推荐)
i - 斜体
img - 图片
input - 输入框
kbd - 定义键盘文本
label - 表格标签
q - 短引用
s - 中划线(不推荐)
samp - 定义范例计算机代码
select - 项目选择
small - 小字体文本
span - 常用内联容器,定义文本内区块
strike - 中划线
strong - 粗体强调
sub - 下标
sup - 上标
textarea - 多行文本输入框
tt - 电传文本
u - 下划线
var - 定义变量

3)可变元素

可变元素为根据上下文语境决定该元素为块元素或者内联元素。
applet - java applet
button - 按钮
del - 删除文本
iframe - inline frame
ins - 插入的文本
map - 图片区块(map)
object - object 对象
script - 客户端脚本

  img、input 属于行内替换元素。height/width/padding/margin 均可用。效果等于块元素。

17.margin 塌陷?

  当两个盒子在垂直方向上设置 margin 值时,会出现塌陷现象

  解决方法:

  1.给父盒子添加 border

  2.给父盒子添加 padding-top

  3.给父盒子添加 overflow:hidden

  4.父盒子:position:fixed

  5.父盒子:display:table

  6.给子元素的前面添加一个兄弟元素 属性为:content:""; overflow:hidden;

  解决方法的主要原理就是设置盒子为 BFC

  BFC 为页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。

18.伪类与伪元素的区别

  1)伪类

  伪类用于选择 DOM 树之外的信息,或是不能用简单选择器进行表示的信息。前者包含那些匹配指定状态的元素,比如:link,:visited,:hover,:active;后者包含那些满足一定逻辑条件的 DOM 树中的元素,比如:first-child,:first-of-type,:target。

  2)伪元素

  伪元素为 DOM 树没有定义的虚拟元素。不同于其他选择器,它不以元素元素为最小选择单元,它选择的是元素制定单元。比如::before 表示选择元素内容的之前内容;::selection 表示选择元素被选中的内容。

  3)伪类/伪元素一览表

  <伪类如下>

:active      选择正在被激活的元素
:hover         选择被鼠标悬浮着元素
:link        选择未被访问的元素
:visited     选择已被访问的元素
:first-child 选择满足是其父元素的第一个子元素的元素    
:lang         选择带有指定 lang 属性的元素
:focus       选择拥有键盘输入焦点的元素
:enable      选择每个已启动的元素
:disable     选择每个已禁止的元素
:checked     选择每个被选中的元素    
:target      选择当前的锚点元素

<伪元素如下>

::first-letter    选择指定元素的第一个单词
::first-line      选择指定元素的第一行
::after           在指定元素的内容后面插入内容
::before          在指定元素的内容前面插入内容
::selection       选择指定元素中被用户选中的内容

19.介绍一下 JS 的基本数据类型

Undefined,Null,Boolean,Number,String

20.JavaScript 的 typeof 返回那些数据类型

undefined,string,boolean,number,symbol(ES6),object,function

21.介绍一下 JS 有哪些内置对象?

  数据封装类对象:Object、Array、Boolean、Number、String

  其他对象:Function、Argument、Math、Date、RegExp、Error

22。null 和 undefined 的区别

  1)null 表示一个对象被定义了,值为“空值”;undefined 表示不存在这个值。

  2)变量被定义了,但是没有赋值时,就等于 undefined。

  3)注意:在验证 null 时,要用===,因为==无法区分 null 和 undefined。

  typeofnull//"object"说明:null 是一个没有任何属性和方法的对象

23.对 JSON 的了解

  1)JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。

  2)它是基于 JavaScript 的一个子集。数据格式简单,易于读写,占用带宽小。如:{"age":"12","name":"back"}

24.列举 3 种强制类型转换和 2 种隐式类型转换

  强制:parseInt(),parseFloat(),Number(),(Boolean(),String())

  隐式:==,!

25.input 的 type 属性有哪些?

  text:文本框

  password:密码

  radio:单选按钮

  checkbox:复选框

  file:文件选择域

  hidden:隐藏域

  button:按钮

  reset:重置按钮

  submit:表单提交按钮

  image:图片按钮

26.IE 和标准下有哪些兼容性的写法

var ev = ev || window.event
document.documentElement.clientWidth || document.body.clientWidth
var target = ev.srcElement||ev.target

27.如何阻止事件冒泡

  ie:阻止冒泡 ev.cancelBubble=true;

  非 IEev.stopPropagation();

28.如何阻止默认事件

1)return false;

2) ev.preventDefault();

29.说说前端中的事件流

  事件流描述的是从页面接收事件的顺序,DOM2 级事件流包括下面几个阶段。

  事件捕获阶段

  处理事件阶段

  事件冒泡阶段

  (addEventListener:addEventListener 是 DOM2 级事件新增的指定事件处理程序的操作,这个方法接收 3 个参数:要处理的事件名,作为事件处理程序的函数和一个布尔值。最后的布尔值如果为 true,表示在捕获阶段调用事件处理程序;如果为 false,表示在冒泡阶段调用事件处理程序,默认为 false)

30.如何实现一个自适应的正方形

  1)CSS3vw 单位

  CSS3 中新增了一组相对于可视区域百分比的长度单位 vw、vh、vmin、vmax。其中 vw 是相对于视口宽度百分比的单位,1vw=1%viewportwidth,vh 是相对于视口高度百分比的单位,1vh=1%viewportheight;vmin 是相对于当前视口宽高中较小的一个的百分比单位,同理 vmax 是相对当前视口宽高中较大的一个百分比单位。之前也写过相关文章,大家可以查看《视口百分比长度 vh、vw、vi、vb、vmin、vmax 单位的了解

  代码实现:

.placeholder{
  width: 50vw;
  height: 50vw;
}

  优点:简洁方便

  缺点:浏览器兼容不好

  2)设置垂直方向的 padding 撑开容器

  margin、padding 的百分比数值是相对于父元素宽度计算的。由此可以发现只需要将元素垂直方向的 padding 值设定为与 width 相同的百分比就可以制作出自适应正方形了:

  代码实现:

.placeholder{
  width: 100%;
  padding-bottom:100%;
}

如果正方形中没有内容(相当于只是一个几何里面的正方形,并没有展示其他任何内容),一切看起来都很正常;但是,如果正方形中有其他内容(这种情况会更常见一些,比如说有一些文本和图片),此时容器的高度就会被拉伸,因为盒子模型中的 padding 是不包含在 content 中的,所以我们可以通过 height:0 解决这个问题;这种方案简洁明了,且兼容性好;但是除了填充内容后会出现问题以外,还有可能碰上 max-height 不收缩,于是第三种方案来了:

3)利用伪元素的 margin(padding)-top 撑开容器

在方案二中,我们利用百分比数值的 padding-bottom 属性撑开容器内部空间,但是这样做会导致在元素上设置的 max-height 属性失效;而失效的原因是 max-height 属性只限制于 height,也就是只会对元素的 contentheight 起作用。那么我们是不是能用一个子元素撑开 content 部分的高度,从而使 max-height 属性生效呢?我们来试试:

.placeholder {
    width: 20%;
    background-color: #000;
    /* overflow: hidden; */
    /* display: inline-block; */
    float: left;
    /*设置成 BFC 才能使 margin-top 撑起高度*/
}
.placeholder:after {
    content: '';
    display: block;
    margin-top: 100%;
    /* margin 百分比相对父元素宽度计算 */
}

31.js 中的位置关系

js 中的位置关系

  offsetParent:该属性返回一个对象的引用,这个对象是距离调用 offsetParent 的元素最近的(在包含层次中最靠近的),已进行过 CSS 定位的容器元素。如果这个容器元素未进行 CSS 定位,则 offsetParent 属性的取值为 body 元素的引用。当容器元素的 style.display 被设置为"none"时(译注:IE 和 Opera 除外),offsetParent 属性返回 null。

  top:该属性一般对用过 css 定位的元素有效(position 为“static”时为 auto,不产生效果),定义了一个 top 属性有效的元素(即定位元素)的上外边距边界与其包含块上边界之间的偏移。

  clientTop:元素上边框的厚度,当没有指定边框厚底时,一般为 0。

  scrollTop:位于对象最顶端和窗口中可见内容的最顶端之间的距离,简单地说就是滚动后被隐藏的高度。

  offsetTop:获取对象相对于由 offsetParent 属性指定的父坐标(css 定位的元素或 body 元素)距离顶端的高度。

  clientHeight:内容可视区域的高度,也就是说页面浏览器中可以看到内容的这个区域的高度,一般是最后一个工具条以下到状态栏以上的这个区域,与页面内容无关。

  scrollHeight:IE、Opera 认为 scrollHeight 是网页内容实际高度,可以小于 clientHeight。FF 认为 scrollHeight 是网页内容高度,不过最小值是 clientHeight。

  offsetHeight:获取对象相对于由 offsetParent 属性指定的父坐标(css 定位的元素或 body 元素)的高度。IE、Opera 认为 offsetHeight=clientHeight+滚动条+边框。FF 认为 offsetHeight 是网页内容实际高度,可以小于 clientHeight。offsetHeight 在新版本的 FF 和 IE 中是一样的,表示网页的高度,与滚动条无关,chrome 中不包括滚动条。

*诸如 left、clientLeft、offsetLeft、clientWidth、scrollWidth 等,和 top、height 类似,不再赘述。

  clientX、clientY:相对于浏览器窗口可视区域的 X,Y 坐标(窗口坐标),可视区域不包括工具栏和滚动条。IE 事件和标准事件都定义了这 2 个属性。

  pageX、pageY:类似于 event.clientX、event.clientY,但它们使用的是文档坐标而非窗口坐标。这 2 个属性不是标准属性,但得到了广泛支持。IE 事件中没有这 2 个属性。

  offsetX、offsetY:相对于事件源元素(target 或 srcElement)的 X,Y 坐标,只有 IE 事件有这 2 个属性,标准事件没有对应的属性。

  screenX、screenY:相对于用户显示器屏幕左上角的 X,Y 坐标。标准事件和 IE 事件都定义了这 2 个属性

32.Doctype 作用? 严格模式与混杂模式如何区分?它们有何意义?

Doctype 声明于文档最前面,告诉浏览器以何种方式来渲染页面,这里有两种模式,严格模式和混杂模式。

严格模式的排版和 JS 运作模式是以该浏览器支持的最高标准运行。

混杂模式,向后兼容,模拟老式浏览器,防止浏览器无法兼容页面。

33.link 标签和 import 标签的区别?

  link 属于 html 标签,而@import 是 css 提供的

  页面被加载时,link 会同时被加载,而@import 引用的 css 会等到页面加载结束后加载。

  link 是 html 标签,因此没有兼容性,而@import 只有 IE5 以上才能识别。

  link 方式样式的权重高于@import 的。

34.元素的文本省略号?

单行:

overflow:hidden;
text-overflow:ellipsis;
white-space:nowrap

  多行:

  1)直接用 css 属性设置(只有-webkit 内核才有作用)

display: -webkit-box
-webkit-box-orient:vertical
-webkit-line-clamp:3
overflow:hidden

  移动端浏览器绝大部分是 WebKit 内核的,所以该方法适用于移动端;

  -webkit-line-clamp 用来限制在一个块元素显示的文本的行数,这是一个不规范的属性(unsupportedWebKitproperty),它没有出现在 CSS 规范草案中。

  display:-webkit-box 将对象作为弹性伸缩盒子模型显示。

  -webkit-box-orient 设置或检索伸缩盒对象的子元素的排列方式。

  text-overflow:ellipsis 以用来多行文本的情况下,用省略号“…”隐藏超出范围的文本。

2)利用伪类

<style>
#txt{
  display: inline-block;
  height: 40px;
  width: 250px;
  line-height: 20px;
  overflow: hidden;
  font-size: 16px;
}
.t:after{
  display: inline;
  content: "...";
  font-size: 16px;
    
}
</style>
<div id="con">
  <span id="txt">文本溢出显示省略号,文本溢出显示省略号,文本溢出显示省略号,文本溢出显示省略</span>
  <span class="t"></span>
</div>

这种方法的缺点就是内容不足以超过位置的时候,还是会有…

35.XML 和 JSON 的区别?

  1)数据体积方面

  JSON 相对于 XML 来讲,数据的体积小,传递的速度更快些

  2)数据描述方面

  JSON 和 JavaScript 交互更加方便,更容易解析处理,更容易交互

  3)数据描述方面

  JSON 对数据的描述性比 XML 差

  4)传输速度方面

  JSON 的速度要远远快于 XML

36.前端需要注意哪些 SEO?

  合理的 title、description、keywords:搜索对着三项的权重逐个减少,title 值强调重点即可,重要关键词不要超过两次,而且要靠前,不同页面的 title 要有所不同;

  description 把页面的内容高度概括,长度合适,不可过分分堆砌关键词,不同页面的 description 有所不同;

  keywords 列举重要关键词即可;

// title 标题
<title>标题</title>
// keywords 关键词
<meta name="description" content="关键词 1,关键词 2,关键词 3">
// description 内容摘要
<meta name="description" content="网页的简述">

  语义化的 HTML 代码,符合 W3C 规范:语义化代码让搜索引擎容易理解网页;

  重要内容的 HTML 代码放在最前:搜索引擎抓取 HTML 顺序是从上到下,有的搜索引擎对抓取长度有限制,所以要保证重要内容一定会被抓取;

  重要内容不要用 JS 输出:爬虫不会执行 JS 获取内容;

  少用 iframe:搜索引擎不会抓取 iframe 中的内容;

  非装饰性图片必须加 alt;

  提高网站速度:网站速度是搜素引擎排序的一个重要指标;

37.HTTP 的几种请求方法用途?

  1)GET 方法

  发送一个请求来取得服务器上的某一资源

  2)POST 方法

  向 URL 指定的资源提交数据或附加新的数据

  3)PUT 方法

  跟 POST 方法很像,也是想服务器提交数据。但是,它们之间有不同。PUT 指定了资源在服务器上的位置,而 POST 没有

  4)HEAD 方法

  只请求页面的首部

  5)DELETE 方法

  删除服务器上的某资源

  6)OPTIONS 方法

  它用于获取当前 URL 所支持的方法。如果请求成功,会有一个 Allow 的头包含类似“GET,POST”这样的信息

  7)TRACE 方法

  TRACE 方法被用于激发一个远程的,应用层的请求消息回路

  8)CONNECT 方法

  把请求连接转换到透明的 TCP/IP 通道

38.如何进行网页性能优化?

  1)content 方面

  减少 HTTP 请求:合并文件、CSS 精灵图

  减少 DNS 查询:DNS 缓存、将资源分布到恰当数量的主机名

  减少 DOM 元素数量

  2)Server 方面

  使用 CDN

  配置 ETag

  对组件使用 Gzip 压缩

  3)Cookie 方面

  减少 cookie 大小

  4)CSS 方面

  将样式表放到页面顶部

  不使用 CSS 表达式

  使用不使用@import

  5)JavaScript 方面

  将脚本放到页面底部

  将 JavaScript 和 CSS 从外部引入

  压缩 JavaScript 和 CSS

  删除不需要的脚本

  减少 DOM 访问

  6)图片方面

  优化 CSS 精灵

  不要再 HTML 中拉伸图片

39.语义化的理解

  HTML 语义化就是让页面的内容结构化,便于对浏览器、搜索引擎解析;

  在没有 CSS 样式情况下也以一种文档格式显示,并且是容易阅读的;

  搜索引擎的爬虫依赖于标记来确定上下文的各个关键字的权重,利于 SEO;

  使阅读源代码的人更容易将网站分块,便于阅读维护理解;

40.WEB 标准以及 W3C 标准是什么

  标签闭合、标签小写、不乱嵌套、使用外链 CSS 和 JS、结构行为表现的分离

41.说说对作用域链的理解

  作用域链的作用是保证执行环境里有权访问的变量和函数是有序的,作用域链的变量只能向上访问,变量访问到 window 对象即被终止,作用域链向下访问变量是不被允许的;

  即作用域就是变量与函数的可访问范围,即作用域控制这变量与函数的可见性和生命周期;

42.如何实现瀑布流

  1)瀑布流布局的要求要进行布置的元素等宽;

  然后计算元素的宽度,

  与浏览器宽度之比,得到需要布置的列数;

  2)创建一个数组,长度为列数,

  里面的值为以已布置元素的总高度(最开始为0);

  3)然后将未布置的元素的依次布置到高度最小的那一列,

  就得到了瀑布流布局;

  4)滚动加载,scroll 事件得到 scrollTop,

  与最后盒子的 offsetTop 对比,

  符合条件就不断滚动加载。

  瀑布流布局核心代码:

/**
 * 实现瀑布流的布局
 * @param {string}parentBox
 * @param {string}childBox
 */
function waterFull(parentBox, childBox) {
    // 1. 求出父盒子的宽度
    //  1.1 获取所有的子盒子
    var allBox = $(parentBox).
         getElementsByClassName(childBox);
    // console.log(allBox);

    // 1.2 求出子盒子的宽度
    var boxWidth = allBox[0].offsetWidth;
    // console.log(boxWidth);

    // 1.3 获取窗口的宽度
    var clientW = document.
        documentElement.clientWidth;
    // console.log(clientW);

    // 1.4 求出总列数
    var cols = Math.floor(clientW / boxWidth);
    // console.log(cols);

    // 1.5 父盒子居中
    $(parentBox).style.width = cols * boxWidth + 'px';
    $(parentBox).style.margin = '0 auto';

    // 2. 子盒子定位
    //  2.1 定义变量
    var heightArr = [], boxHeight = 0, 
        minBoxHeight = 0, minBoxIndex = 0;

    // 2.2 遍历所有的子盒子
    for (var i = 0; i < allBox.length; i++) {
        // 2.2.1 求出每一个子盒子的高度
        boxHeight = allBox[i].offsetHeight;
        // console.log(boxHeight);
        // 2.2.2 取出第一行盒子的高度放入高度数组中
        if (i < cols) { // 第一行
            heightArr.push(boxHeight);
        } else { // 剩余行的盒子
            // 2.2.3 取出数组中最矮的高度
            minBoxHeight = _.min(heightArr);
            // 2.2.4 求出最矮高度对应的索引
            minBoxIndex = getMinBoxIndex(heightArr, minBoxHeight);
            // 2.2.5 盒子定位
            allBox[i].style.position = 'absolute';
            allBox[i].style.left = minBoxIndex * boxWidth + 'px';
            allBox[i].style.top = minBoxHeight + 'px';
            // 2.2.6 更新最矮的高度
            heightArr[minBoxIndex] += boxHeight;

        }
    }
}

/**
 * 根据内容取出在数组中对应的索引
 * @param {object}arr
 * @param {number}val
 * @returns {boolean}
 */

function getMinBoxIndex(arr, val) {
    for (var i = 0; i < arr.length; i++) {
        if (arr[i] === val) return i;
    }
}

/**
 * 判断是否具备加载子盒子的条件
 * @returns {boolean}
 */
function checkWillLoadImage() {
    // 1. 获取最后一个盒子
    var allBox = $('main').getElementsByClassName('box');
    var lastBox = allBox[allBox.length - 1];

    // 2. 求出高度
    var lastBoxDis = lastBox.offsetHeight * 0.5 + lastBox.offsetTop;

    // 3. 求出窗口的高度
    var clientH = document.documentElement.clientHeight;

    // 4. 求出页面滚动产生的高度
    var scrollTopH = scroll().top;

    // 5. 对比
    return lastBoxDis <= clientH + scrollTopH;
}

43.原生 JS 都有哪些方式可以实现两个页面间的通信?

  1)通过 url 地址栏传递参数;

  例如:点击列表中的每一条数据,跳转到一个详情页面,在 URL 中传递不同的参数区分不同的页面;

  2)通过本地存储 cookie、localStorage、sessionStorage;

  例如京东的登陆,把登陆后获得的页面信息存储到本地,其他页面需要用户信息的话就从本地的存储数据中获取;

  3)使用 iframe

  例如在A页面中嵌入B页面,在A中可以通过一些属性和实现方法和B页面的通信;

  4)利用 postMessage 实现页面间的通信

  例如父窗口往子窗口传递信息,子窗口往父窗口传递信息

44.ES6 中的 let,const,var 的区别是什么?

  var:声明全局变量;

  let:声明块级变量,即局部变量,定以后可以修改;

  const:用于声明常量,定义后不能再修改值或者引用值的常量,也具有块级作用域;

45.对数组进行去重,es5 或者 es6 方法

//es5 四种方式:

//方式一:
Array.prototype.unique1 = function() {
    // 1. 定义数组
    var temp = [];
    // 2. 遍历当前数组
    for(var i = 0; i < this.length; i++) {
        // 3.如果当前数组的第 i 已经保存进了临时数组,
        // 那么跳过,否则把当前项 push 到临时数组里面
        if (-1 === temp.indexOf(this[i])) {
            temp.push(this[i]);
        }
    }
    return temp;
};

//方式二:
Array.prototype.unique2 = function() {
    //1. hash 为 hash 表,r 为临时数组
    var hash = {}, temp=[];
    // 2.遍历当前数组
    for(var i = 0; i < this.length; i++)
    {
        // 3. 如果 hash 表中没有当前项
        if (!hash[this[i]])
        {
            // 4.存入 hash 表
            hash[this[i]] = true;
            // 5.把当前数组的当前项
            // push 到临时数组里面
            temp.push(this[i]);
        }
    }
    return temp;
};

//方式三:
Array.prototype.unique3 = function() {
    var n = [this[0]];
    for(var i = 1; i < this.length; i++){
        if (this.indexOf(this[i]) === i) {
            n.push(this[i]);
        }
    }
    return n;
};

//方式四:
Array.prototype.unique4 = function() {
    this.sort();
    var re=[this[0]];
    for(var i = 1; i < this.length; i++)
    {
        if( this[i] !== re[re.length-1])
        {
            re.push(this[i]);
        }
    }
    return re;
};

//es6 实现方式:

Array.prototype.unique =
Array.prototype.unique 
|| function () {
    return [...new Set(this)];
};

46.页面加载过程中可能触发哪些事件?它们的顺序是?

  页面加载时,大致可以分为以下几个步骤:

  1)开始解析 HTML 文档结构

  2)加载外部样式表及 JavaScript 脚本

  3)解析执行 JavaScript 脚本

  4)DOM 树渲染完成

  5)加载未完成的外部资源(如图片)

  6)页面加载成功

  执行顺序:

  1)documentreadystatechange 事件

  2)documentDOMContentLoaded 事件

  3)windowload 事件

47.什么是 CDN,CDN 对于网络有什么意义,它有什么的缺点?

  CDN 又称为内容分发网络;本意在于尽可能避开互联网上有可能影响数据传输速度和稳定性的瓶颈和环节,使内容传输的更快、更稳定。

  主要目的:解决因分布、带宽、服务器性能带来的访问延迟问题,适用于站点的加速、点播、直播等场景。使用户就近取得所需内容,解决 Internet 网络拥挤的状况,提高用户访问网站的响应速度和成功率。

  缺点:

  1)实施复杂,投资大

  2)目前大部分的 CDN 还只是对静态的内容加速,对动态加速效果不好;而双线对动态加速的效果跟静态是一样的。

48.vue-router 中$route 和$router 的区别?

  1)$route 为当前 router 跳转对象里面可以获取 name、path、query、params 等

  2)$router 为 VueRouter 实例,想要导航到不同 URL,则使用$router.push 方法

  返回上一个 history 也是使用$router.go 方法

49.vue 路由传参 query 与 params 两种方式的区别

query 要用 path 来引入,例如 ths.$router.push({ path:"detail",query:{id:"00"}}),接收参数为 this.$route.query.id,params 要用 name 来引入,例如 ths.$router.push({ name:"detail",params:{id:"00"}}),接收参数为 this.$route.params.id。以 query 传输的参数会在类似于 get 传参,在浏览器地址栏中显示参数。

50.描述一下渐进增强和优雅降级

  优雅降级(gracefuldegradation):一开始就构建站点的完整功能,然后针对浏览器测试和修复。

  渐进增强(progressiveenhancement):一开始只构建站点的最少特性,然后不断针对各浏览器追加功能。

51.为什么利用多个域名来请求网络资源会更有效?

  动静分离请求,使用不同的服务器处理请求,提高效率;

  突破浏览器的并发限制,同一时间针对同一域名下的请求有一定的数量限制。

  节约主域名的连接数,从而提高客户端网络带宽的利用率,优化页面响应。

52.HTML5 有哪些新特性、移除了哪些元素?

  1)绘画标签 canvas;

  2)用于媒介回放的 video 和 audio 元素;

  3)本地离线存储 localStorage 长期存储数据,浏览器关闭后数据不丢失;

  4)sessionStorage 的数据在浏览器关闭后自动删除;

  5)语义化更好的内容元素,比如 article、footer、header、nav、section;

  6)表单控件,calendar、data、time、email、url、search;

  7)webworker、websocket、Geolocation;

  移除的元素:

  1)纯表现的元素:basefont、big、center、font、s、strike、tt

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

53.display:none;与 visibility:hidden;的区别?

  相同点:它们都能让元素不可见‘

  不同点:

  display:none;会让元素完全从渲染树中消失,渲染的时候不占据任何空间;

  visibility:hidden;不会让元素从渲染树消失,渲染元素继续占据空间,只是内容不可见;

  display:none;是非继承属性,子孙节点的消失由于元素从渲染树消失造成,通过修改子孙节点属性无法显示;

  visibility:hodden;是继承属性,子孙节点的消失由于继承了 hidden,通过设置 visibility:visible;可以让子孙节点显示;

  修改常规流中元素的 display 通常会造成文档重排。修改 visibility 属性只会造成本元素的重绘;

  读屏器不会读取 display:none;元素内容;会读取 visibility:hidden;元素内容;

54.CSS 去掉 inline-block 元素间隙的几种方法?

  间隙是怎么来的:间隙是由换行或者回车导致的;只要把标签写成一行或者标签没有空格,就不会出现间隙;

  去除方法:

  方法一:

  元素间的间隙出现的原因,是元素标签之间的空格,把空格去掉间隙就会消失

<div class="itlike">
  <span>lhh</span><span>lhh</span>
</div>

  方法二:

  利用 HTML 注释标签

<div class="demo">
    <span>lhh</span><!-- 
    --><span>lhh</span>
</div>

  方法三:

  取消标签闭合

<div class="demo">
    <span>lhh
    <span>lhh
    <span>lhh
    <span>lhh
</div>

  方法四:

  在父容器上使用 font-size:0;可以消除间隙

<div class="demo">
    <span>lhh</span>
    <span>lhh</span>
    <span>lhh</span>
    <span>lhh</span>
</div>
.demo {font-size: 0;}

55.input 标签的 type 种类

button、checkbox、file、hidden、image、password、radio、reset、submit、text

56.apply,call,bind 有什么区别?

  三者都可以把一个函数应用到其他对象上,apply,call 是直接执行函数调用,bind 是绑定,执行需要再次调用。

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

  代码如下:

function Person() {
    }
    Person.prototype.sayName() { alert(this.name); }

    var obj = {name: 'michaelqin'}; // 注意这是一个普通对象,它不是 Person 的实例
    // 1) apply
    Person.prototype.sayName.apply(obj, [param1, param2, param3]);

    // 2) call
    Person.prototype.sayName.call(obj, param1, param2, param3);

    // 3) bind
    var liaoke = Person.prototype.sayName.bind(obj);    
    liaoke ([param1, param2, param3]); // bind 需要先绑定,再执行 
    liaoke (param1, param2, param3); // bind 需要先绑定,再执行

57.介绍一下 defineProperty,hasOwnProperty, propertyIsEnumerable

  Object.defineProperty(obj,prop,descriptor)用来给对象定义属性,有 value,writeable,enumerable,set/get,configurable,

  hasOwnProperty 用于检查某一属性是不是存在于对象本身,

  propertyIsEnumerable 用来检测某一属性是否可遍历,也就是能不能用 for…in 循环来取到。

58.JS 常用设计模式的实现思路(单例、工厂、代理、装饰、观察者模式等)

// 1) 单例: 任意对象都是单例,无须特别处理
    var obj = {name: 'michaelqin', age: 30};

 // 2) 工厂: 就是同样形式参数返回不同的实例
    function Person() { this.name = 'Person1'; }
    function Animal() { this.name = 'Animal1'; }

    function Factory() {}
    Factory.prototype.getInstance = function(className) {
        return eval('new ' + className + '()');
    }

    var factory = new Factory();
    var obj1 = factory.getInstance('Person');
    var obj2 = factory.getInstance('Animal');
    console.log(obj1.name); // Person1
    console.log(obj2.name); // Animal1

 // 3) 代理: 就是新建个类调用老类的接口,包一下
    function Person() { }
    Person.prototype.sayName = function() { console.log('michaelqin'); }
    Person.prototype.sayAge = function() { console.log(30); }

    function PersonProxy() { 
        this.person = new Person();
        var that = this;
        this.callMethod = function(functionName) {
            console.log('before proxy:', functionName);
            that.person[functionName](); // 代理
            console.log('after proxy:', functionName);
        }
    }

    var pp = new PersonProxy();
    pp.callMethod('sayName'); // 代理调用 Person 的方法 sayName()
    pp.callMethod('sayAge'); // 代理调用 Person 的方法 sayAge() 

  // 4) 观察者: 就是事件模式,比如按钮的 onclick 这样的应用.
    function Publisher() {
        this.listeners = [];
    }
    Publisher.prototype = {
        'addListener': function(listener) {
            this.listeners.push(listener);
        },

        'removeListener': function(listener) {
            delete this.listeners[listener];
        },

        'notify': function(obj) {
            for(var i = 0; i < this.listeners.length; i++) {
                var listener = this.listeners[i];
                if (typeof listener !== 'undefined') {
                    listener.process(obj);
                }
            }
        }
    }; // 发布者

    function Subscriber() {

    }
    Subscriber.prototype = {
        'process': function(obj) {
            console.log(obj);
        }
    }; // 订阅者

    var publisher = new Publisher();
    publisher.addListener(new Subscriber());
    publisher.addListener(new Subscriber());
    publisher.notify({name: 'michaelqin', ageo: 30}); // 发布一个对象到所有订阅者
    publisher.notify('2 subscribers will both perform process'); // 发布一个字符串到所有订阅者

59.处理字符串常用的十个函数

charAt()   // 返回在指定位置的字符。
concat()   // 连接字符串。
fromCharCode()   // 从字符编码创建一个字符串。
indexOf()  // 检索字符串。
match()   // 找到一个或多个正则表达式的匹配。
replace()   // 替换与正则表达式匹配的子串。
search()   // 检索与正则表达式相匹配的值。
slice()   // 提取字符串的片断,并在新的字符串中返回被提取的部分。
split()   // 把字符串分割为字符串数组。
substr()   // 从起始索引号提取字符串中指定数目的字符。
substring()   // 提取字符串中两个指定的索引号之间的字符。
toLocaleLowerCase()   // 把字符串转换为小写。
toLocaleUpperCase()   // 把字符串转换为大写。
toLowerCase()   // 把字符串转换为小写。
toUpperCase()   // 把字符串转换为大写。
toString()   // 返回字符串。
valueOf()   // 返回某个字符串对象的原始值。

60.如何判断一个变量是对象还是数组

function isObjArr(variable){
     if (Object.prototype.toString.call(value) === "[object Array]") {
            console.log('value 是数组');
       }else if(Object.prototype.toString.call(value)==='[object Object]'){//这个方法兼容性好一点
            console.log('value 是对象');
      }else{
          console.log('value 不是数组也不是对象')
      }
}

// 注意:千万不能使用 typeof 来判断对象和数组,因为这两种类型都会返回"object"。

61.ES5 的继承和 ES6 的继承有什么区别?

  ES5 的继承是通过 prototype 或构造函数机制来实现。

  ES5 的继承实质上是先创建子类的实例对象,然后再将父类的方法添加到 this 上(Parent.apply(this))。

  ES6 的继承机制实质上是先创建父类的实例对象 this(所以必须先调用父类的 super()方法),然后再用子类的构造函数修改 this。具体为 ES6 通过 class 关键字定义类,里面有构造方法,类之间通过 extends 关键字实现继承。子类必须在 constructor 方法中调用 super 方法,否则新建实例报错。因为子类没有自己的 this 对象,而是继承了父类的 this 对象,然后对其调用。如果不调用 super 方法,子类得不到 this 对象。

注意:super 关键字指代父类的实例,即父类的 this 对象。在子类构造函数中,调用 super 后,才可使用 this 关键字,否则报错。

62.下面的 ul,如何点击每一列的时候 alert 其 index?(闭包)

<ul id="test">
 <li>这是第一条</li>
 <li>这是第二条</li>
 <li>这是第三条</li>
 </ul>

// 方法一:
var lis=document.getElementById('test').getElementsByTagName('li');
for(var i=0;i<3;i++)
{
lis[i].index=i;
lis[i].onclick=function(){
alert(this.index);
};
}

//方法二:
var lis=document.getElementById('test').getElementsByTagName('li');
for(var i=0;i<3;i++)
{
lis[i].index=i;
lis[i].onclick=(function(a){
return function() {
alert(a);
}
})(i);
}

63.对于 MVVM 的理解

  MVVM 是 Model-View-ViewModel 的缩写。

  Model 代表数据模型,也可以在 Model 中定义数据修改和操作的业务逻辑。

  View 代表 UI 组件,它负责将数据模型转化成 UI 展现出来。

  ViewModel 监听模型数据的改变和控制视图行为、处理用户交互,

  简单理解就是一个同步 View 和 Model 的对象,连接 Model 和 View。

  在 MVVM 架构下,View 和 Model 之间并没有直接的联系,

  而是通过 ViewModel 进行交互,Model 和 ViewModel 之间的交互是双向的,

  因此 View 数据的变化会同步到 Model 中,而 Model 数据的变化也会立即反应到 View 上。

  ViewModel 通过双向数据绑定把 View 层和 Model 层连接了起来,

  而 View 和 Model 之间的同步工作完全是自动的,无需人为干涉,

  因此开发者只需关注业务逻辑,不需要手动操作 DOM,

  不需要关注数据状态的同步问题,

  复杂的数据状态维护完全由 MVVM 来统一管理。

64.解释 Vue 的生命周期

Vue 实例从创建到销毁的过程,就是生命周期。从开始创建、初始化数据、编译模板、挂载 Dom->渲染、更新->渲染、销毁等一系列过程,称之为 Vue 的生命周期。

  Vue 的生命周期包括:

  beforeCreate(创建前)在数据观测和初始化事件还未开始,

  created(创建后)完成数据观测,属性和方法的运算,初始化事件,$el 属性还没有显示出来;

  beforeMount(载入前)在挂载开始之前被调用,相关的 render 函数首次被调用,实例已完成以下的配置:编译模板,把 data 里面的数据和模板生成 html,注意此时还没有挂载 html 到页面上;

  mounted(载入后)在 el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用,实例已完成以下配置:用上面编译好的 html 内容替换 el 属性指向的 DOM 对象,完成模板中的 html 渲染到 html 页面中,此过程中进行 ajax 交互。

  beforeUpdate(更新前)在数据更新之前调用,发生在虚拟 DOM 重新渲染和打补丁之前,可以在该钩子中进一步地更改状态,不会触发附加的重渲染过程。

  updated(更新后)在由于数据更改导致的虚拟 DOM 重新渲染和打补丁之后调用。调用时,组件 DOM 已经更新,所以可以执行依赖于 DOM 的操作。然而在大多数情况下,应该避免在此期间更改状态,因为这可能会导致更新无限循环,该钩子在服务器渲染期间不被调用。

  beforeDestroy(销毁前)在实例销毁之前调用,实例仍然完全可用。

  destroyed(销毁后)在实例销毁之后调用。调用后,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务器端渲染期间不被调用。

65.为什么使用 Node.js,它有哪些优缺点?

  优点:

  事件驱动,通过闭包很容易实现客户端的生命活期。

  不用担心多线程,锁,并行计算的问题

  V8 引擎速度非常快

  对于游戏来说,写一遍游戏逻辑代码,前端后端通用

  缺点:

  node.js 更新很快,可能会出现版本兼容

  node.js 还不算成熟,还没有大制作

  node.js 不像其他的服务器,对于不同的链接,不支持进程和线程操作

66.什么是错误优先的回调函数?

  错误优先(Error-first)的回调函数(Error-FirstCallback)用于同时返回错误和数据。

  第一个参数返回错误,并且验证它是否出错;其他参数返回数据。

fs.readFile(filePath, function(err, data)
{
    if (err)
    {
        // 处理错误
        return console.log(err);
    }
    console.log(data);
});

67.使用 npm 有哪些好处?

  通过 npm,你可以安装和管理项目的依赖,

  并且能够指明依赖项的具体版本号。

  对于 Node 应用开发而言,

  可以通过 package.json 文件来管理项目信息,

  配置脚本,以及指明依赖的具体版本。

68.在 JavaScript 源文件的开头包含 use strict 有什么意义和好处?

  sestrict 是一种在 JavaScript 代码运行时自动实行更严格解析和错误处理的方法。(严格模式)

  将值分配给一个未声明的变量会自动创建该名称的全局变量。这是 JavaScript 中最常见的错误之一。在严格模式下,这样做的话会抛出错误。消除 this 强制。

  当检测到对象(例如,varobject={foo:"bar",foo:"baz"};)中重复命名的属性,或检测到函数中(例如,functionfoo(val1,val2,val1){})重复命名的参数时,严格模式会抛出错误,因此捕捉几乎可以肯定是代码中的 bug 可以避免浪费大量的跟踪时间。比 eval()更安全。

69.vuejs 与 angularjs 以及 react 的区别?

  与 AngularJS 的区别

  相同点:

  都支持指令:内置指令和自定义指令。

  都支持过滤器:内置过滤器和自定义过滤器。

  都支持双向数据绑定。

  都不支持低端浏览器。

  不同点:

  1.AngularJS 的学习成本高,比如增加了 DependencyInjection 特性,而 Vue.js 本身提供的 API 都比较简单、直观。

  2.在性能上,AngularJS 依赖对数据做脏检查,所以 Watcher 越多越慢。

  Vue.js 使用基于依赖追踪的观察并且使用异步队列更新。所有的数据都是独立触发的。

  对于庞大的应用来说,这个优化差异还是比较明显的。

  与 React 的区别

  相同点:

  React 采用特殊的 JSX 语法,Vue.js 在组件开发中也推崇编写.vue 特殊文件格式,对文件内容都有一些约定,两者都需要编译后使用。

  中心思想相同:一切都是组件,组件实例之间可以嵌套。

  都提供合理的钩子函数,可以让开发者定制化地去处理需求。

  都不内置列数 AJAX,Route 等功能到核心包,而是以插件的方式加载。

  在组件开发中都支持 mixins 的特性。

  不同点:

  React 依赖 VirtualDOM,而 Vue.js 使用的是 DOM 模板。React 采用的 VirtualDOM 会对渲染出来的结果做脏检查。

  Vue.js 在模板中提供了指令,过滤器等,可以非常方便,快捷地操作 VirtualDOM。

70.标签 keep-alive 的作用是什么?

<keep-alive></keep-alive> 包裹动态组件时,会缓存不活动的组件实例,主要用于保留组件状态或避免重新渲染。

71.WeakMap 和 Map 的区别?

  WeakMap 结构与 Map 结构基本类似,唯一的区别是它只接受对象作为键名(null 除外),不接受其他类型的值作为键名,而且键名所指向的对象,不计入垃圾回收机制。

  WeakMap 最大的好处是可以避免内存泄漏。一个仅被 WeakMap 作为 key 而引用的对象,会被垃圾回收器回收掉。

  WeakMap 拥有和 Map 类似的 set(key,value)、get(key)、has(key)、delete(key)和 clear()方法,没有任何与迭代有关的属性和方法。

72.http 和 https 的基本概念?

  http:超文本传输协议,是互联网上应用最为广泛的一种网络协议,是一个客户端和服务器端请求和应答的标准(TCP),用于从 WWW 服务器传输超文本到本地浏览器的传输协议,它可以使浏览器更加高效,使网络传输减少。

  https:是以安全为目标的 HTTP 通道,简单讲是 HTTP 的安全版,即 HTTP 下加入 SSL 层,HTTPS 的安全基础是 SSL,因此加密的详细内容就需要 SSL。

  https 协议的主要作用是:建立一个信息安全通道,来确保数组的传输,确保网站的真实性。

73.git fetch 和 git pull 的区别?

  git pull:相当于是从远程获取最新版本并 merge 到本地

  git fetch:相当于是从远程获取最新版本到本地,不会自动 merge

74.介绍一下对浏览器内核的理解?

  主要分成两部分:渲染引擎(layout engineer 或 Rendering Engine)和 JS 引擎。

  渲染引擎:负责取得网页的内容(HTML、XML、图像等等)、

  整理讯息(例如加入 CSS 等),以及计算网页的显示方式,然后会输出至显示器或打印机。

  浏览器的内核的不同对于网页的语法解释会有不同,所以渲染的效果也不相同。

  所有网页浏览器、电子邮件客户端以及其它需要编辑、显示网络内容的应用程序都需要内核。

  JS 引擎:解析和执行 javascript 来实现网页的动态效果。

  最开始渲染引擎和 JS 引擎并没有区分的很明确,后来 JS 引擎越来越独立,内核就倾向于只指渲染引擎。

75.什么是微格式

  微格式(Microformats)是一种让机器可读的语义化 XHTML 词汇的集合,是结构化数据的开放标准。

  是为特殊应用而制定的特殊格式

  优点:将智能数据添加到网页上,让网站内容在搜索引擎结果界面可以显示额外的提示。

76.数据绑定基本的实现

// 实现一个方法,可以给 obj 所有的属性添加动态绑定事件,当属性值发生变化时会触发事件
let obj = {
  key_1: 1,
  key_2: 2
}
function func(key) {
  console.log(key + ' 的值发生改变:' + this[key]);
}
bindData(obj, func);
obj.key_1 = 2; // 此时自动输出 "key_1 的值发生改变:2"
obj.key_2 = 1; // 此时自动输出 "key_2 的值发生改变:1"

答案:

function bindData(obj, fn) {
  for (let key in obj) {
    Object.defineProperty(obj, key, {
      set(newVal) {
        if (this.value !== newVal) {
          this.value = newVal;
          fn.call(obj, key);
        }
      },
      get() {
        return this.value;
      }
    })
  }
}

77.数据结构处理

// 有一个祖先树状 json 对象,当一个人有一个儿子的时候,其 child 为其儿子对象,如果有多个儿子,child 为儿子对象的数组。

请实现一个函数,找出这个家族中所有有多个儿子的人的名字(name),输出一个数组。

列子:
// 样例数据
let data = {
  name: 'jack',
  child: [
    { name: 'jack1' },
    {
      name: 'jack2',
      child: [{
        name: 'jack2-1',
        child: { name: 'jack2-1-1' }
      }, {
        name: 'jack2-2'
      }]
    },
    {
      name: 'jack3',
      child: { name: 'jack3-1' }
    }
  ]
}


// 答案:

// 用递归
function findMultiChildPerson(data) {
  let nameList = [];

  function tmp(data) {
    if (data.hasOwnProperty('child')) {
      if (Array.isArray(data.child)) {
        nameList.push(data.name);
        data.child.forEach(child => tmp(child));
      } else {
        tmp(data.child);
      }
    }
  }
  tmp(data);
  return nameList;
}

// 不用递归
function findMultiChildPerson(data) {
  let list = [data];
  let nameList = [];

  while (list.length > 0) {
    const obj = list.shift();
    if (obj.hasOwnProperty('child')) {
      if (Array.isArray(obj.child)) {
        nameList.push(obj.name);
        list = list.concat(obj.child);
      } else {
        list.push(obj.child);
      }
    }
  }
  return nameList;
}

78.JavaScript 如何实现二分法查找?

  二分法查找,也称折半查找,是一种在有序数组中查找特定元素的搜索算法。

  查找过程可以分为以下步骤:

  (1)首先,从有序数组的中间的元素开始搜索,

  如果该元素正好是目标元素(即要查找的元素),则搜索过程结束,否则进行下一步。

  (2)如果目标元素大于或者小于中间元素,

  则在数组大于或小于中间元素的那一半区域查找,然后重复第一步的操作。

  (3)如果某一步数组为空,则表示找不到目标元素。

  代码:

// 非递归算法
 function binary_search(arr, key) {
   var low = 0,
   high = arr.length - 1;
   while(low <= high){
   var mid = parseInt((high + low) / 2);
       if(key == arr[mid]){
           return  mid;
       }else if(key > arr[mid]){
           low = mid + 1;
       }else if(key < arr[mid]){
           high = mid -1;
       }else{
           return -1;
        }
   }
};

 var arr = [1,2,3,4,5,6,7,8,9,10,11,23,44,86];
 var result = binary_search(arr,10);
 alert(result); // 9 返回目标元素的索引值

 // 递归算法
 function binary_search(arr,low, high, key) {
     if (low > high){
       return -1;
        }
     var mid = parseInt((high + low) / 2);
      if(arr[mid] == key){
        return mid;
     }else if (arr[mid] > key){
        high = mid - 1;
        return binary_search(arr, low, high, key);
     }else if (arr[mid] < key){
        low = mid + 1;
        return binary_search(arr, low, high, key);
     }
};

 var arr = [1,2,3,4,5,6,7,8,9,10,11,23,44,86];
 var result = binary_search(arr, 0, 13, 10);
 alert(result); // 9 返回目标元素的索引值

79.有一楼梯共 M 级,刚开始时你在第一级,若每次只能跨上一级或二级,要走上第 M 级,共有多少种走法?

  这个问题要倒过来看,要到达 n 级楼梯,只有两种方式,从(n-1)级或(n-2)级到达的。

  所以可以用递推的思想去想这题,假设有一个数组 s[n],那么 s[1]=1(由于一开始就在第一级,只有一种方法),s[2]=1(只能从 s[1]上去没有其他方法)。

  那么就可以推出 s[3]~s[n]了。

  下面继续模拟一下,s[3]=s[1]+s[2],

  因为只能从第一级跨两步,或者第二级跨一步。

function cStairs(n) {
    if(n === 1 || n === 2) {
        return 1;
    } else {
        return cStairs(n-1) + cStairs(n-2)
    }
}

80.递归设计。 实现一个函数,给该函数一个 DOM 节点,函数访问其所有子元素(所有子元素,不仅仅是直接子元素),每次访问子元素的时候,并为其传一个 callback?

//访问一个 DOM tree,是一个经典的深度优先搜索的算法
function Traverse(DOM,callback) {
    callback(DOM);
    var list = DOM.children;
    Array.prototype.forEach.apply(list,(item)=>{
        Traverse(item,callback); //递归
    })
}

81.介绍一下对 webpack 的认识?

  WebPack 是一个模块打包工具,可以使用 WebPack 管理模块依赖,并编绎输出模块们所需的静态文件。

  它能够很好地管理、打包 Web 开发中所用到的 HTML、javaScript、CSS 以及各种静态文件(图片、字体等),让开发过程更加高效。

  对于不同类型的资源,webpack 有对应的模块加载器。webpack 模块打包器会分析模块间的依赖关系,最后生成了优化且合并后的静态资源。

  webpack 的两大特色:

  1)codesplitting(可以自动完成)

  2)loader 可以处理各种类型的静态文件,并且支持串联操作

  webpack 是以 commonJS 的形式来书写脚本,但对 AMD/CMD 的支持也很全面,方便旧项目进行代码迁移。

  webpack 具有 requireJs 和 browserify 的功能,但仍有很多自己的新特性:

  1)对 CommonJS、AMD、ES6 的语法做了兼容

  2)对 js、css、图片等资源文件都支持打包

  3)串联式模块加载器以及插件机制,让其具有更好的灵活性和扩展性,例如提供对 CoffeeScript、ES6 的支持

  4)有独立的配置文件 webpack.config.js

  5)可以将代码切割成不同的 chunk,实现按需加载,降低了初始化时间

  6)支持 SourceUrls 和 SourceMaps,易于调试

  7)具有强大的 Plugin 接口,大多是内部插件,使用起来比较灵活

  8)webpack 使用异步 IO 并具有多级缓存。这使得 webpack 很快且在增量编译上更加快

82.关于 HTTP2.0 的认识

  HTTP/2 引入了“服务端推(serverpush)”的概念,它允许服务端在客户端需要数据之前就主动地将数据发送到客户端缓存中,从而提高性能。

  HTTP/2 提供更多的加密支持,HTTP/2 使用多路技术,允许多个消息在一个连接上同时交差。

  它增加了头压缩(headercompression),因此即使非常小的请求,其请求和响应的 header 都只会占用很小比例的带宽。

83.对 AMD 和 Commonjs 的理解?

  CommonJS 是服务器端模块的规范,nodejs 采用了这个规范。

  CommonJS 规范加载模块是同步的,也就是说,只有加载完成,才能执行后面的操作。

  AMD 规范则是非同步加载模块,允许指定回调函数。

  AMD 推荐的风格通过返回一个对象做为模块对象,CommonJS 的风格通过对 module.exports 或 exports 的

  属性赋值来达到暴露模块对象的目的。

84.mongoDB 和 MySQL 的区别?

  MySQL 是传统的关系型数据库,MongoDB 则是非关系型数据库

  mongodb 以 JSON 结构(二进制)进行存储,对海量数据存储有着很明显的优势。

  对比传统关系型数据库,NoSQL 有着非常显著的性能和扩展性优势,与关系型数据库相比,MongoDB 的优点有:

  ①弱一致性(最终一致),更能保证用户的访问速度:

  ②文档结构的存储方式,能够更便捷的获取数据。

85.讲讲 304 缓存的原理?

  服务器首先产生 ETag,服务器可在稍后使用它来判断页面是否已经被修改。

  本质上,客户端通过将该记号传回服务器,要求服务器验证其(客户端)缓存。

  304 是 HTTP 状态码,服务器用来标识这个文件没修改,不返回内容,浏览器在接收到个状态码后,会使用浏览器已缓存的文件。

  客户端请求一个页面 A。

  服务器返回页面 A,并在给 A 加上一个 ETag。

  客户端展现该页面,并将页面连同 ETag 一起缓存。

  客户再次请求页面 A,并将上次请求时服务器返回的 ETag 一起传递给服务器。

  服务器检查该 ETag,并判断出该页面自上次客户端请求之后还未被修改,直接返回响应 304(未修改——NotModified)和一个空的响应体。

86.用 node 模拟客户端发起请求?

var http = require("http");
var request = http.request({
    host:"localhost",
    port:"8080",
    path:"/request",
    method:"post"
},function(res){
    res.on("data",function(chunk){
        console.log(chunk.toString());
    });
});
request.write("user=zhang&pass=111");
request.end("请求结束");//结束本次请求

87.CommonJS 中的 require/exports 和 ES6 中的 import/export 区别?

  CommonJS 模块的重要特性是加载时执行,即脚本代码在 require 的时候,就会全部执行。

  一旦出现某个模块被”循环加载”,就只输出已经执行的部分,还未执行的部分不会输出。

  ES6 模块是动态引用,如果使用 import 从一个模块加载变量,那些变量不会被缓存,而是成为一个指向被加载模块的引用,需要开发者自己保证,真正取值的时候能够取到值。

  import/export 最终都是编译为 require/exports 来执行的。

  CommonJS 规范规定,每个模块内部,module 变量代表当前模块。

  这个变量是一个对象,它的 exports 属性(即 module.exports)是对外的接口。

  加载某个模块,其实是加载该模块的 module.exports 属性。

  export 命令规定的是对外的接口,必须与模块内部的变量建立一一对应关系。

88.数组方法

  1)join()把数组上午所有元素放入一个字符串。元素通过指定的分隔符进行分隔。

  该方法只接收一个参数,用作分隔符的字符串,然后返回包含所有数组项的字符串,如果不给 join()方法传入任何值,则使用逗号作为分隔符。

var a = [1,2,3];
console.log(a.join());//'1,2,3'
console.log(a.join(' '));//'1 2 3'
console.log(a.join(''));//'123'
var b = new Array(10);
b.join('-');//'---------',9 个连字符组成的字符串

  注意:如果 join()方法的参数是 undefined,标准浏览器以逗号为分隔符返回字符串,而 IE7-浏览器以"undefined"为分隔符返回字符串;

  如果数组中某一项的值是 null 或者 undefined,则该值在 join()方法返回的结果中以空字符串表示。

  2)push()方法可以接收任意数量的参数,把它们逐个添加到数组末尾,并且返回修改后数组的长度。

var a = [];
console.log(a,a.push(1));//[1] 1
console.log(a,a.push('a'));//[1,'a'] 2
console.log(a,a.push(true, {}));//[1,'a',true,{}] 4
console.log(a,a.push([5,6]));//[1,'a',true,{},[5,6]] 5

  3)pop()方法从数组末尾移除最后一项,减少数组的 length,然后返回移除的项。

var a = ['a', 'b', 'c'];
console.log(a,a.pop()); // ['a', 'b'] 'c'

  注意:给 pop 参数传其他数字不起作用,也不报错。还是只删除最后一项;

  对空数组使用 pop()方法,不会报错,而是返回 undefined

  4)shift()方法移除数组中的第一个项并返回该项,同时数组的长度减 1

var a = ['a', 'b', 'c'];
console.log(a,a.shift());//['b', 'c'] 'a'
var arr6 = [1];
console.log(arr6,arr6.shift()); //[] 1

  注意:对空数组使用 shift()方法,不会报错,而是返回 undefined

  5)unshift()方法在数组前面添加任意个项并返回新数组长度。

var a = ['a', 'b', 'c'];
console.log(a,a.unshift('x')); //['x', 'a', 'b', 'c'] 4

  注意:当传入多个参数时,是一次性插入。最终的数组中插入的元素的顺序和它们在参数列表中的顺序一致;

  在 IE-7 浏览器中,unshift()方法的返回值总是 undefined

  6)reserve()方法用于反转数组的顺序,返回经过排序之后的数组;而原来数组的顺序也发生改变。

var array = [1,2,4,3,5];
console.log(array,array.reverse());//[5,3,4,2,1] [5,3,4,2,1]
var array = ['str',true,3];
console.log(array,array.reverse());//[3,true,'str'] [3,true,'str']

  7)sort()按照字符编码的顺序进行排序。sort()方法会调用每个数组项的 toString()方法,然后比较得到的字符串排序,返回经过排序之后的数组,而原数组顺序也发生改变。

var array = [2,1,4,3,5];
console.log(array,array.sort());//[1,2,3,4,5] [1,2,3,4,5]
var array = ['3str',3,2,'2'];
console.log(array,array.sort());//[2, "2", 3, "3str"] [2, "2", 3, "3str"]

  注意:如果数组包含 undefined 元素,它们会被排到数组的尾部;

  arrayObject.sort(sortby)参数可选。规定排序顺序。必须是函数。比较函数接收两个参数,如果第一个参数应该位于第二个之前则返回一个负数,如果两个参数相等则返回 0,如果第一个参数应该位于第二个之后则返回一个正数。

  8)concat()方法基于当前数组中的所有项创建一个新的数组,先创建当前数组一个副本,然后将接收到的参数添加到这个副本的末尾,最后返回新构建的数组。所以 concat()不影响原数组。

// 如果不给 concat()方法传递参数时,它只是复制当前的数组;
var arr = [1,2,3];
console.log(arr,arr.concat()); //[1,2,3] [1,2,3]

// 如果参数是一个或多个数组,则该方法会将这些数组中的每一项都添加到结果数组中;
console.log(arr,arr.concat([6,7,8],[77,33,44]));
//[1, 2, 3] [1, 2, 3, 6, 7, 8, 77, 33, 44]
var arr1 = [4,5,6];
console.log(arr,arr.concat(arr1)); //[1,2,3] [1,2,3,4,5,6]

// 如果传递的值不是数组,这些值就会被简单地添加到结果数组的末尾。console.log(arr,arr.concat(4,5));//[1,2,3] [1,2,3,4,5]
console.log(arr,arr.concat(4,[5,[6,7]]));
//[1,2,3] [1, 2, 3, 4, 5, [6,7]]

  浅拷贝

  如果不提供参数,concat()方法返回当前数组的一个浅拷贝。

// 该方法实际只复制了数组的第一维。
// 数组第一维存放的是第二维的引用,而第二维才是实际存放他们的内容
var numbers = [1,2];
var newNumbers = numbers.concat();
console.log(numbers,newNumbers);//[1,2] [1,2]
numbers[0] = 0;
console.log(numbers,newNumbers);//[0,2] [1,2]
var numbers = [[1,2]];
var newNumbers = numbers.concat();
console.log(numbers,newNumbers);//[[1,2]] [[1,2]]
numbers[0][0] = 0;
console.log(numbers,newNumbers);//[[0,2]] [[0,2]]

  9)slice()方法基于当前数组中的一个或多个项创建一个新数组,接受一个或两个参数,最后返回新数组,所以 slice()不影响原数组。

  slice(start,end)方法需要两个参数 start 和 end,返回这个数组从 start 位置到 end 位置(不包含)的一个子数组,左闭右开。

  注意:a.如果 end 为 undefined 或不存在,则返回从 start 位置到数组结尾的所有项;

  b.如果没有参数,则返回原数组,即返回当前数组的一个浅拷贝;

  10)splice()方法用于删除原数组的一部分成员,并可以在被删除的位置添加入新的数组成员,该方法会改变原数组。

  splice()返回一个由删除元素组成的数组,或者如果没有删除元素就返回一个空数组

  splice(start,number…)的第一个参数 start 指定了插入或删除的起始位置,第二个参数 number 指定了应该从数组中删除的元素的个数,如果后面还有更多的参数,则表示这些就是要被插入数组的新元素。

  11)indexOf(search,start)方法接收 search 和 start 两个参数,返回 search 首次出现的位置,如果没有找到则返回-1,start 代表从 start 位置开始寻找。

  12)lastIndexOf(search,start)方法从右向左查找。

  接收 search 和 start 两个参数,返回 search 第一次出现的位置,如果没有找到则返回-1

  13)reduce()方法需要两个参数,第一个是执行化简操作的函数,化简函数的任务就是用某种方法把两个值组合或化简为一个值,并返回化简后的值。

var total = [0, 1, 2, 3].reduce(function(sum, value) {
  return sum + value;
}, 0);
// total is 6

  reduceRight()则从右到左执行对应的化简函数

  14)map()方法对数组中的每一项运行给定的函数,返回每次函数调用的结果组成的数组,

  map 方法还可以接受第二个参数,表示回调函数执行时 this 所指向的对象。

  15)forEach()方法对数组中的每一项运行给定的函数,这个方法没有返回值。本质上和 for 循环迭代数组一样。如果需要有返回值,一般使用 map 方法。

  forEach()方法除了接受一个必须的回调函数参数,第二个参数还可以接受一个可选的上下文参数(改变回调函数里面的 this 指向)

array.forEach(callback(currentValue, index, array){
    //do something
}, this)

  16)filter()方法对数组中的每一项运行给定的函数,返回该函数会返回 true 的项组成的数组。该方法常用于查询符合条件的所有数组项。

  filter()方法还可以接受第二个可选的上下文参数(改变回调函数里面的 this 指向)

var arr= [1,10,20,30]
var brr = arr.filter((item)=>{
    return item>10;
})
//[20,30]

  17)some()方法对数组中的每一项运行给定函数,如果该函数对任一项返回 true,则返回 true。并且当且仅当数值中的所有元素调用判定函数都返回 false,它才返回 false

  注意:在空数组上调用 some()方法会返回 false

const isBiggerThan10 = (element, index, array) => {
  return element > 10;
}

[2, 5, 8, 1, 4].some(isBiggerThan10);  
// false

[12, 5, 8, 1, 4].some(isBiggerThan10); 
// true

  18)every()方法对数组中的每一项运行给定函数,如果函数对每一项都返回 true,则返回 true;只要有一项返回 false,则返回 false

  19)fill()方法,用一个固定值填充一个数组中起始索引到终止索引内的全部元素

arr.fill(value, start, end)
var numbers = [1, 2, 3]
numbers.fill(1);
// results in [1, 1, 1]

  20)find()方法返回数组中满足提供的测试函数的第一个元素的值

function isBigEnough(element) {
    return element >= 15;
}
[12, 5, 8, 130, 44].find(isBigEnough); // 130

  21)findIndex()方法返回数组中满足提供的测试函数的一个元素的索引

function isBigEnough(element) {
  return element >= 15;
}
[12, 5, 8, 130, 44].findIndex(isBigEnough); 
//'3'

  22)includes()方法用来判断一个数组是否包含一个指定的值,如果是,则返回 true,如果没有则返回 false

let a = [1, 2, 3];
a.includes(2); 
// true 
a.includes(4); 
// false

  23)toLocaleString()方法返回一个字符串表示数组中的元素。数组中的元素将使用各自的 toLocaleString 方法转成字符串,这些字符串将使用一个特定语言环境的字符串(例如一个逗号",")隔开

var number = 1337;
var date = new Date();
var myArr = [number, date, "foo"];
var str = myArr.toLocaleString(); 
console.log(str); 
// 输出 "1,337,2019/2/15 下午 8:32:24,foo"

  24)copyWithin(target,start,end)方法浅复制数组的一部分到同一数组的另一个位置

  25)Array.isArray()方法用于确定传递的值是否是一个 Array

Array.isArray([]) => true;
Array.isArray({}) => false;

  26)Array.of()

Array.of(7);       // [7] 
Array.of(1, 2, 3); // [1, 2, 3]

Array(7);          // [ , , , , , , ]
Array(1, 2, 3);    // [1, 2, 3]

  27)Array.from()

  对伪数组或可迭代对象(包括 arguments Array,Map,Set,String…)转换成数组对象

  语法 Array.from(arrayLike,mapFn,thisArg)

  arrayLike

  想要转换成数组的伪数组对象或可迭代对象。

  mapFn(可选参数)

  如果指定了该参数,新数组中的每个元素会执行该回调函数。

  thisArg(可选参数)

  可选参数,执行回调函数 mapFn 时 this 对象。

  返回值

  一个新的数组实例

89.数组降维

方法一:

function flattenDeep(arr) {
        arr = "" + arr;  // 或者 arr = arr.toString();

        arr = arr.split(",");
        arr = arr.map(Number)
        return arr;
    }
flattenDeep([1, [[2],[3, [4]], 5]]);

方法二:

function flattenDeep(arr) {
    if(!Array.isArray(arr))
        return [arr];
    return arr.reduce((prev,cur) => {        
        return [...prev, ...flattenDeep(cur)];
    },[]);
}

flattenDeep([1, [[2], [3, [4]], 5]]);fang

方法三:

function flattenDeep(arr){
    while(arr.some(item=>Array.isArray(item)){
        arr = [].concat(...arr);
    }
    return arr;
}

90.栈的压入和弹出

  输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列 1,2,3,4,5 是某栈的压入顺序,序列 5,4,3,2,1 或 3,2,1 是该压栈序列对应的一个弹出序列,但 4,3,5,1,2 就不可能是该压栈序列的弹出序列。

function IsPopOrder(pushV,popV){
    if(pushV.length === 0) return false;
    var stack = []; // 模拟栈
    for(var i = 0, j = 0; i < pushV.length;){
        stack.push(pushV[i]);
        i += 1;
        // 压入栈的值需要被弹出
        while(j < popV.length && stack[stack.length-1] === popV[j]){
            stack.pop();
            j++;
            if(stack.length === 0) break;
        }
    }
    return stack.length === 0;
}

91.利用栈模拟队列

思路:

  • 对栈 A 添加数据。
  • 如果栈 B 为空,循环将栈 A 中内容弹出放入栈 B,并弹出栈 B 最后一项
  • 如果栈 B 不为空,则直接弹出栈 B 的最后一项
var stackA = [];
var stackB = [];

function push(node){
    stackA.push(node);
}
function pop(){
    if(!stackB.length){
        while(stackA.length){
            stackB.push(stackA.pop());
        }
    }
    return stackB.pop();
}

92.连续最长不重复字符串

  在一个字符串中找出连续的不重复的最大长度的字符串,解决这类问题的思路:

  • 利用循环叠加字符串,直到出现重复为止
  • 每一次叠加,记录下来最大长度的字符串
// 连续最长不重复字符串
function getMaxLenStr(str) {
    var cur = [];
    var maxLenStr = '';
    for(var i = 0; i < str.length; i++) {
        if(!cur.includes(str[i])) {
            cur.push(str[i]);
        } else {
            cur = []; // 置为空
            cur.push(str[i]);
        }
        // 存储最大长度的字符串
        if(maxLenStr.length < cur.length) {
            maxLenStr = cur.join('');
        }        
    }
    return maxLenStr;
}
getMaxLenStr('ababcabcde'); // abcde

93.求一个数组当中,连续子向量的最大和。

function FindGreatestSumOfSubArray(arr) {
    let sum = arr[0];
    let max = arr[0];
    for(let i = 1; i < arr.length; i++) {
        if(sum < 0) {
            sum = arr[i];
        }else{
            sum += arr[i];
        }
        // 记录最大值
        if(max < sum) {
            max = sum;
        }
    }
    return max;
}

94.给定一个编码字符,按编码规则进行解码,输出字符串

  编码规则:coount[letter],将 letter 的内容 count 次输出,count 是 0 或正整数,letter 是区分大小写的纯字母。

  实例:

  • const s= 3[a]2[bc]; decodeString(s); // 返回 ‘aaabcbc’
  • const s= 3[a2[c]]; decodeString(s); // 返回 ‘accaccacc’
  • const s= 2[ab]3[cd]ef; decodeString(s); // 返回 ‘ababcdcdcdef’

  思路:

  使用栈这种数据结构,如果 push 的内容为‘]’,则循环 pop 字符,直到碰到’[‘,然后将 pop 出来的字符串按规则整理后,重新 push 进栈中,最后将栈内的内容拼接成字符串输出即可。

function decodeString(str) {
    let stack = []; // 存储字符串的栈
    for (let i = 0; i < str.length; i++) {
        let cur = str[i];
        if (cur !== ']') {
            stack.push(cur);
        } else { // 弹出
            let count = 0;
            let loopStr = [];
            let popStr = '';
            while ((popStr = stack.pop()) !== '[') {
                loopStr.unshift(popStr);
            }
            count = stack.pop();
            // 添加结果
            let item = '';
            for (let i = 0; i < count; i++) {
                item += loopStr.join('');
            }
            stack.push(...(item.split('')));
        }
    }
    return stack.join('');
}

95.['1', '2', '3'].map(parseInt) 的运行结果

  答案为:[1,NaN,NaN]

  解析:

arr.map(functioncallback(currentValue[,index[,array]]){//Returnelementfornew_array}[,thisArg])

  这个 callback 一共可以接收三个参数,其中第一个参数代表当前被处理的元素,而第二个参数代表该元素的索引。

  而 parseInt 则是用来解析字符串的,使字符串成为指定基数的整数。

  -parseInt(string,radix)

  接收两个参数,第一个表示被处理的值(字符串),第二个表示为解析时的基数。

  parseInt('1',0)//radix 为 0 时,且 string 参数不以“0x”和“0”开头时,按照 10 为基数处理。这个时候返回 1

  parseInt('2',1)//基数为 1(1 进制)表示的数中,最大值小于 2,所以无法解析,返回 NaN

  -parseInt('3',2)//基数为 2(2 进制)表示的数中,最大值小于 3,所以无法解析,返回 NaN

  map 函数返回的是一个数组,所以最后结果为[1,NaN,NaN]

96.自定义事件

var content = document.querySelector('.content');
    // 自定义事件
    var evt = new Event('custom');
    var customEvt = new CustomEvent('customEvt', {
        // 通过这个属性传递参数
        detail: {
            name: 'tom',
            age: 12
        }
    });
    content.addEventListener('custom', (e) => {
        console.log('自定义事件被触发,无参数...');
        console.log(e);
    });
    content.addEventListener('customEvt', (e) => {
        console.log('自定义事件被触发,有参数...');
        console.log(e);
        console.log(e.detail);
    });
    // 点击时触发这个自定义事件
    content.addEventListener('click', (e) => {
        content.dispatchEvent(evt);
        content.dispatchEvent(customEvt);
    });

97.以下递归函数存在栈溢出的风险,请问如何优化?

function factorial(n){
    return n*factorial(n-1)
}

答案:

function factorial(n){
    return n > 1 ? n * factorial(n-1) : 1;
}

98.请实现一个计算最大公约数的函数

function greatestCommonDivisor(a,b){
//在这里编写代码
}
greatestCommonDivisor(8, 12) //4
greatestCommonDivisor(8, 16) //8
greatestCommonDivisor(8, 17) //1

解答:

function greatestCommonDivisor(a,b){
  var num=0;  
      while(b!=0){       
           num=a%b;  
           a=b;  
           b=num;  
      }  
      return a;
}

99.数组去重(如果数组中有 NaN)

Array.prototype.uniq = function () {
   var resArr = [];
   var flag = true;
      
   for(var i=0;i<this.length;i++){
       if(resArr.indexOf(this[i]) == -1){
           if(this[i] != this[i]){   

               //排除 NaN
              if(flag){
                   resArr.push(this[i]);
                   flag = false;
              }
           }else{
                resArr.push(this[i]);
           }
       }
   }
    return resArr;
}

100. 用 JavaScript 实现斐波那契数列函数,返回第 n 个斐波那契数。 f(1) = 1, f(2) = 1 等

function fibonacci(n) {
    if(n ==1 || n == 2){
        return 1
    }
    return fibonacci(n - 1) + fibonacci(n - 2);
}

101.根据包名,在指定空间中创建对象

输入描述:

namespace({a: {test: 1, b: 2}}, 'a.b.c.d')

输出描述:

{a: {test: 1, b: {c: {d: {}}}}}
function namespace(oNamespace, sPackage) {
    var properties = sPackage.split('.');
    var parent = oNamespace;
    for (var i = 0, lng = properties.length; i < lng; ++i) {
        var property = properties[i];
        if (Object.prototype.toString.call(parent[property])!== '[object Object]') {
            parent[property] = {};
        }
        parent = parent[property];
    }
    return oNamespace;
}

102.封装函数 f,使 f 的 this 指向指定的对象

function bindThis(f, oTarget) {
    return function(){
        var parames = Array.prototype.slice.call(arguments);
        return f.apply(oTarget,parames); //注意这里需要返回 f 的执行结果
    }

103.dom 节点查找

查找两个节点的最近的一个共同父节点,可以包括节点自身

输入描述:

oNode1 和 oNode2 在同一文档中,且不会为相同的节点

function commonParentNode(oNode1, oNode2) {
    if(oNode1.contains(oNode2)){
        return oNode1;
    }else if(oNode2.contains(oNode1)){
        return oNode2;
    }else{
        return commonParentNode(oNode1.parentNode,oNode2);
    }
}

104.关系型数组转换成树形结构对象

关系型数组:

var obj = [
    { id:3, parent:2 },
    { id:1, parent:null },
    { id:2, parent:1 },
]

期望结果:

o = {
  obj: {
    id: 1,
    parent: null,
    child: {
      id: 2,
      parent: 1,
      child: {
          id: ,3,
          parent: 2
      }
    }
  }
}

实现源码:

function treeObj(obj) {
  obj.map(item => {
    if (item.parent !== null) {
      obj.map(o => {
        if (item.parent === o.id) {
          if (!o.child) {
            o.child = [];
          }
          o.child.push(item);
          o.child = o.child;
        }
      });
    }
  });
  return obj.filter(item => item.parent === null)[0]
}

或者:

function treeObj(obj) {
  return obj.sort((a, b) => b.parent - a.parent)
      .reduce((acc, cur) => (acc ? { ...cur, child: acc } : cur));
}

105.JS 如何判断一组数字是否连续

// 当出现连续数字的时候以‘-’输出
[1, 2, 3, 4, 6, 8, 9, 10]

期望结果:

["1-4", 6, "8-10"]

实现代码:

判断是否连续:

var arrange = function(arr){
    var result = [],temp = [];
    arr.sort(function(source, dest){
        return source - dest;
    }).concat(Infinity).reduce(function(source, dest){
        temp.push(source);
        if(dest-source > 1){
            result.push(temp);
            temp = [];
        }
        return dest;
    });
    return result;
};

格式化实现:

var formatarr = function(arr) {
    var newArr = []
    var arr1 = arrange(arr)
    for (var i in arr1) {
        var str = '';
        if (arr1[i].length > 1) {
            str = arr1[i][0] + '-' + arr1[i][arr1[i].length - 1];
            newArr.push(str)
        } else {
            newArr.push(arr1[i][0]);
        }
   }
   return newArr;
}

106.创建子类 Child,使用原型和构造函数的方式继承父类 People 的方法,并调用 say 函数说出姓名和年龄。

父类:

function People(name,age){
     this.name=name;
     this.age=age;
     this.say=function(){
         console.log("我的名字是:"+this.name+"我今年"+this.age+"岁!");
     };
}

原型继承:

function Child(name, age){
    this.name = name;
    this.age = age;
}
Child.prototype = new People();
var child = new Child('Rainy', 20);
child.say()

构造函数继承:

function Child(name, age){
    People.call(this)
    this.name = name;
    this.age = age;
}
var child = new Child('Rainy', 20);
child.say()

组合继承:

function Child(name, age){
    People.call(this);
    this.name = name;
    this.age = age;
}
Child.prototype = People.prototype;
var child = new Child('Rainy', 20);
child.say()

组合继承优化:

function Child(name, age){
    People.call(this);
    this.name = name;
    this.age = age;
}
Child.prototype = Object.create(People.prototype);
Child.prototype.constructor = Child;
var child = new Child('Rainy', 20);
child.say()

结束语

以上就是今天为大家带来的 web 前端开发经典面试题整理,内容有点多,但是干货却是满满,大家可以收藏下来。每次复习都有一种温故而知新的感觉,学会的可以时常复习一下,毕竟时间长了人都是会遗忘的,大佬也可以适当的补充更好的方案帮助小伙帮们学到更多知识。

「点点赞赏,手留余香」

17

给作者打赏,鼓励TA抓紧创作!

微信微信 支付宝支付宝

还没有人赞赏,快来当第一个赞赏的人吧!

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。
码云笔记 » web前端开发经典面试题整理

2 评论

  1. 金三银四,找工作

回复 码云 取消回复