前端面试题,求职看过来!!

前言


文章对大部分知识点做了总结,当然也不会面面俱到,有一些具体的实现步骤和底层代码并没有展示出来。 相关知识点的详解都有各自的参考链接,可以查缺补漏。

一、HTML


1.doctype 的作用是什么?
  • DOCTYPE 是 html5 标准网页声明,且必须声明在HTML文档的第一行。来告知浏览器的解析器用什么文档标准解析这个文档,不同的渲染模式会影响到浏览器对于 CSS 代码甚至 JavaScript 脚本的解析。
2.HTML、XHTML、XML 有什么区别?
  • HTML:HyperText Markup Language超文本标记语言,HTML是一种基本的WEB网页设计语言。
  • XML主要用于存储数据和结构,JSON作用类似,但更加轻量高效,XML是一种跨平台的,与软、硬件无关的,处理与传输信息的工具。
  • XHTML(可扩展超文本标记语言)是一个基于XML的标记语言。
3.常用的 meta 标签?
  • charset,用于描述 HTML 文档的编码形式
<meta charset="UTF-8" >
  • http-equiv,相当于http 的文件头作用,比如下面的代码就可以设置 http 的缓存过期日期
<meta http-equiv="expires" content="Wed, 20 Jun 2019 22:33:00 GMT">
  • viewport,控制视口的大小和比例
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
4.script 标签中 defer 和 async 的区别?
  • defer:script 被异步加载后并不会立刻执行,而是等待文档被解析完毕后执行。
  • async:脚本加载完毕后立即执行。
5.前端储存的方式?
  • cookies: 兼容性好,请求头自带 cookie 方便,缺点是大小只有4k,自动请求头加入 cookie 浪费流量,每个 domain 限制20个 cookie,使用起来麻烦需要自行封装。
  • localStorage:HTML5 加入的以键值对(Key-Value)为标准的方式,优点是操作方便,永久性储存(除非手动删除),大小为5M,兼容IE8+。
  • sessionStorage:与 localStorage 基本类似,区别是 sessionStorage 当页面关闭后会被清理,而且与 cookie、localStorage 不同,他不能在所有同源窗口中共享,是会话级别的储存方式。
  • IndexedDB:NoSQL 数据库,用键值对进行储存,可以进行快速读取操作,非常适合 web 场景,同时用 JavaScript 进行操作会非常方便。
6.iframe有那些缺点?
  • iframe会阻塞主页面的Onload事件,会影响页面的并行加载;
  • 搜索引擎的检索程序无法解读这种页面,不利于SEO;
  • 改进:通过javascript动态给iframe添加src属性值,这样可以绕开以上两个问题。

二、CSS


1.css盒模型

标准模型:宽高计算不包含 padding 和 border ;通过 box-sizing: content-box; 来设置(浏览器默认)。
IE模型:宽高计算包含 padding 和 border ;通过 box-sizing: border-box; 来设置。

2.BFC(块状格式化上下文)

特点:

  • 是一个独立的容器,里面的元素和外面的元素互不影响;

  • BFC垂直方向的边距会发生重叠;

  • BFC 区域不会与浮动元素区域重叠;

  • 计算 BFC 高度时,浮动元素也参与计算。
    创建方式:

  • float 值不为 none;

  • position 的值不为 static 或 relative;

  • display 为 inline-box, table, table-cell 等;

  • overflow 不为 visible
    作用:

  • 清除浮动;

  • 防止同一 BFC 容器中的相邻元素间的外边距重叠问题;

3.实现垂直居中布局
  • translate
{
            position:absolute;
            top:50%;
            left:50%;
            -webkit-transform: translate(-50%, -50%);
            -moz-transform: translate(-50%, -50%);
            -ms-transform: translate(-50%, -50%);
            -o-transform: translate(-50%, -50%);
            transform: translate(-50%, -50%);
}
  • table
    父级元素:{ display:table;}
    子级元素: { display:table-cell;vertical-align:middle }
  • flex
    父级元素:{ display:flex;flex-direction:row;justify-content:center;align-items:center;}
    子级元素:{flex:1}

方法很多,不一一赘述

常见各种布局实例分析-推荐文章

