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的指向。
代码执行阶段:开始执行代码,会完成变量赋值,函数引用,以及执行其他代码。
跨域
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)。
网友评论