美文网首页前端开发
前端JavaScript高级面试技巧[1]

前端JavaScript高级面试技巧[1]

作者: Mark同学 | 来源:发表于2019-12-07 10:05 被阅读0次

    第1章 课程介绍

    1-1 导学

    课程概述

    • 做什么?— 讲解前端 JS 高级面试题
    • 哪些部分?— 高级基础, 框架原理, app混合开发
    • 技术?— JS、ES6、虚拟DOM、vue、React、hybrid

    知识点介绍

    • 基础知识:ES6 常用语法、原型高级应用、异步全面讲解
    • 框架原理:虚拟 DOM、MVVM vue、组件化 React
    • 混合开发:hybrid、hybrid vs H5、前端客户端通讯

    课程安排

    • 高级基础
      1. ES6 常用语法:Class Module Promise 等
      2. 原型高级应用:结合 jQuery 和 zepto 源码
      3. 异步全面讲解:从原理到 jQuery 再到 Promise
    • 框架原理
      1. 虚拟 DOM:存在价值,如何使用,diff 算法
      2. vue:MVVM,vue 响应式、模板解析、渲染
      3. React:组件化,JSX,vdom,setState
      4. 对比:有主见,自圆其说
    • App混合开发
      1. hybrid:基础、和 h5 对比,上线流程
      2. 通讯:通讯原理,JS-Bridge 封装

    讲授方式

    • 先出面试题,带领大家思考
    • 通过题目引出知识点,扩充讲解知识体系
    • 最后通过学到的知识点,解答题目

    课程收获

    • 从深度和广度都扩充了自己的知识体系
    • 学会如何高效学习
    • 深入理解常用框架的实现原理和 hybrid 应用

    1-2 课程重要提示

    1-3 架构-题目

    ES6

    模块化的使用和编译环境?

    Class 与 JS 构造函数的区别?

    Promise 的用法?

    ES6 其他常用功能?

    异步

    什么是单线程,和异步有什么关系?

    什么是 event-loop?

    目前 JS 解决异步的方案有哪些?

    如果只用 jquery 如何解决异步?

    Promise 的标准?

    async/await 的使用?

    原型

    原型如何实际应用?

    原型如何满足扩展?

    vdom

    什么是 vdom,为何要用 vdom?

    vdom如何使用,核心函数有哪些?

    了解 diff 算法吗?

    MVVM

    之前使用 jquery 和现在使用 Vue 或 React 框架的区别?

    你如何理解MVVM?

    Vue 如何实现响应式?

    Vue 如何解析模版?

    介绍 Vue 的实现流程?

    组件化

    对组件化的理解?

    JSX 是什么?

    JSX 和 vdom 什么关系?

    简述 React 的 setState?

    简述自己如何比较 React 和 Vue?

    hybrid

    hybrid 是什么,为何要用hybrid?

    hybrid 如何更新上线?

    hybrid 和 h5 有何区别?

    JS 如何与客户端通信?

    第2章 ES6 语法

    2-1 开始

    ES6

    • 开发环境已经普及使用
    • 浏览器环境却支持不好(需要开发环境编译)
    • 内容很多,重点了解常用语法
    • 面试:开发环境的使用 + 重点语法的掌握

    题目

    • ES6 模块化如何使用,开发环境如何打包?
    • Class 和普通构造函数有何区别?
    • Promise 的基本使用和原理?
    • 总结一下 ES6 其他常用功能?

    2-2 模块化

    【题目】

    ES6 模块化如何使用,开发环境如何打包?

    【知识点】

    1. 模块化的基本语法

    • export 语法
    // util1.js
    export default {
      a: 100
    };
    
    // util2.js
    export function fn1() {
      alert('fn1');
    };
    export function fn2() {
      alert('fn2');
    };
    
    • import 语法
    // index.js
    import util1 from './util1.js';
    import { fn1, fn2 } from './util2.js';
    
    console.log(util1);
    fn1();
    fn2();
    

    2. 开发环境配置

    babel

    • 安装 babel
    1. 电脑有 node 环境,运行 npm init
    2. npm install --save-dev babel-core babel-preset-es2015 babel-preset-latest
    3. 创建 .babelrc 文件,输入以下内容:
        { 
            "presets": ["es2015", "latest"],
            "plugins": []
        }
    4. sudo npm install -g babel-cli
    5. babel --version
    
    • 使用 babel
    1. 创建:./src/index.js
    2. 内容:[1, 2, 3].map(item => item + 1);
    3. 运行:babel ./src/index.js
    

    webpack

    1. npm install webpack webpack-cli babel-loader@^7.1.2 --save-dev
    2. 配置 webpack.config.js,输入以下内容:
        module.exports = { 
            entry: './src/index.js',
            output: { 
              path: __dirname,
              filename: './build/bundle.js'
            },
            module: { 
              rules: [{
                test: /\.js?$/,
                exclude: /{node_modules}/,
                loader: 'babel-loader'
              }]
            }
        }
    3. 配置 package.json 中的 scripts
        "scripts": {
            "start": "webpack"
        }
    4. 运行 npm start
    5. 创建:index.html
        <script type="text/javascript" src="./build/bundle.js"></script>
    6. http-server -p 8881
    7. 访问 http://localhost:8881/index.html
    

    rollup

    • 安装 rollup

    https://www.rollupjs.com/guide/tutorial rollup 中文网

    1. npm init
    2. npm i rollup rollup-plugin-node-resolve rollup-plugin-babel@^3.0.3 babel-plugin-external-helpers babel-preset-latest babel-core --save-dev
    3. 配置 .babelrc
    {
      "presets": [
        ["latest", {
          "es2015": {
            "modules": false
          }
        }]
      ],
      "plugins": ["external-helpers"]
    }
    4. 配置  rollup.config.js
    import babel from 'rollup-plugin-babel';
    import resolve from 'rollup-plugin-node-resolve';
    
    export default {
      input: 'src/index.js',
      output: {
        file: 'build/bundle.js',
        format: 'cjs'
      },
      plugins: [
        resolve(),
        babel({
            exclude: 'node_modules/**'
        })
      ]
    };
    
    • 使用 rollup
    1. 将 webpack 环境的 JS 代码拷贝过来
    2. 修改 package.json 的 scripts
      "scripts": {
        "start": "rollup -c rollup.config.js"
      }
    3. 运行 npm start
    
    • 关于工具
    rollup 功能单一,webpack 功能强大
    参考设计原则和《Linux/Unix设计思想》
    工具要尽量功能单一,可集成,可扩展
    wangEditor 用的 gulp + rollup
    

    3. 关于 JS 众多模块化标准

    • 没有模块化
    • AMD 成为标准,require.js (也有 CMD)
    • 前端打包工具,是的 nodejs 模块化可以被使用
    • ES6 出现,想统一现在所有模块化标准
    • nodejs 积极支持,浏览器尚未统一
    • 你可以自造 lib ,但是不要自造标准!!!

    【解答】

    • 语法: import export (注意有无 default)
    • 环境: babel 编译 ES6 语法,模块化可用 webpack 和 rollup
    • 扩展: 说一下自己对模块化标准统一的期待

    2-9 class

    【题目】

    Class 与 JS 构造函数的区别?

    • Class 在语法上更加贴合面向对象的写法
    • Class 实现继承更加易读、易理解
    • 更易于写 java 等后端语言的使用
    • 本质还是语法糖,使用 prototype

    【知识点】

    JS 构造函数

    function MathHandle(x, y) {
      this.x = x;
      this.y = y;
    }
    
    MathHandle.prototype.add = function () {
      return this.x + this.y;
    };
    
    var m = new MathHandle(1, 2);
    console.log(m.add());
    
    // typeof MathHandle // "function"
    // MathHandle === MathHandle.prototype.constructor // true
    // m.__proto__ === MathHandle.prototype // true
    

    Class 基本语法

    class MathHandle {
      constructor (x, y) {
        this.x = x;
        this.y = y;
      }
    
      add() {
        return this.x + this.y;
      }
    }
    const m = new MathHandle(1, 2);
    console.log(m.add());
    // typeof MathHandle // "function"
    // MathHandle === MathHandle.prototype.constructor // true
    // m.__proto__ === MathHandle.prototype // true
    

    语法糖

    class MathHandle {
      // ...
    }
    
    typeof MathHandle // "function"
    MathHandle === MathHandle.prototype.constructor // true
    

    继承

    • 继承 - JS
    ES6 之前的继承,是把低级构造函数的原型,赋值成高级构造函数的实例这种方式来实现的
    // 动物
    function Animal() {
      this.eat = function () {
        console.log('animal eat');
      }
    }
    // 狗
    function Dog() {
      this.bark = function () {
        console.log('dog bark');
      }
    }
    // 绑定原型,实现继承
    Dog.prototype = new Animal();
    // 哈士奇
    var hashiqi = new Dog();
    hashiqi.eat(); // animal eat
    hashiqi.bark(); // dog bark
    
    • 继承 - Class
    // 动物
    class Animal {
      constructor(name) {
        this.name = name;
      }
      eat() {
        console.log(`${this.name} eat`);
      }
    }
    // 狗
    class Dog extends Animal {
      constructor(name) {
        super(name);
        this.name = name;
      }
      say() {
         console.log(`${this.name} say`);
      }
    }
    // 哈士奇
    const dog = new Dog('哈士奇');
    dog.say(); // 哈士奇 say
    dog.eat(); // 哈士奇 eat
    

    【解答】

    class Ad extends Component {
        constructor(props) {
            super(props);
            this.state = {
                data: []
            }
        }
        render() {
            return (
                <div>Hello, World!</div>
            )
        }
        componentDidMount() {
    
        }
    }
    

    2-13 Promise

    【题目】

    Promise 的用法?

    【知识点】

    Promise 的基本使用

    • Callback Hell
    function loadImg(src, callback, fail) {
      var img = document.createElement('img');
      img.onload = function () {
        callback(img);
      }
      img.onerror = function () {
        fail();
      }
      img.src = src;
    }
    
    var src = 'https://www.imooc.com/courseimg/s/cover042_s.jpg';
    loadImg(src, function (img) {
      console.log(img.width);
    }, function () {
      console.log('failed');
    })
    
    • Promise 语法
    function loadImg(src) {
      const promise = new Promise(function (resolve, reject) {
        var img = document.createElement('img');
        img.onload = function () {
          resolve(img);
        }
        img.onerror = function () {
          reject();
        }
        img.src = src;
      })
      return promise;
    };
    
    var src = 'https://www.imooc.com/courseimg/s/cover042_s.jpg';
    var result = loadImg(src);
    
    result.then(function (img) {
      console.log(img.width);
    }, function () {
      console.log('failed');
    });
    
    result.then(function (img) {
      console.log(img.height);
    });
    

    【解答】

    • new Promise 实例,而且要 return
    • new Promise 时要传入函数,函数有 resolve reject 两个参数
    • 成功时执行 resolve() 失败时执行 reject()
    • then 监听结果

    2-16 总结一下 ES6 其他常用功能

    题目

    总结一下 ES6 其他常用功能?

    • let/const
    • 多行字符串/模板变量
    • 解构赋值
    • 块级作用域
    • 函数默认参数
    • 箭头函数 (this)

    知识点

    • let/const
    // JS
    var i = 10;
    i = 100;
    
    // ES6
    let i = 10;
    i = 100;  // 正确
    const j = 20;
    j = 200;  // 报错
    
    • 多行字符串/模板变量
    // JS
    var name = 'zhangsan', age = 20, html = '';
    html += '<div>';
    html += ' <p>' + name + '</p>';
    html += ' <p>' + age + '</p>';
    html += '</div>';
    
    // ES6
    const name = 'zhangsan', age = 20;
    const html = `<div>
                         <p>${name}</p>
                         <p>${age}</p>
                 </div>`;
    console.log(html);
    
    • 解构赋值
    // JS
    var obj = { a: 100, b: 200 };
    var a = obj.a;
    var b = obj.b;
    
    var arr = ['xxx', 'yyy', 'zzz'];
    var x = arr[0];
    
    // ES6
    const obj = { a: 10, b: 20, c: 30 };
    const { a, c } = obj;
    console.log(a);
    console.log(c);
    
    const arr = ['xxx', 'yyy', 'zzz'];
    const [x, y, z] = arr;
    console.log(x);
    console.log(y);
    console.log(z);
    
    • 块级作用域
    // JS
    var obj = { a: 100, b: 200 };
    for (var item in obj) {
      console.log(item);
    }
    console.log(item); // 'b'
    
    // ES6
    const obj = { a: 100, b: 200 };
    for (let item in obj) {
      console.log(item);
    }
    console.log(item); // undefined
    
    • 函数默认参数
    // JS
    function fn(a, b) {
      if (b == null) {
        b = 0;
      }
    }
    
    // ES6
    function fn(a, b=0) {
    
    }
    
    • 箭头函数
    // JS
    var arr = [1, 2, 3];
    arr.map(function (item) {
      return item + 1;
    });
    
    // ES6
    const arr = [1, 2, 3];
    arr.map(item => item + 1);
    arr.map((item, index) => {
      console.log(index);
      return item + 1;
    });
    
    function fn() {
      console.log('real', this); // {a: 100}
      var arr = [1, 2, 3];
      // 普通 JS
      arr.map(function (item) {
        console.log('js', this); // window
        return item + 1;
      });
      // 箭头函数
      arr.map(item => {
        console.log('es6', this); // {a: 100}
        return item + 1;
      });
    }
    fn.call({a: 100});
    

    第3章 原型

    3-1 开始

    • 《前端 JS 面试技巧》已经讲解过原型的基础知识
    • 高级面试题,光会原型基础还不够,还要实际应用
    • zepto jquery 中如何用原型?
    • 顺便也算是解读了 zepto 和 jquery 的部分源码

    题目

    原型如何实际应用?

    • jquery 和 zepto 的简单使用
    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
    </head>
    <body>
        <p>jquery test 1</p>
        <p>jquery test 2</p>
        <p>jquery test 3</p>
    
        <div id="div1">
            <p>jquery test in div</p>
        </div>
    
        <script type="text/javascript" src="./jquery-3.2.1.js"></script>
        <script type="text/javascript">
            var $p = $('p');
            $p.css('color', 'red'); // css 是原型方法
            console.log($p.html()); // html 是原型方法
            
            var $div1 = $('#div1')
            $div1.css('color', 'blue'); // css 是原型方法
            console.log($div1.html()); // html 是原型方法
        </script>
    </body>
    </html>
    
    • zepto 如何使用原型
    (function (window) {
    
      var zepto = {};
    
      function Z(dom, selector) {
          var i, len = dom ? dom.length : 0;
          for (i = 0; i < len; i++) {
              this[i] = dom[i];
          }
          this.length = len;
          this.selector = selector || '';
      }
    
      zepto.Z = function (dom, selector) {
          return new Z(dom, selector);
      };
    
      zepto.init = function (selector) {
          var slice = Array.prototype.slice;
          var dom = slice.call(document.querySelectorAll(selector));
          return zepto.Z(dom, selector);
      };
    
      var $ = function (selector) {
          return zepto.init(selector);
      };
      window.$ = $
    
      $.fn = {
          css: function (key, value) {
              alert('css');
          },
          html: function (value) {
              return '这是一个模拟的 html 函数';
          }
      }
      Z.prototype = $.fn;
    })(window);
    
    • jquery 如何使用原型
    (function (window) {
    
        var jQuery = function (selector) {
            return new jQuery.fn.init(selector)
        };
    
        jQuery.fn = {
            css: function (key, value) {
                alert('css');
            },
            html: function (value) {
                return 'html';
            }
        };
    
        var init = jQuery.fn.init = function (selector) {
            var slice = Array.prototype.slice;
            var dom = slice.call(document.querySelectorAll(selector));
    
            var i, len = dom ? dom.length : 0;
            for (i = 0; i < len; i++) {
                this[i] = dom[i];
            }
            this.length = len;
            this.selector = selector || '';
        }
    
        init.prototype = jQuery.fn;
    
        window.$ = jQuery;
    
    })(window);
    

    原型如何满足扩展?

    第4章 异步

    • 《前端 JS 面试技巧》讲到异步的基础
    • 高级面试会问到更多的内容
    • 如 event-loop Promise Async/Await 等

    题目

    什么是单线程,和异步有什么关系?

    • 单线程 - 只有一个线程,同一时间只能做一件事
    // 循环运行期间,JS 执行和 DOM 渲染暂时卡顿
    var i, sum = 0;
    for (i = 0; i < 1000000000; i++) {
      sum += i;
    }
    console.log(sum);
    
    // alert 不处理, JS 执行和 DOM 渲染暂时卡顿
    console.log(1);
    alert('hello');
    console.log(2);
    
    • 原因 - 避免 DOM 渲染的冲突
    浏览器需要渲染 DOM
    JS 可以修改 DOM 结构
    JS 执行的时候,浏览器 DOM 渲染会暂停
    两段 JS 也不能同时执行(都修改 DOM 就冲突了)
    webworker 支持多线程,但是不能访问 DOM
    
    • 解决方案 - 异步
      什么是 callback?异步完成之后要执行的函数
    问题一:没按照书写方式执行,可读性查
    问题二:callback 中不容易模块化
    

    什么是 event-loop?

    • 文字解释
    事件轮询,JS 实现异步的具体解决方案
    同步代码,直接执行
    异步函数先放在 "异步队列" 中
    待同步函数执行完毕,轮询执行 "异步队列" 的函数
    

    如果只用 jquery 如何解决异步?- Deferred

    • 是否用过 jQuery 的 Deferred
    jQuery 1.5 的变化
    使用 jQuery Deferred
    初步引入 Promise 概念
    
    • jQuery 1.5 的变化 - 1.5 之前
    var ajax = $.ajax({
      url: 'data.json',
      success: function () {
        console.log('success1');
        console.log('success1');
        console.log('success1');
      },
      error: function () {
        console.log('error');
      }
    })
    console.log(ajax); // 返回一个 XHR 对象
    
    • jQuery 1.5 的变化 - 1.5 之后
    var ajax = $.ajax('data.json');
    ajax.done(function () {
          console.log('success 1');
        })
        .fail(function () {
          console.log('error');
        })
        .done(function () {
          console.log('success 2');
        })
    console.log(ajax); // 返回一个 deferred 对象
    
    // 很像 Promise 的写法
    var ajax = $.ajax('data.json');
    ajax.then(function () {
            console.log('success 1');
          }, function () {
            console.log('error 1');
          })
          .then(function () {
            console.log('success 2');
          }, function () {
            console.log('error 2');
          })
    
    • jQuery 1.5 的变化
    无法改变 JS 异步和单线程的本质
    只能从写法上杜绝 callback 这种形式
    它是一种语法糖形式,但是解耦了代码
    很好的体现:开放封闭原则 ( 对扩展开发,对修改封闭 )
    
    • 使用 jQuery Deferred
    var wait = function () {
      var task = function () {
         console.log('执行完成');
         console.log('此处新需求......');
         console.log('第一步');
         console.log('第二步');
         console.log('第三步');
      };
      setTimeout(task, 2000);
    };
    wait();
    
    function waitHandle() {
       var dtd = $.Deferred();
       var wait = function (dtd) {
           var task = function () {
               console.log('执行完成');
               dtd.resolve();
               // dtd.reject()
           }
           setTimeout(task, 2000);
           return dtd.promise(); // return dtd; => return dtd.promise();
       }
       return wait(dtd);
    }
    
    var w = waitHandle();
    // w.then(function () {  
    // => 1.w 接收到的不再是一个 dtd 对象,而是一个 promise 对象
    // => 2.只有 .then .done .fail 这种被动监听方法
    // => 3.不再支持 .resolve .reject 这种主动方法的调用
    $.when(w).then(function () {
        console.log('ok 1');
    }, function () {
        console.log('err 1');
    });
    
    • jQuery 里引入 promise
    总结,dtd 的 API 可分成两类,用意不同
    第一类: dtd.resolve  dtd.reject
    第二类: dtd.then  dtd.done  dtd.fail
    这两类应该分开,否则后果很严重!
    可以在上面代码最后执行 dtd.reject() 试一下后果
    

    Promise 的基本使用和原理?

    • 基本语法回顾
    function loadImg(src) {
      const promise = new Promise(function (resolve, reject) {
        var img = document.createElement('img');
        img.onload = function () {
          resolve(img);
        }
        img.onerror = function () {
          reject();
        }
        img.src = src;
      })
      return promise;
    };
    
    var src = 'https://www.imooc.com/courseimg/s/cover042_s.jpg';
    var result = loadImg(src);
    
    result.then(function (img) {
      console.log(img.width);
    }, function () {
      console.log('failed');
    });
    
    result.then(function (img) {
      console.log(img.height);
    });
    
    • 异常捕获
    // 捕获程序错误异常
    // throw new Error('自定义错误');
    
    result.then(function (img) {
      console.log(img.width);
      return img;
    }).then(function (img) {
      console.log(img.height);
    }).catch(function (ex) {
      // 统一捕获异常
      console.log(ex);
    })
    
    // 捕获业务逻辑异常
    // reject('图片加载失败');
    // var src = 'https://cover042_sXXX.jpg';
    
    result.then(function (img) {
      console.log(img.width);
      return img;
    }).then(function (img) {
      console.log(img.height);
    }).catch(function (ex) {
      // 统一捕获异常
      console.log(ex);
    })
    
    • 多个串联
    //业务需求;先加载一个,再加载另外一个
    var src1 = 'https://www.imooc.com/courseimg/s/cover042_s.jpg';
    var result1 = loadImg(src1);
    var src2 = 'https://coding.imooc.com/static/module/class/content/img/190/section5-img.png';
    var result2 = loadImg(src2);
    
    // 链式操作
    result1.then(function (img) {
      console.log('第一个图片加载完成', img.width); // 240
      return result2;
    }).then(function (img) {
      console.log('第二个图片加载完成', img.width); // 998
    }).catch(function (ex) {
      // 统一捕获异常
      console.log(ex);
    })
    
    • Promise.all 和 Promise.race
    // 全部完成才执行
    Promise.all([result1, result2]).then(function (datas)  {
     console.log(datas[0]);
     console.log(datas[1]);
    });
    
    // 只要有一个完成就执行
    Promise.race([result1, result2]).then(function (data)  {
     console.log(data);
    });
    
    • Promise 标准
    // 关于“标准”的闲谈
    任何技术推广使用都需要一套标准来支撑
    如 html js css http 等,无规矩不成方圆
    任何不符合标准的东西,终将会被用户抛弃
    不要挑战标准,不要自造标准
    
    // Promise 标准 - 状态变化
    三种状态:pending fulfilled rejected
    初始状态是 pending
    pending 变为 fulfilled ,或者 pending 变为 rejected
    状态变化不可逆
    
    // Promise 标准 - then
    Promise 实例必须实现 then 这个方法
    then() 必须可以接收两个函数作为参数
    then() 返回的必须是一个 Promise 实例
    

    介绍一下 async/await(和 Promise 的区别、联系)?

    • then 只是将 callback 拆分了
    var w = waitHandle();
    w.then(function () { 
        console.log('ok 1');
    }, function () {
        console.log('err 1');
    }).then(function () { 
        console.log('ok 2');
    }, function () {
        console.log('err 2');
    });
    
    • async/await 是最直接的同步写法
    const load = async function () {
      const result1 = await loadImg(src1);
      console.log(result1);
      const result2 = await loadImg(src2);
      console.log(result2);  
    }
    load();
    
    • 语法
    使用 await ,函数必须用 async 标识
    await 后面跟的是一个 Promise 实例
    需要 babel-polyfill
    
    • 坑来了
    // webpack 打包
    1.首先,npm i babel-polyfill -D
    2.运行报错,Uncaught ReferenceError: regeneratorRuntime is not defined
    3.在 .babelrc 文件中添加:
        "plugins": [
            [
                "transform-runtime",
                {
                    "helpers": false,
                    "polyfill": false,
                    "regenerator": true,
                    "moduleName": "babel-runtime"
                }
            ]
        ]
    4.运行报错,ReferenceError: Unknown plugin "transform-runtime" specified
    5.npm install babel-plugin-transform-runtime -D
    6.运行再次报错,Cannot use import statement outside a module
    7.<script type="module"></script>
    8.成功
    // rollup 打包
    1.npm i babel-polyfill -D
    之前也报和 webpack 相同的错误,自己就好了,原因未知
    
    • 小结
    promise 是对异步回调的封装
    async await 是使用 promise 时的一种扩展
    完全是同步的写法,再也没有回调函数
    但是:改变不了 JS 单线程、异步的本质
    

    目前 JS 解决异步的方案有哪些?

    • jQuery Deferred
    • Promise
    • Async/Await
    • Generator

    相关文章

      网友评论

        本文标题:前端JavaScript高级面试技巧[1]

        本文链接:https://www.haomeiwen.com/subject/ijoygctx.html