4.分析比较 opacity: 0、visibility: hidden、display: none 优劣和适用场景。
  • 结构上:display:none 会从渲染树中消失,元素不占据空间且无法点击;visibility: hidden 不会从渲染树中消失,元素继续占据空间但无法点击;opacity: 0 不会从渲染树消失,元素占据空间且可点击。
  • 继承性:display: none 和 opacity: 0 是非继承属性;父元素设置了 display:none 或 opacity: 0,子元素无论怎么设置都无法显示;visibility: hidden 会被子元素继承,并且子元素可以通过设置设置 visibility: visible; 来取消隐藏。
  • 性能:display: none 会引起重排,性能消耗较大;visibility: hidden 会引起重绘,性能消耗相对较小; opacity: 0 会重建图层,性能较高。
5.link 标签和 import 标签的区别
  • link属于XHTML标签,link引用的css于页面同步加载。
  • @import是css提供的一种方式,@import等到页面全部加载完加载,在CSS2.1以下不支持。
  • link 方式样式的权重高于 @import 的;link 可以使用 js 动态引入,@import不行;
6.移动端 Retina 1px 像素问题的解决方案
7.文本显示行数控制
  • 单行
overflow:hidden;
text-overflow:ellipsis;
white-space:nowrap;
  • 多行
overflow: hidden;
text-overflow: ellipsis;        // 超出显示'...'
display: -webkit-box;           // 将元素作为弹性伸缩盒子模型显示 。
-webkit-line-clamp: 2;          // 用来限制在一个块元素显示的文本的行数
-webkit-box-orient: vertical;   // 设置或检索伸缩盒对象的子元素的排列方式
8. 清除浮动的方式
.clearfix:after {
  visibility: hidden;
  display: block;
  font-size: 0;
  content: " ";
  clear: both;
  height: 0;
clear:both
9.transition 和 animate 有何区别?
  • transition:用于做过渡效果,没有帧概念,只有开始和结束状态,性能开销较小。
  • animate:用于做动画,有帧的概念,可以重复触发且有中间状态,性能开销较大。
10.实现一个扇形
.sector {
  width: 0;
  height: 0;
  border-width: 50px;
  border-style: solid;
  border-color: red transparent transparent;
  border-radius: 50px;
}

三、JS


1.JS 的内置类型
  • 基本类型:null、undefined、boolean、number、string、symbol
  • 对象(Object):引用类型

tips:NaN也属于number类型,并且NaN不等于自身。

2.类型判断
  • Typeof

tip:typeof对于基本类型,除了 null 都可以显示正确的类型;对于对象,除了函数都会显示 object

  • Object.prototype.toString

js温故而知新-类型判断–文章推荐

3.原型和原型链的理解
  • 原型:每个函数都有 prototype 属性,该属性指向原型对象;使用原型对象的好处是所有对象实例共享它所包含的属性和方法。
  • 原型链:主要解决了继承的问题;每个对象都拥有一个原型对象,通过__proto__ 指针指向其原型对象,并从中继承方法和属性,同时原型对象也可能拥有原型,这样一层一层,最终指向 null。

下图展示了构造函数、原型对象、实例和原型链的关系:
在这里插入图片描述

4.闭包
  • 闭包是指有权访问另一个函数作用域中的变量的函数。
  • 闭包会使得函数内部的变量都被保存在内存中,造成较大的内存开销,因此不要滥用闭包。解决的方法是在退出函数之前将不使用的局部变量置为 null ;

经典面试题:改造下面的代码,使之输出0 - 9

for (var i = 0; i< 10; i++){
	setTimeout(() => {
		console.log(i);
    }, 1000)
}

方法一、利用 setTimeout 函数的第三个参数,会作为回调函数的第一个参数传入
for (var i = 0; i < 10; i++) {
  setTimeout(i => {
    console.log(i);
  }, 1000, i)
}

方法二、使用 let 变量 的特性
for (let i = 0; i < 10; i++) {
  setTimeout(() => {
    console.log(i);
  }, 1000)
}
等价于
for (let i = 0; i < 10; i++) {
  let _i = i;// const _i = i;
  setTimeout(() => {
    console.log(_i);
  }, 1000)
}

方法三、利用函数自执行的方式,把当前 for 循环过程中的 i 传递进去,构建出块级作用域。
for (var i = 0; i < 10; i++) {
  (i => {
    setTimeout(() => {
      console.log(i);
    }, 1000)
  })(i)
}
6.this指向问题

this 的指向取决于函数以哪种方式调用:

  • 作用函数调用:非严格模式下 this 指向全局对象,严格模式为 undefined;
  • 作用方法调用:this 指向调用函数的对象;
  • 构造函数调用:this 指向 new 创建出来的实例对象;
  • call()和apply:它们的第一个参数为 this 的指向;
  • 补充:箭头函数中的 this
function a() {
    return () => {
        return () => {
        	console.log(this)
        }
    }
}
console.log(a()()())

箭头函数其实是没有 this 的,这个函数中的 this 只取决于他外面的第一个不是箭头函数的函数的 this。在这个例子中,因为调用 a 符合前面代码中的第一个情况,所以 this 是 window。并且 this 一旦绑定了上下文,就不会被任何代码改变。

7.call 和 apply 的实现
Function.prototype.call2 = function(context) {
    var context = context || window;
    context.fn = this;

    var args = [];
    for(var i = 1, len = arguments.length; i < len; i++) {
        args.push('arguments[' + i + ']');
    }
    
    var result = eval('context.fn(' + args + ')');
    
    delete context.fn
    return result;
}

Function.prototype.apply = function (context, arr) {
    var context = Object(context) || window;
    context.fn = this;

    var result;
    if (!arr) {
        result = context.fn();
    }
    else {
        var args = [];
        for (var i = 0, len = arr.length; i < len; i++) {
            args.push('arr[' + i + ']');
        }
        result = eval('context.fn(' + args + ')')
    }

    delete context.fn
    return result;
}

call()和apply()的实现–推荐文章

8.bind 的实现
Function.prototype.bind2 = function (context) {

    if (typeof this !== "function") {
      throw new TypeError("error");
    }

    var self = this;
    var args = Array.prototype.slice.call(arguments, 1);
    
    // 通过一个空函数作一个中转,避免绑定函数的 prototype 的属性被修改
    var fNOP = function () {};

    var fBound = function () {
        var bindArgs = Array.prototype.slice.call(arguments);
        return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs));
    }

    fNOP.prototype = this.prototype;
    fBound.prototype = new fNOP();
    return fBound;
}
9.new 的实现原理

