面试题总结

基础篇

HTML && CSS 基础

  1. flex 常用的方法和属性有哪些?

    1
    2
    3
    4
    5
    flex-direction: row | row-reverse | column | column-reverse;
    flex-wrap: nowrap | wrap | wrap-reverse;
    justify-content: flex-start | flex-end | center | space-between | space-around;
    align-items: flex-start | flex-end | center | baseline | stretch;
    align-content: flex-start | flex-end | center | space-between | space-around | stretch;

    Flex 布局教程:语法篇

  2. HTML 语义化

    HTML 语义化是指仅仅从 HTML 元素上就能看出页面的大致结构,HTML 语义化便是在抛开样式之后,页面能有一个友好的展示效果。我们力求让页面有良好的结构,让页面的元素有含义,同时利于被搜索引擎解析,利于 SEO。

    HTML 语义化的建议:

    • 少使用无意义的

      标签;

    • 设置 标签的 alt 属性,给 标签设置 title 属性,利于 SEO;

    • 在页面的标题部分使用

      ~

      标签,不需要给它们加多余的样式;

    • 与表单、有序列表、无序列表相关的标签不要单独使用。

    H5 新增的语义化标签:

  3. CSS 盒模型

    由于浏览器的差异性,盒模型分为标准盒模型和IE盒模型,它们的呈现方式和对盒子大小的计算略有不同。

    标准盒模型

    • 元素的 width、height 只包含内容 content,不包含 border 和 padding 值;

    • 盒子的大小由元素的宽高、边框和内边距决定。

    IE盒模型

    • 元素的 width、height 不仅包括 content,还包括 border 和 padding;

    • 盒子的大小取决于 width、height,修改 border 和 padding 值不能改变盒子的大小。

  4. 动画的最小间隔时间设置为多少合适?

    多数显示器默认频率是60Hz,即1秒刷新60次,所以理论上最小间隔为1/60*1000ms = 16.7ms

  5. rem && em

    • em:1em与当前元素的字体大小相同。CSS样式被应用之前,浏览器给网页设置的默认基础字体大小是16像素,这意味着对一个元素来说1em的计算值默认为16像素。但是要小心—em单位是会继承父元素的字体大小,所以如果在父元素上设置了不同的字体大小,em的像素值就会变得复杂。现在不要过于担心这个问题,我们将在后面的文章和模块中更详细地介绍继承和字体大小设置。
    • rem: REM(root em)和em以同样的方式工作,但它总是等于默认基础字体大小的尺寸;继承的字体大小将不起作用,所以这听起来像一个比em更好的选择,虽然在旧版本的IE上不被支持(查看关于跨浏览器支持 Debugging CSS.)
    • vw, vh: 分别是视口宽度的1/100和视口高度的1/100,其次,它不像rem那样被广泛支持。

    https://developer.mozilla.org/zh-CN/docs/Learn/CSS/Introduction_to_CSS/Values_and_units

  6. 元素居中显示

    1
    2
    3
    4
    5
    6
    7
    #main {
    width: 100px;
    height: 100px;
    display: flex;
    justify-content: center;
    align-item: center;
    }
  7. position

    • relative:相对定位。相对于其正常位置进行定位
    • absolute:绝对定位。元素相对于最近的 ‘positioned’ 祖先元素 定位
    • fixed:固定定位。元素相对于窗口定位
    • static:默认值。没有定位
  8. CSS 权重优先级

    内联 > ID选择器 > 类选择器 > 标签选择器
    https://juejin.im/post/5be3d07be51d457d4932b043#heading-0

    https://developer.mozilla.org/zh-CN/docs/Web/CSS/Specificity

  9. CSS 预处理器

    Less:http://lesscss.cn/

    Sass: https://sass-guidelin.es/zh/

