面试2

作者: allen_tian | 来源:发表于2021-03-10 22:42 被阅读0次

    https://mp.weixin.qq.com/s/O3eY6_cHL6xrfh3t13qUUQ

    1.HTML生命周期

    DOMContentLoaded:DOM加载完毕,图片等一些资源没有加载完,js可以访问DOM,页面初始化;


    image.png

    loaded:全部加载完成,包括图片等资源;


    image.png
    beforeunloaded:关闭页面或者刷新页面之前,可以用在用户关闭刷新前提示确定是否关闭页面;
    image.png

    unloaded:关闭离开页面后,可用来清除localStorage;


    image.png
    document.readyState
    image.png

    2.作用域和执行上下文

    JavaScript代码的整个执行过程,分为两个阶段,代码编译阶段与代码执行阶段。

    编译阶段:由编译器完成,将代码翻译成可执行代码。这个阶段作用域规则会确定。
    执行阶段:由引擎完成,主要任务是执行可执行代码。执行上下文在这个阶段创建。

    作用域
    简单来说作用域就是一个区域,没有变量。作用域可以嵌套。作用域规定了如何查找变量,也就是确定当前执行代码对变量的访问权限。作用域在函数定义时就已经确定了,不是在函数调用确定。

    ES6 之前 JavaScript 只有全局作用域和函数作用域。ES6 后,增加了块级作用域(最近大括号的作用范围), 通过let 和 const 声明的变量。
    词法作用域
    JavaScript 采用词法作用域(lexical scoping),也就是静态作用域。

    所谓词法(代码)作用域,就是代码在编写过程中体现出来的作用范围,代码一旦写好了,没有运行之前(不用执行),作用范围就已经确定好了,这个就是所谓的词法作用域。

    执行上下文
    当在代码执行阶段执行到一个函数的时候,就会进行准备工作,这里的“准备工作”,就叫做"执行上下文(EC)",也叫执行上下文环境,也叫执行环境。
    创建阶段:在这个阶段,执行上下文会分别创建变量对象,建立作用域链,以及确定this的指向。
    代码执行阶段:开始执行代码,会完成变量赋值,函数引用,以及执行其他代码。

    image.png

    跨域

    image.png

    3.模块化

    AMD/CMD/CommonJs都是JS模块化开发的标准,目前对应的实现是RequireJS,SeaJs, nodeJs;
    CommonJS
    CommonJS 是以在浏览器环境之外构建 javaScript 生态系统为目标而产生的写一套规范,主要是为了解决 javaScript 的作用域问题而定义的模块形式,可以使每个模块它自身的命名空间中执行。

    实现方法:模块必须通过 module.exports 导出对外的变量或者接口,通过 require() 来导入其他模块的输出到当前模块的作用域中;
    AMD
    AMD:异步模块定义【浏览器端js】
    AMD 是 Asynchronous Module Definition 的缩写,意思是异步模块定义;采用的是异步的方式进行模块的加载,在加载模块的时候不影响后边语句的运行。主要是为前端 js 的表现指定的一套规范。

    实现方法:通过define方法去定义模块,通过require方法去加载模块。
    define(id?,dependencies?,factory): 它要在声明模块的时候制定所有的依赖(dep),并且还要当做形参传到factory中。没什么依赖,就定义简单的模块(或者叫独立的模块)
    require([modules], callback): 第一个参数[modules],是需加载的模块名数组;第二个参数callback,是模块加载成功之后的回调函数
    CMD
    CMD:通用模块定义【浏览器端js】
    CMD 是 Common Module Definition 的缩写,通过异步的方式进行模块的加载的,在加载的时候会把模块变为字符串解析一遍才知道依赖了哪个模块;

    主要针对浏览器端(异步加载文件),按需加载文件。对应的实现是seajs

    AMD和CMD的区别
    对于依赖的模块,AMD 是提前执行,CMD 是延迟执行。不过 RequireJS 从 2.0 开始,也改成可以延迟执行(根据写法不同,处理方式不同)。CMD 推崇 as lazy as possible(尽可能的懒加载,也称为延迟加载,即在需要的时候才加载)。
    CMD 推崇依赖就近,AMD 推崇依赖前置。

    import和require区别
    import和require都是被模块化使用。

    require是CommonJs的语法(AMD规范引入方式),CommonJs的模块是对象。import是es6的一个语法标准(浏览器不支持,本质是使用node中的babel将es6转码为es5再执行,import会被转码为require),es6模块不是对象。
    require是运行时加载整个模块(即模块中所有方法),生成一个对象,再从对象上读取它的方法(只有运行时才能得到这个对象,不能在编译时做到静态化),理论上可以用在代码的任何地方。import是编译时调用,确定模块的依赖关系,输入变量(es6模块不是对象,而是通过export命令指定输出代码,再通过import输入,只加载import中导的方法,其他方法不加载),import具有提升效果,会提升到模块的头部(编译时执行)
    export和import可以位于模块中的任何位置,但是必须是在模块顶层,如果在其他作用域内,会报错(es6这样的设计可以提高编译器效率,但没法实现运行时加载)。

    require是赋值过程,把require的结果(对象,数字,函数等),默认是export的一个对象,赋给某个变量(复制或浅拷贝)。import是解构过程(需要谁,加载谁)。

    4.继承

    原型链继承
    核心:“父类的实例作为子类的原型”

    new 创建新实例对象经过了以下几步:

    1.创建一个新对象
    2.将新对象的proto指向构造函数的prototype对象
    3.将构造函数的作用域赋值给新对象 (也就是this指向新对象)
    4.执行构造函数中的代码(为这个新对象添加属性)
    5.返回新的对象

    // 1. 创建一个新对象
    var Obj = {};
    // 2. 将新对象的_proto_指向构造函数的prototype对象
    Obj._proto_ =  Animal.prototype();
    // 3. 执行构造函数中的代码(为这个新对象添加属性) 
    Animal.call(Obj);
    // 4. 返回新的对象
    return Obj;
    

    特点:
    1.实例可继承的属性有:实例的构造函数的属性,父类构造函数属性,父类原型的属性
    2.非常纯粹的继承关系,实例是子类的实例,也是父类的实例
    3.父类新增原型方法/原型属性,子类都能访问到

    缺点:
    1.新实例无法向父类构造函数传参。
    2.继承单一。
    3.所有新实例都会共享父类实例的属性。(原型上的属性是共享的,一个实例修改了原型属性,另一个实例的原型属性也会被修改!)
    4.要想为子类新增原型上的属性和方法,必须要在new Animal()这样的语句之后执行,不能放到构造器中
    构造函数继承
    重点:用.call()和.apply()将父类构造函数引入子类函数(在子类函数中做了父类函数的自执行(复制))

    特点:
    1.只继承了父类构造函数的属性,没有继承父类原型的属性。
    2.解决了原型链继承缺点1、2、3。
    3.可以实现多继承,继承多个构造函数属性(call多个)。
    4.在子实例中可向父实例传参。
    缺点:
    1.能继承父类构造函数的属性。
    2.无法实现构造函数的复用。(每次用每次都要重新调用)
    3.每个新实例都有父类构造函数的副本,臃肿。
    4.实例并不是父类的实例,只是子类的实例

    组合继承(原型链继承和构造函数继承)(常用)
    核心:通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用

    function Cat(name){
      Animal.call(this, name);
      this.name = name;
    }
    Cat.prototype = new Animal();
    Cat.prototype.constructor = Cat;
    
    var cat = new Cat();
    console.log(cat.name);
    console.log(cat.sleep());
    console.log(cat instanceof Animal); // true
    console.log(cat instanceof Cat); // true
    

    重点:结合了两种模式的优点,传参和复用

    特点:
    1.可以继承父类原型上的属性,可以传参,可复用。
    2.每个新实例引入的构造函数属性是私有的。
    3.既是子类的实例,也是父类的实例

    缺点:
    调用了两次父类构造函数(耗内存),子类的构造函数会代替原型上的那个父类构造函数。

    5.数组多层扁平化

    将多维数组变为一维数组,遍历数组如果遍历的值里还是数组就递归遍历,直到不是数组,使用concat

    3.虚拟 dom 是如何提升性能的?

    虚拟dom就是一个真实dom的JS对象;
    以前没有虚拟dom,如果需要比较两个页面的差异,我们需要通过对真实dom进行比对。
    真实dom节点是非常复杂的,它里面会绑定的事件,它会有属性,背后会有各种方法,所有两个真实dom比对,非常耗性能。
    于是将dom对象变成了JS对象,JS对象就没有乱七八糟的dom身上的特性。
    单纯的比较JS对象就比较快,有效的提高了react的性能。测试两个dom通过递归方法进行比对,结果很慢。

    4.setState 是同步还是异步的

    “setState 只在合成事件和钩子函数中是“异步”的,在原生事件和 setTimeout 中都是同步的。”
    合成事件:就是react 在组件中的onClick等都是属于它自定义的合成事件
    原生事件:比如通过addeventListener添加的,dom中的原生事件。
    在 React的生命周期和合成事件中, React仍然处于他的更新机制中,这时无论调用多少次 setState,都会不会立即执行更新,而是将要更新的·存入 _pendingStateQueue,将要更新的组件存入 dirtyComponent。

    当上一次更新机制执行完毕,以生命周期为例,所有组件,即最顶层组件 didmount后会将批处理标志设置为 false。这时将取出 dirtyComponent中的组件以及 _pendingStateQueue中的 state进行更新。这样就可以确保组件不会被重新渲染多次。

    当我们在执行 setState后立即去获取 state,这时是获取不到更新后的 state的,因为处于 React的批处理机制中, state被暂存起来,待批处理机制完成之后,统一进行更新。
    所以。setState本身并不是异步的,而是 React的批处理机制给人一种异步的假象。

    5.context

    定义: Context提供了一种方式,能够让数据在组件树中传递,而不必一级一级手动传递。父直接传给孙。

    6.受控组件和非受控组件的区别

    值受到react控制的表单元素
    调用 React.createRef() 方法创建ref对象
    将创建好的 ref 对象添加到文本框中
    通过ref对象获取到文本框的值
    非受控组件: 表单组件没有value prop就可以称为非受控组件

    7.getSnapshotBeforeUpdate

    getSnapshotBeforeUpdate() 方法

    1:在render之前调用,state已更新

    2:典型场景:获取render之前的dom状态

    8.数组的使用

    .some()和.every()的区别
    .some()判断数组中的元素是否满足指定条件(函数),有一个满足就返回true;some不会对空数组检测,some不会改变原数组;
    every()判断数组中的所有元素是否满足指定条件(函数),有一个不满足就停止返回false,后面的就不继续检查;所有元素都满足就返回true.

    9.数组reduce的使用

    https://www.jianshu.com/p/e375ba1cfc47

    10.函数柯里化

    接收函数作为参数的函数,都可以叫做高阶函数,
    柯里化,其实就是高阶函数的一种特殊用法,
    柯里化是指这样一个函数(假设叫做createCurry),他接收函数A作为参数,运行后能够返回一个新的函数。并且这个新的函数能够处理函数A的剩余参数。
    https://www.jianshu.com/p/5e1899fe7d6b

    11.手写正则

    https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular_Expressions

    12.webpack打包优化

    https://w.cnblogs.com/xiaohuochai/p/9178390.html

    13.按需加载

    组件先在路由里注册但不进行组件的加载与执行,等到需要访问组件的时候才进行真正的加载。
    懒加载前提:
    进行懒加载的子模块(子组件)需要是一个单独的文件。

    第一次会加载页面, 以后的每次页面切换,只需要进行组件替换;减少了请求体积,加快页面响应速度,降低了对服务器的压力
    懒加载或者按需加载,是一种很好的优化网页或应用的方式。这种方式实际上是先把你的代码在一些逻辑断点处分离开,然后在一些代码块中完成某些操作后,立即引用或即将引用另外一些新的代码块。这样加快了应用的初始加载速度,减轻了它的总体体积,因为某些代码块可能永远不会被加载。
    懒加载前提的实现:ES6的动态地加载模块——import()。
    https://blog.csdn.net/weixin_44869002/article/details/106288371
    https://www.cnblogs.com/joyco773/p/9051401.html
    https://blog.csdn.net/weixin_44869002/article/details/106288371

    14.AMD、UMD、Common.js是什么?区别是什么?

    AMD:以浏览器为第一的原则发展,选择异步加载模块。它的模块支持objects、functions、constructors、strings、JSON等各种类型的模块。因此在浏览器中它非常灵活。
    CommonJS:以服务器端为第一的原则发展,选择同步加载模块。它的模块是无需包装的且贴近于ES.next/Harmony的模块格式。但它仅支持对象类型(objects)模块。
    UMD:AMD和CommonJS的结合。实现很简单:先判断是否支持NodeJS模块格式(exports是否存在),存在则使用NodeJS模块格式。
    再判断是否支持AMD(define是否存在),存在则使用AMD方式加载模块。前两个都不存在,则将模块公开的全局(window或global)。

    15.白屏处理

    https://blog.csdn.net/yanhang0227/article/details/106559840

    image.png

    相关文章

      网友评论

          本文标题:面试2

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