new 操作符做了什么?

  • 创建一个空对象
  • 然后让这个空对象的__proto__指向函数的原型prototype
  • 执行构造函数中的代码,构造函数中的this指向该对象
  • 如果构造函数有返回值,则以该对象作为返回值。若没有return或return了基本类型,则将新对象作为返回值
function objectFactory() {

    var obj = new Object(),

    Constructor = [].shift.call(arguments);

    obj.__proto__ = Constructor.prototype;

    var ret = Constructor.apply(obj, arguments);

    return typeof ret === 'object' ? ret : obj;

};
10.深浅拷贝
  • 浅拷贝——如果被拷贝对象的元素是基本类型,就会拷贝出一份,并且互不影响。而如果被拷贝对象的元素是对象或者数组,就只会拷贝对象和数组的引用,此时若是在新旧对象上进行修改,都会相互影响。
// 数组浅拷贝:slice()、concat()
// 对象浅拷贝:Object.assign()、ES6的扩展运算符
  • 深拷贝——完全的拷贝一个对象,即使嵌套了对象,两者也互相分离,修改对象的属性,也不会影响到另一个。
// 递归实现
function clone(source) {
    var target = {};
    for(var i in source) {
        if (source.hasOwnProperty(i)) {
            if (typeof source[i] === 'object') {
                target[i] = clone(source[i]); // 如果是引用类型,则继续遍历
            } else {
                target[i] = source[i];
            }
        }
    }

    return target;
}

当然这只是简单的实现,没有考虑到特殊的情况,如对象或数组中的函数,正则等特殊类型的拷贝等。

// JSON.parse(JSON.stringify)
var arr = [
   { value: 1 },
   { value: 2 },
   { value: 3 }
];
var copyArr = JSON.parse(JSON.stringify(arr))
copyArr[0].value = 0;
console.log(arr);       // [{value: 1}, { value: 2 }, { value: 3 }]
console.log(copyArr);   // [{value: 0}, { value: 2 }, { value: 3 }]

上面这种方法简单粗暴,不能拷贝函数。

深拷贝的终极探索–文章推荐

11.防抖和节流的实现(简易版)
  • 防抖:触发高频事件后n秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间
funtion debounce(fn) {
    // 创建一个标记用来存放定时器的返回值
    let timeout = null;
    return function() {
        // 每次触发事件时都取消之前的延时调用方法
        clearTimeout(timeout);
        // 然后又创建一个新的 setTimeout, 这样就能保证 1000ms 间隔内如果重复触发就不会执行 fn 函数
        timeout = setTimeout(() => {
            fn.apply(this, arguments);
        }, 1000);
    };
}
  • 节流:高频事件触发,但在n秒内只会执行一次,所以节流会稀释函数的执行频率