JavaScript 基础

  1. JavaScript 中的数据类型有哪些,typeof(null) 的值是什么?

    javascript 中的数据类型:nullundefinedbooleannumberstringsymbol

    typeof(null) = 'object'

  2. JavaScript 中解决异步的方法有哪些?

    回调函数(callback)、事件监听、发布/订阅、promise对象、async/await

  3. history.go() 与 history.back() 的区别

    history.back(-1) //直接返回当前页的上一页,数据全部消失,是个新页面
    history.go(-1) //也是返回当前页的上一页,不过表单里的数据全部还在

  4. 闭包

    闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式,就是在一个函数内部创建另一个函数。

  5. this 的指向
    第一准则是:this永远指向函数运行时所在的对象,而不是函数被创建时所在的对象。

    • 普通的函数调用,函数被谁调用,this就是谁。
    • 构造函数的话,如果不用new操作符而直接调用,那即this指向window。用new操作符生成对象实例后,this就指向了新生成的对象。
    • 匿名函数或不处于任何对象中的函数指向window 。
    • 如果是call,apply等,指定的this是谁,就是谁。
    • 参考:www.cnblogs.com/beidan/p/53…
  6. 原型/原型链

    原型: 无论什么时候,只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个 prototype 属性,这个属性指向函数的原型对象。在默认情况下,所有原型对象都会自动获得一个 constructor(构造函数)属性,这个属性是一个指向 prototype 属性所在函数的指针
    原型链:原型链的基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法
    构造函数、原型和实例的关系:每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。如果让原型对象等于另一个类型的实例,那么此时的原型对象将包含一个指向另一个原型的指针,相应地,另一个原型中也包含着一个指向另一个构造函数的指针。假如另一个原型又是另一个类型的实例,那么上述关系依然成立,如此层层递进,就构成了实例与原型的链条。这就是所谓原型链的基本概念。

  7. 深拷贝
    浅拷贝:所谓的浅拷贝就是复制一份引用数据类型的地址,当改变了内存中数据的某一个值得话,也会影响到另一个对象
    深拷贝:所谓的深拷贝就是复制一份引用数据类型的数据,当改变了数据的某一个值得话,不会影响到另一个对象(注意深拷贝是拷贝的数据,而不是索引,浅拷贝拷贝的是索引而不是数据)

    这个问题通常可以通过 JSON.parse(JSON.stringify(object)) 来解决。但是这个方法也是有局限性的:会忽略 undefined、会忽略 symbol、不能序列化函数、不能解决循环引用的对象。在通常情况下,复杂数据都是可以序列化的,所以这个函数可以解决大部分问题,并且该函数是内置函数中处理深拷贝性能最快的。

    当然如果你的数据中含有以上三种情况下,可以使用 lodash 的深拷贝函数

    如果你所需拷贝的对象含有内置类型并且不包含函数,可以使用 MessageChannel

    https://yuchengkai.cn/docs/frontend/#%E6%B7%B1%E6%8B%B7%E8%B4%9D

  8. promise

    http://es6.ruanyifeng.com/#docs/promise

  9. async/await

    http://es6.ruanyifeng.com/#docs/async

框架基础

  1. Vue 生命周期

  2. Vue 组件传值
    https://juejin.im/post/5a427cf6f265da4327187dcc

  3. Vuex
    https://segmentfault.com/a/1190000009404727

  4. Vue 双向数据绑定原理
    vue实现双向数据绑定的原理就是利用了 Object.defineProperty() 这个方法重新定义了对象获取属性值(get)和设置属性值(set)的操作来实现的。

    在MDN上对该方法的说明是:Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。

    它接收三个参数,要操作的对象,要定义或修改的对象属性名,属性描述符。重点就是最后的属性描述符。

    属性描述符是一个对象,主要有两种形式:数据描述符和存取描述符。这两种对象只能选择一种使用,不能混合两种描述符的属性同时使用。上面说的 get 和 set 就是属于存取描述符对象的属性。

    然后我们可以通过在存取描述符中的 get 和 set 方法内写入自定义的逻辑来实现对象获取属性和设置属性时的行为。

  5. React
    https://segmentfault.com/a/1190000016885832

网络基础

  1. 一个完整的 HTTP 事务:

    域名解析 ==> 发起 TCP 的三次握手 ==> 建立 TCP 连接后发起 http 请求 ==> 服务器响应 http 请求,浏览器得到 html 代码 ==> 浏览器解析 html 代码,并请求 html 代码中的资源(如 js,css,图片等) ==> 浏览器对页面进行渲染呈现给用户

  2. 当你设计一个组件的时候你会考虑哪些方面

    兼容性、可复用性、稳定性、安全等方面……
    前端组件设计原则:

  3. 框架选型的时候你会考虑哪些方面

    大前端架构思考与选择:

算法基础

