前端基础知识学习第四节

1.

  console.log(1);
  setTimeout(function() {
    console.log(2);
  }, 0);
  async function async1() {
    console.log(3);
    await console.log(4);
    console.log(5);
  }
  async1();
  Promise.resolve().then(function() {
    console.log(6);
  });
  console.log(7);
  答案:1 3 4 7 5 6 2

  分析:这里主要是JS Event Loop相关的知识点,比较疑惑的地方是await 后面的语句,那么先复习一下async awiat 相关的知识
     async: 申明一个函数是异步的,返回是一个promise对象,但必须要等到内部所有await后面的表达式都执行完,状态才会发生改变

     await: 等待意思,必须出现在async修饰的函数中,等待的可以是promise对象也可以是其他的值,但如果是其他值,则会被转成一个立即resolve的

        Promise对象,await实际上是一个让出线程的标志,首先await后面的函数先执行一遍,然后就会跳出整个async函数来执行后面JS执行栈的代码,

        等本轮事件循环执行完以后再跳回到async函数中等待await。

     所以第一轮事件循环执行完以后输出 1 3 4 7,微任务队列执行完以后输出5 6,然后执行宏任务输出2

     参考链接:https://blog.csdn.net/Liu_yunzhao/article/details/90734257

2.

  写出JS主要数据类型和两种复合型数据类型,并写出下列结果:

  string number boolean null undefined symbol
  object array function

  console.log(null == NaN); // NaN不等于任何值,所以false
  console.log(Number(undefined)); // 引用W3school介绍,构造函数 Number() 可以不与运算符 new 一起使用,而直接作为转化函数来使用。以这种方式调用                     Number() 时,它会把自己的参数转化成一个数字,然后返回转换后的原始数值(或 NaN),undefined转成一个数字肯定
                    是失败的,所以返回NaN

  console.log(null == undefined); // true

  console.log(Number('')); // 0
  console.log(null == false); // true和false与任何值比较的时候会先转化成1和0然后再做比较,所以这里是true

  console.log(isNaN(23)); // false

  console.log(false == ''); // true

  console.log(typeof NaN); // NaN 属于数字,所以结果是"number"

  console.log(false == 0); // true

  console.log(isNaN(NaN)); // true

  console.log(2 + 1 + '3'); // 33

  console.log(NaN == NaN); // NaN不等于任何值所以false

  console.log('3' + 2 + 1); // 321

  console.log(undefined == undefined); // true

3.

  写出执行结果:

  function t(a) {
    var a = 'hello';
    alert(a);
    function a() {
      alert(null);
    }
    alert(a);
  }
  t(null); 
  答案:hello,hello
  分析:JS编译器会对函数声明进行提升,同时也会对变量声明就行提升,但是函数提升优先于变量,所以var a = 'hello';会覆盖函数声明的提升,

     所以两条alert(a);语句都会打印出来hello。

  function fn(b) {
    alert(b);
    function b() {
      alert(b);
    }
    b();
  }
  fn(10);
  答案:"function b() { alert(b); }","function b() { alert(b); }"
  分析:JS编译器会对函数声明进行提升,所以两个条alert(b)语句都是打印出来函数b的字符串形式

  function a(b) {
    alert(b);

    b = function() {
      alert(b);
    }

    b();
  }
  a();
  答案:JS编译器不会对函数变量进行提升,所以第一个alert(b)语句访问到的是undefined,因为没有传递b参数,第二条alert(b)语句

     打印出来的是函数b的字符串形式"function() { alert(b); }"

4.

  封装一个函数正则匹配获取当前页面中的class类名

  function getClassNames() {
    let regexp = /class="[\w\s-]*"/g;

    return document.documentElement.outerHTML.match(regexp);
  }
  参考:https://www.runoob.com/regexp/regexp-metachar.html

 5.

  随机打印1-100之间10个数字,去重后且取出该10个数字之间的最大值和最小值

  答案:

    let arr = [];
  for (let i = 0; i < 10; i++) {
    arr.push(Math.floor(Math.random() * 100) + 1);
  }

  arr = Array.from(new Set(arr));
  arr = arr.sort((a, b) => a - b);

  let max = arr[arr.length - 1];
  let min = arr[0];
  // let max = Math.max.apply([], arr);
  // let min = Math.min.apply([], arr);

 6.

  JS实现一个对象的深拷贝?

  答案:
  function deepClone(target, src) {
    for (let key in src) {
      let val = src[key];

      if (Object.prototype.toString.call(val) === '[object Object]') { // 当前属性值是对象
        target[key] = {};

        deepClone(target[key], val);
      }

      if (Array.isArray(val)) { // 当前属性值是数组
        target[key] = JSON.parse(JSON.stringify(val));
      }

      // 其他对象
    }
  }
  参考:https://github.com/vuejs/vuex/blob/dev/src/util.js#L22

