Loading

1.23面试题汇总

对象类型与原始值类型的不同之处?

  • 原始值类型保存在栈中,值是直接存储在变量访问的位置, 复制原始类型变量的值时,会将值拷贝一份,和原来的值是相互独立的

  • 引用变量的名称储存在栈中,但是把其实际对象储存在堆中,且存在一个指针由变量名指向储存在堆中的实际对象,复制对象类型变量的值时,会将存储在变量中的值拷贝一份,也就是它所指向的对象在内存中的地址值。复制操作结束后,它们俩指向同一对象吗,存在深浅拷贝问题,新对象修改会影响旧对象

typeof能否正常判断类型?有何局限

不能,原始值类型除了null都可以正确判断,对象类型除了function其它都显示'object'

== 与 === 区别

  • == 首先会判断两者类型是否相同。相同的话就直接进行比较
    如果对比双方的类型不一样的话,就会进行类型转换
  • === 判断两者类型和值是否相同

模块化有什么优点?有哪几种方案

优点:解决命名冲突 提高代码复用性 提高项目的可维护性

  • IIFE(立即执行函数)
var module1 = (function(){

    var _count = 0;

    var m1 = function(){
      //...
    };

    var m2 = function(){
      //...
    };

    return {
      m1 : m1,
      m2 : m2
    };

  })();
  • CommonJS

CommonJS采用的是同步导入的方式
CommonJS模块规范主要分为三部分:模块定义、模块标识、模块引用。主要在node中使用,webpack中也有使用

//模块定义、模块标识
//在数据库模块database.js文件中,把各个函数写好,然后暴露出来
 module.exports={
    'saveDatabase':saveDatabase,//保存数据函数
    'saveLastSpider':saveLastSpider,//保存最后一条数据函数
    'getInfoByType':getInfoByType,//通过类型查找函数
    'getInfoByOrder':getInfoByOrder,//通过排序查找函数
 }
// 模块引用
var database = require('./database');//引用模块,将生成的对象指向database对象(命名可以是任意定的)
database.saveDatabase([1,2,3,4,5]);//通过database对象调用相应的函数
  • AMD(Asynchronous Module Definition)、

AMD采用异步方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。

// AMD需要接受两个参数
// 第一个参数[module],是一个数组,里面的成员就是要加载的模块;第二个参数callback,则是加载成功之后的回调函数
// 导出
define([id], [dependencies], factory) // 模块名 依赖前置  工厂方法(对象就作为该模块的输出值)
define('mod2', ['mod1'], function (mod1) {
    // ...
    return {
        // ...
    }
})

导入

require(['mod1','mod2'],function(mod1, mod2){
                // ...
            });
  • CMD

CMD是SeaJS 在推广过程中对模块定义的规范化产出

对于依赖的模块AMD是提前执行,CMD是延迟执行

define(function (requie, exports, module) {
    
    //依赖可以就近书写
    var a = require('./a');
    a.test();
    
    ...
    //软依赖
    if (status) {
    
        var b = requie('./b');
        b.test();
    }
});
  • ES Module

ES6在语言标准的层面上,实现了模块功能,成为浏览器和服务器通用的模块解决方案,完全可以取代 CommonJS 和 AMD 规范
模块功能主要由两个命令构成:export和import。export命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能;

ES Module 与 CommonJS的差别

  • CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。

  • CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。

  • CommonJS 模块的require()是同步加载模块,ES6 模块的import命令是异步加载,有一个独立的模块依赖的解析阶段。

  • CommonJS 是动态语法,可以写在判断中 ES Module 是 静态语法,只能写在最顶层(ES2020的import()可以实现动态语法)

  • CommonJS 的this指向当前模块,ES Module采用严格模式,thisundefined

什么是Promise的链

每次调用 then 或 catch 之后返回的都是一个全新的Promise,因此又可以接着使用then或catch方法,由此形成promise链

Promise 构造函数执行和 then 函数执行有什么区别

  • 构造 Promise 的时候,构造函数内部的代码是立即执行的(同步代码)

  • then函数在promise.resolve()执行后执行

Promise.resolve()在本轮“事件循环”结束时执行