代码题

  1. 实现下面的方法:

    1
    2
    3
    4
    5
    //	let a = [1,2,3]
    // console.log( a.fnName() ) 输出 1,2,3,1,4,9
    Array.prototype.fnName = function() {
    return [this, this.map(item => item * item)].flat()
    }
  2. 下列代码的输出分别是什么?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    //	some()
    [1,2,3].some( item => item < 3 ) // true

    // every()
    [1,2,3].every( item => item > 2 ) // false

    // filter()
    [1,2,3,4,5].filter( item => item > 2 ) // 3,4,5

    // map()
    [1,2,3].map( item => item * item ) // 1,4,9
  1. 数组元素去重(限制最多一次循环)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    /* indexOf 处理(不兼容 IE678) */
    var ary = [1,2,2,3,2,2,4,6,7,5,5,6,7,7,5,2,5];
    for (var i = 0; i < ary.length; i++) {
    var cur = ary[i];
    var curNextAry = ary.slice(i+1);

    if (curNextAry.indexOf(cur) > -1) {
    ary.splice(i,1);
    i--;
    }
    }


    /* 前后对比法 */
    ary = ary.sort((a,b) => a - b);
    for (var i = 0; i < ary.length - 1; i++) {
    if (ary[i] === ary[i+1]) {
    ary.splice(i+1, 1);
    i--;
    }
    }
  1. 数组扁平化

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    /* 编写一个程序,将数组扁平化,并去除其中重复部分数据,最终得到一个升序且不重复的数组 */
    let arr = [[1,2,2],[3,4,5,5],[6,7,8,9,[11,12,[12,13,[14]]]],10];

    /* 方法一 */
    // 使用 ES6 中提供的 Array.prototype.flat() 处理
    // arr = arr.flat(Infinity) // Infinity 表示展开任意级数组
    // 去重排序 [...new Set(arr)] 或者 Array.from(new Set(arr))
    // arr = Array.from(new Set(arr)).sort((a,b) => a - b);
    arr = Array.from(new Set(arr.flat(Infinity))).sort((a,b)=>a-b)
    console.log(arr)

    /* 方法二 */
    // 把数组直接变为字符串即可(数组 toString 之后,不管有多少级,最后都会变为以都好分隔的字符串,没有中括号和层级了),相当于直接扁平化了
    arr = arr.toString().split(',').map(item => Number(item));

    /* 方法三 */
    // JSON.stringify 也可以扁平化数组
    arr = JSON.stringify(arr).replace(/(\[|\])/g, '').split(',').map(item => Number(item))

    /* 方法四 */
    // 基于数组的 some 方法进行判断检测:验证数组中的某一项有没有符合函数中提供的规则的
    // find 和 some 的区别:some 返回的是 boolean,find 找到符合规则的,返回当前这一项,没有找到符合规则的,返回 undefined
    while (arr.some(item => Array.isArray(item))) {
    arr = [].concat(...arr);
    }

    /* 方法五 */
    // 自己递归处理
    ~function () {
    function myFlat() {
    let result = [],
    _this = this;
    let fn = (arr) => {
    for (let i = 0; i < arr.length; i++) {
    let item = arr[i];
    if (Array.isArray(item)) {
    fn(item);
    continue;
    }
    result.push(item);
    }
    };
    fn(_this);
    return result;
    }
    Array.prototype.myFlat = myFlat;
    }();
    arr = arr.myFlat();
  2. 实现一个 $attr(name, value) 遍历

    实现一个 $attr(name, value) 遍历
    属性为 name
    值为 value 的元素集合

    例如下面示例:
    let ary = $attr(‘class’, ‘box’); // 获取页面中所有的 class 为 box 的元素

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    function $attr(property, value) {
    // 获取当前页面中所有的标签
    let elements = document.getElementsByTagName('*'),
    arr = [];
    // [].forEach.call(elements, item=>{})
    elements = Array.from(elements);
    elements.forEach(item => {
    // 存储的是当前元素 property 对应的属性值
    let itemValue = item.getAttribute(property);
    if (property === 'class') {
    // 样式类属性名要特殊的处理
    // \b 是正则的边界匹配,匹配一个完整单词
    new RegExp("\\b" + value + "\\b").test(itemValue) ? arr.push(item) : null;
    return;
    }
    if (itemValue === value) {
    // 获取的值和传递的值校验成功:当前就是我们想要的
    arr.push(item);
    }
    })

    return arr;
    }

    let ary = $attr('class', 'box')

    index.html

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <body>
    <div id="AA" class="box clearfix"></div>
    <div myIn="1"></div>
    <div class="content box"></div>
    <div name="BB"></div>
    <div></div>
    <div id="AA"></div>
    <div myIn="1" class="clearfix"></div>
    <div class="box"></div>
    <div myIn="2"></div>
    <div name="BB"></div>
    </body>
  1. 用正则给英文单词前后加空格

    1
    2
    3
    4
    5
    6
    7
    8
    /* 英文字母汉字组成的字符串,用正则给英文单词前后加空格 */
    let str = "xxxxxx",
    reg = /\b[a-z]+\b/ig;
    str = str.replace(reg, value => {
    return " " + value + " ";
    }).trim(); // String.prototype.trim/.trimLeft/.trimRight 去除字符串首尾空格

    console.log(str)
  2. 自己实现一个 _new 方法,也能模拟出内置 new 后的结果

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    function Dog(name) {
    this.name = name;
    }
    Dog.prototype.bark = function(){
    console.log('wangwang');
    }
    Dog.prototype.sayName = function(){
    console.log('my name is ' + this.name);
    }
    /* 基于内置的 new 关键词,我们可以创建 Dog 的一个实例 sanmao,实例可以调取原型上的属性和方法,现在的需求是:自己实现一个 _new 方法,也能模拟出内置 new 的结果 */
    function _new() {
    // 完成你的代码
    }
    let sanmao = _new(Dog, '三毛');
    sanmao.bark(); // "wangwang"
    sanmao.sayName(); // "my name is 三毛"
    console.log(sanmao instanceof Dog); // true



    /*
    let sanmao = new Dog('三毛')
    1、像普通函数执行一样,形成一个私有作用域
    形参赋值
    变量提升
    2、默认创建一个对象,让函数中的 this 指向这个对象,这个对象就是当前类的一个实例
    3、代码执行
    4、默认把创建的对象返回
    */
    /* 代码实现 */

    // Fn 当前要 new 的类 ==> Dog
    // arg 后期需要给构造函数传递的参数信息 ==> ['三毛']
    function _new(Fn, ...arg) {
    // let obj = {};
    // obj.__proto__ = Fn.prototype;
    let obj = Object.create(Fn.prototype); // Object.create([A对象]):创建一个空对象obj,并且让空对象obj作为 A对象所属构造函数的实例(obj.__proto__=AA)
    Fn.call(obj, ...arg);
    return obj;
    }
    let sanmao = _new(Dog, '三毛');
  3. 匿名函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    var b = 10;
    (function b(){
    b = 20;
    console.log(b); // 函数本身
    })();
    console.log(b); // 10

    /*
    let fn = function AAA() {
    // "use strict";
    // AAA = 1000; // 报错:Assignment to constant variable.
    console.log(AAA); // 当前函数
    };
    AAA(); // 报错: AAA is not defined
    */

    // 1. 本应匿名的函数如果设置了函数名,在外面还是无法调用,但是在函数里面是可以使用的
    // 2. 而且类似于创建常量一样,这个名字存储的值不能再被修改(非严格模式下不报错,但是不会有任何效果,严格模式下直接报错,可以把 AAA 理解为是用 const 创建出来的)
  4. === 判断数据类型转换规则

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    var a = ?;
    if (a == 1 && a == 2 && a== 3) {
    console.log(1)
    }

    /*
    == 进行比较的时候,如果左右两边数据类型不一样,则先转换为相同的数据类型,然后再进行比较
    1. {}=={} 两个对象进行比较,比较的是堆内存的地址
    2. null==undefined 相等的 | null===undefined 不相等
    3. NaN==NaN 不相等 NaN 和谁都不相等
    4. [12]=="12" 对象和字符串比较,是把对象 toString() 转换为字符串后在进行比较
    5. 剩余所有情况在进行比较的时候,都是转换为数字(前提是数据类型不一样)
    对象转数字:先转换为字符串,然后再转换为数字
    字符串转数字:只要出现一个非数字字符,结果就是 NaN
    布尔转数字:true=>1 false=>0
    null 转数字 0
    undefined 转数字 NaN
    */

    // 对象和数字比较:先把对象 .toString() 变为字符串,然后在转换为数字
    var a = {
    n: 0,
    toString: function(){ // 私有的属性方法
    return ++this.n;
    }
    };
    // a.toString(); // 此时调取的就不再是 Object.prototype.toString 了,调取的是自己私有的
    if (a == 1 && a == 2 && a == 3) {
    console.log('OK');
    }


    /* 方法二 */
    // shift:删除数组第一项,把删除的内容返回,原数组改变
    let a = [1,2,3];
    a.toString = a.shift;
    if(a == 1 && a == 2 && a==3) {
    console.log('OK')
    }

    /* 方法三 */
    let n = 0;
    Object.defineProperty(window, 'a', {
    get: function(){
    this.value ? this.value++ : this.value = 1;
    return this.value;
    }
    });
    if(a == 1 && a == 2 && a == 3) {
    console.log('OK')
    }
打赏功能
-------------本文结束感谢您的阅读-------------
0%