function throttle(fn) {
    // 通过闭包保存一个标记
    let canRun = true;
    return function(){
        // 每次开始执行函数时都先判断标记是否为 true,不为 true 则 return
        if (!canRun) return;
        // 上一次定时器执行完后 canRun 为 true,所以要先设置为false
        canRun = false;
        setTimeout(() => {
            fn.apply(this, arguments);
            // 最后在 setTimeout 执行完毕后再把标记设置为true(关键)表示可以执行下一次循环了。当定时器没有执行的时候标记永远是 false,在开头被 return 掉
            canRun = true;
        }, 1000)
    }
}
12.ES5继承的实现
// 组合继承
function SuperType(name) {
    this.name = name;
    this.colors = ['red', 'blue', 'green'];
}
SuperType.prototype.sayName = function() {
    console.log(this.name);
}

function SubType(name, age) {
    SuperType.call(this, name);
    this.age = age;
}
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;

SubType.prototype.sayAge = function() {
    console.log(this.age);
}
13.JS 异步解决方案的发展历程以及优缺点
  • 回调函数(callback)
ajax('XXX1', () => {
    // callback 函数体
    ajax('XXX2', () => {
        // callback 函数体
        ajax('XXX3', () => {
            // callback 函数体
        })
    })
})

优点:解决了同步的问题,缺点:回调地狱,不能用 try catch 捕获错误,不能 return.

  • Promise
ajax('XXX1')
  .then(res => {
      // 操作逻辑
      return ajax('XXX2')
  }).then(res => {
      // 操作逻辑
      return ajax('XXX3')
  }).then(res => {
      // 操作逻辑
  })

优点:解决了回调地狱的问题,缺点:无法取消 Promise ,错误需要通过回调函数来捕获。

  • Generator
function *fetch() {
    yield ajax('XXX1', () => {})
    yield ajax('XXX2', () => {})
    yield ajax('XXX3', () => {})
}
let it = fetch()
let result1 = it.next()
let result2 = it.next()
let result3 = it.next()

// 配合 co 库使用
const co = require('co')

function *fetch() {
    yield ajax('XXX1', () => {})
    yield ajax('XXX2', () => {})
    yield ajax('XXX3', () => {})
}

co(fetch()).then(data => {
    //code
}).fetch(err => {
    //code
})

优点:可以控制函数的执行,配合自动执行器 co 模块 简化了手动执行的步骤,缺点:不配合 co 函数库的话使用起来比较麻烦。

  • async/await
// async其实是一个语法糖,它的实现就是将 Generator 函数和自动执行器(co),包装在一个函数中
async function test() {
  // 以下代码没有依赖性的话,完全可以使用 Promise.all 的方式
  // 如果有依赖性的话,其实就是解决回调地狱的例子了
  await fetch('XXX1')
  await fetch('XXX2')
  await fetch('XXX3')
}
read().then((data) => {
    //code
}).catch(err => {
    //code
});

优点:代码清晰,不用像 Promise 写一大堆 then 链,处理了回调地狱的问题,缺点:await 将异步代码改造成同步代码,如果多个异步操作没有依赖性而使用 await 会导致性能上的降低。

14.Promise的简单实现

promise 的使用(有关 Promise 的详细用法,可参考阮一峰老师的ES6文档

var promise = new Promise((resolve,reject) => {
    if (操作成功) {
        resolve(value)
    } else {
        reject(error)
    }
})
promise.then(function (value) {
    // success
},function (value) {
    // failure
})

简单实现

function myPromise(constructor) {
    let self = this;
    self.status = "pending"   // 定义状态改变前的初始状态
    self.value = undefined;   // 定义状态为resolved的时候的状态
    self.reason = undefined;  // 定义状态为rejected的时候的状态
    function resolve(value) {
       if(self.status === "pending") {
          self.value = value;
          self.status = "resolved";
       }
    }
    function reject(reason) {
       if(self.status === "pending") {
          self.reason = reason;
          self.status = "rejected";
       }
    }
    // 捕获构造异常
    try {
       constructor(resolve,reject);
    } catch(e) {
       reject(e);
    }
}

添加 then 方法

myPromise.prototype.then = function(onFullfilled,onRejected) {
   let self = this;
   switch(self.status) {
      case "resolved":
        onFullfilled(self.value);
        break;
      case "rejected":
        onRejected(self.reason);
        break;
      default:       
   }
}

var p = new myPromise(function(resolve,reject) {
    resolve(1)
});
p.then(function(x) {
    console.log(x) // 1
})

Promise实现原理这篇文章讲的很仔细,比较好理解。

四、网络基础

未完待更新

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值