通过 new 的方式创建对象和通过字面量创建有什么区别

// new Object()
// {}
  • new Object() 方式创建对象本质上是方法调用,涉及到在proto链中遍历该方法,当找到该方法后,又会生产方法调用必须的 堆栈信息,方法调用结束后,还要释放该堆栈,性能不如字面量的方式。

  • 通过对象字面量定义对象时,不会调用Object构造函数。

字面量创建对象,不会调用 Object构造函数, 简洁且性能更好

为什么0.1+0.2!==0.3?

因为在JS中,浮点数是使用64位固定长度来表示的,其中的1位表示符号位,11位用来表示指数位,剩下的52位尾数位,由于只有52位表示尾数位。

0.1转为二进制是一个无限循环数0.0001100110011001100......(1100循环)

由于只能存储52位尾数位,所以会出现精度缺失,把它存到内存中再取出来转换成十进制就不是原来的0.1了,就变成了0.100000000000000005551115123126

0.1+0.2 转换成二进制计算后

// 0.1 和 0.2 都转化成二进制后再进行运算
0.00011001100110011001100110011001100110011001100110011010 +
0.0011001100110011001100110011001100110011001100110011010 =
0.0100110011001100110011001100110011001100110011001100111

// 转成十进制正好是 0.30000000000000004

什么是执行栈?

  • 首先栈特点:先进后出

  • 当进入一个执行环境,就会创建出它的执行上下文,然后进行压栈,当程序执行完成时,它的执行上下文就会被销毁,进行弹栈。

  • 栈底永远是全局环境的执行上下文,栈顶永远是正在执行函数的执行上下文

  • 只有浏览器关闭的时候全局执行上下文才会弹出

为什么js是单线程的?单线程优缺点是什么?

  • 因为JS里面有可视的Dom,如果是多线程的话,这个线程正在删除DOM节点,另一个线程正在编辑Dom节点,导致浏览器不知道该听谁的

  • 优点:不会出现因线程之间争夺资源导致的死锁现象,所有代码都是同步执行的,没有线程切换的资源开销

  • 缺点:单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着,容易造成堵塞

事件触发的过程是怎样的

三个阶段:

  • 捕获阶段:事件从根节点流向目标节点,途中流经各个DOM节点,在各个节点上触发捕获事件,直到达到目标节点。

  • 目标阶段:事件到达目标节点时,就到了目标阶段,事件在目标节点上被触发

  • 冒泡阶段:事件在目标节点上触发后,不会终止,一层层向上冒,回溯到根节点

下面输出是什么

<div id="div1">
        <div id="div2">
            <div id="div3">
            </div>
        </div>
    </div>
    <script>
        const div1 = document.getElementById('div1')
        const div2 = document.getElementById('div2')
        const div3 = document.getElementById('div3')
        div1.addEventListener('click', function () {
            console.log(1);
        })
        div1.addEventListener('click', function () {
            console.log(3);
        }, false)
        div1.addEventListener('click', function () {
            console.log(2);
        }, true)

        div2.addEventListener('click', function () {
            console.log(4);
        })
        div2.addEventListener('click', function () {
            console.log(6);
        }, false)
        div2.addEventListener('click', function () {
            console.log(5);
        }, true)

        div3.addEventListener('click', function () {
            console.log(7);
        })
        div3.addEventListener('click', function () {
            console.log(9);
        }, false)
        div3.addEventListener('click', function () {
            console.log(8);
        }, true)

        div1.dispatchEvent(new Event('click'))
        div2.dispatchEvent(new Event('click'))
        div3.dispatchEvent(new Event('click'))
        // 结果: 1 3 2 2 4 6 5 2 5 7 9 8 
        // dispatchEvent方法默认的是不进行冒泡
        // 分析: div1被点击后执行 1 3 2 然后2是div捕获了div2的第三个点击事件
        // 4 6 5 是 div2的点击事件
        // 2 5 分别是div1和div2捕获了div3的点击事件 然后div3的点击事件执行 输出 7 9 8
    </script>

posted @ 2021-01-29 16:10  不吃苦瓜^  阅读(60)  评论(0编辑  收藏  举报