7.
  请思考以下代码运行结果,为什么?

  document.body.addEventListener('click', () => {
    Promise.resolve(1).then(val => {
      console.log(val);
    });

    console.log(2);
  }, false);
  document.body.addEventListener('click', () => {
    Promise.resolve(3).then(val => {
      console.log(val);
    });

    console.log(4);
  }, false);

  答案:
  在之前浏览器对DOM元素通过addEventListener绑定多次事件处理方式是不一样的,有的浏览器能够响应多次绑定的同一事件,而有的浏览器不可以,这里考察的
  是在浏览器支持多次绑定同一个事件的情况。了解过JS引擎异步任务处理方式之后我们知道DOM事件属于宏任务,所以这里相当于添加了两个宏任务,宏任务会从
  上到下一个一个执行,执行第一个宏任务之后打印2,接下来取出微任务执行打印出来1,接下来主线程会继续从任务队列取出下一个宏任务执行打印出来4,宏任务
  执行完之后取出微任务执行打印出来3

8.

  在浏览器地址栏输入一个url回车发生了什么?

  答案:
  1)首先会做DNS查找,域名解析
  2)浏览器与服务器建立TCP连接,客户端与服务器进行3次握手,客户端开始发送HTTP请求报文,服务器返回响应报文(HTML字符串)给客户端
  3)浏览器解析HTML字符串生成,生成DOM Tree,解析CSS文件CSSOM Tree,将DOM Tree和CSSOM Tree结合,生成Render Tree(渲染树),
  根据Render Tree渲染绘制,把页面显示到屏幕上
  参考:https://zhuanlan.zhihu.com/p/43282197

9.

  介绍下深度优先遍历和广度优先遍历,如何实现?

  答案:
  深度优先遍历类似DFS与树的先序遍历类似,假设初始状态所有顶点均未被访问,则从某个顶点v出发,首先访问该顶点然后依次从它的各个未被访问的邻接点出发
  深度优先搜索遍历图,直至图中所有和v有路径相通的顶点都被访问到。若此时尚有其他顶点未被访问到,则另选一个未被访问的顶点作起始点,重复上述过程,直
  至所有顶点都被访问到为止。
  // 递归方式
  function deepTraversal(node, nodeList = []) {
    if (node) {
      nodeList.push(node);
      let childrens = node.children;
      
      for (let i = 0; i < childrens.length; i++) {
        let children = childrens[i];
        
        deepTraversal(children, nodeList);
      }
    }
    
    return nodeList;
  }
  // 非递归方式
  function deepTraversal(node) {
    let stack = [];
    let nodeList = [];

    if (node) {
      stack.push(node);
      
      while (stack.length) {
        let item = stack.pop();
        let childrens = item.children;
        nodeList.push(item);

        // nodeList = [] stack = [parent]
        // nodeList = [parent] stack = [child3, child2, child1]
        // nodeList = [parent, child1] stack = [child3, child2, child1-3, child1-2, child1-1]
        // nodeList = [parent, child1, child1-1] stack = [child3, child2, child1-3, child1-2, child1-1-3, child1-1-2, child1-1-1]
        for (let i = childrens.length - 1; i >= 0; i--) {
          let children = childrens[i];
          
          stack.push(children);
        }
      }
    }
    
    return nodeList;
  }
  广度优先遍历BFS
  function widthTraversal(node) {
    let stack = [];
    let nodeList = [];
    
    if (node) {
      statck.push(node);

      while (stack.length) {
        let item = stack.shift();
        let childrens = item.children;
        nodeList.push(item);

        for (let i = 0; i < childrens.length; i++) {
          let children = childrens[i];

          stack.push(children);
        }
      }
    }

    return nodeList;
  }
  参考:https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/9

10.

  请分别用深度优先思想和广度优先思想实现一个拷贝函数?  

  function deepCloneByDFS() {
    
  }
  function deepCloneBFS() {

  }

11.

  ES5/ES6的继承除了写法不同以外还有什么区别?

  答案:
  ES5继承
  function A() {
    this.name = 'Herly';
  }
  A.prototype.sayName = function() {
    alert(this.name);
  }
  function B() {}
  B.prototype = new A(); // 子类的prototype指向父类的实例
  B.prototype.__proto__ == A.prototype // true
  B.__proto__ == Function.prototype // true
  ES6继承
  class A() {
    constructor() {
      this.name = 'Herly';
    }
    sayName() {
      alert(this.name);
    }
  }
  class B extends A {
    constructor() {
      super();
    }
  }
  B.prototype.__proto__ == A.prototype; // true
  B.__proto__ == A // true,这个地方有区别
  其实ES6本质上也是通过原型继承,class继承只是原型继承的语法糖

12.

  实现一个Promise.all方法?

  答案:
  Promise.all = function(arr) { // 接收数组参数
    return new Promise(function (resolve, reject) {
      let args = Array.prototype.slice.call(arr, 0); // 这里是为了复制一份参数
      let remaining = args.length;

      function res(i, promise) {
        try {
          promise.then(function(val) {
            args[i] = val;
            remaining--;

            if (!remaining) {
              resolve(args);
            }
          });
        } catch (e) {
          reject(e);
        }
      }
      
      for (let i = 0; i < args.length; i ++) {
        res(i, args[i]);
      }
    });
  }

猜你喜欢

转载自www.cnblogs.com/typeof/p/12229386.html