前端面试题_fnjsoo

+ 保护函数内变量安全,实现封装,防止变量流入其他环境中发生命名冲突
+ 在内存中维持一个变量,可以做缓存
+ 匿名只执行函数可以减少内存消耗
  • 缺点
    • 私有变量不能被销毁,增大了内存消耗,容易造成内存泄露;解决方法使用完后手动赋值为null
    • 由于闭包涉及跨域访问,会导致性能损失;通过把跨域作用域变量存储在局部变量中,然后直接访问局部变量,来减轻对执行速度的影响

this指向

this概念

  1. 普通函数:谁调用就指向谁,没有调用者,就指向window对象
  2. 箭头函数:this指向函数作用域所用对象

this

全局this
  1. 在全局作用域下,this始终指向window
	var a=1
    console.log(this)
    console.log(this.a)

函数内的this
  1. 直接调用函数,指向window对象
  2. 被别的调用,指向调用对象
const btn=document.querySelector('.btn')
    function test(){
        console.log(this)
    }
    test() // window //注意这个在严格模式下为undefined
    window.test() // window
    btn.onclick=test // 指向<button class="btn"></button>节点

对象中的this
  1. 也是谁调用就指向谁
  2. 但得注意this丢失问题
	const obj={
        name:'obj',
        test:function(){
            console.log(this.name)
        }
    }
    obj.test() // 指向obj,显示obj
    const fun=obj.test
    fun() // 指向window,显示为空

构造函数中this

指向实例对象

	function Person(name){
        this.name=name;
    
    }
    const p1=new Person('jack')
    console.log(p1.name) // 指向实例对象,显示jack

原型链上this

会先在自己实例上找,没有的话,顺着原型链找

 	var oo = {
        f: function() {
            return this.a + this.b;
        }
    };

    var p = Object.create(oo);
    p.a = 1;
    p.b = 2;
    console.log(p.f()); // 3

箭头函数this

箭头函数中this是静态的,不会随着调用而改变,总是指向上一作用域中的this

var name = 'window'
    const obj = {
        name:'obj',
        fn:()=>{
            console.log(this.name)
        }
    }
    obj.fn() //window


原型链

请添加图片描述

  • Object的隐式原型为null
  • 构造函数Function的原型和隐式原型都指向Function原型
  • 原型链作用是在我们使用方法时,不仅会在实例上找,也会在原型链上查找

new函数过程发生了什么

  1. new时先创建一个新对象
    let obj=new Object();
  2. 将空对象的__proto__成员指向了原函数对象prototype成员对象
    obj.proto=Person.prototype
  3. 修改this到obj上
    Person.call(obj)
  4. 返回一个对象

详情


在地址栏输入url到页面出现,经历了那些过程

  1. DNS查询/解析(先看本地是否有缓存)
    1. 浏览器DNS缓存
    2. 计算机DNS缓存
    3. 路由器DNS缓存
    4. 网络运营商DNS缓存
    5. 递归查询查找 a.b.c.d.com
  2. TCP三次握手
    1. 浏览器告诉服务器我准备好发送请求了
    2. 服务器告诉浏览器我也准备好,需要再次确认一下
    3. 浏览器告诉给服务器确认完毕,马上发送请求
  3. 发送请求
  4. 返回响应
  5. 渲染页面
    1. 调用html解析器将HTML解析成DOM树
    2. 调用css解析器将CSS解析成CSSOM数
    3. 调用js引擎解析JS
      1. 如果修改了DOM节点,重新生成DOM树
      2. 如果修改了CSS节点,重新生成CSSOM树
    4. 将DOM树 + CSSOM树 = render树
    5. layout布局
    6. render渲染
  6. 断开连接TCP四次挥手
    1. 浏览器告诉服务器请求报文发送完毕
    2. 服务器告诉浏览器请求报文接受完毕,可以等待断开
    3. 服务器告诉浏览器响应报文发送完毕
    4. 浏览器告诉服务器响应报文接受完毕,可以断开
  7. 服务器断开连接,然后浏览器断开连接

详情


Vue2数据响应式原理及缺陷

概述

当对象本身或对象属性被读和写的时候,我们需要知道该数据被操作了,并在这过程中执行一些函数,例如:render函数,而这一过程我把它定义为数据响应式

实现

Observer
  1. Observer通过Object.defineProperty将普通对象包装成一个带有getter\setter属性的特殊对象
  2. 对于对象,递归遍历对象的所有属性,以完成深度属性转换
  3. 对于数组,vue会重写数组的一些方法,更改Array的隐式原型
Dep
  1. Observe只是让Vue感觉读写操作了,具体干什么还需要Dep来做
  2. 主要负责在getter中收集依赖,在setter中通知依赖更新
Watcher
  1. 当Dep收集依赖后,当数据发生改变时,准备派发通知,不知道派发给谁,或者不知道谁用了该数据,这时就需要watcher
  2. 当数据发生改变时,vue不直接通知相关依赖更新,而是通知依赖对应的watcher实例去执行
Scheduler
  1. 当watcher收到派发的更新通知后,watcher不会立即执行,而是将自己交给一个调度器scheduler
  2. 调度器scheduler维护一个执行队列,同一个watcher在该队列中只会存在一次,队列中的watcher不会立即执行,而是通过nextTick的工具函数执行,nextTick是一个微队列,会把需要执行的watcher放到事件循环的微队列中执行

缺陷

  1. 浅度劫持,对于对象数组要进行处理
  2. 对象新增的属性没有响应式
  3. 数组的部分操作没有响应式(修改数组元素第一个的值,修改数组长度)

参考


双向绑定原理

原理

  1. Vue双向绑定原理是通过数据劫持结合发布订阅者模式来实现的
  2. 数据和视图同步,视图变化,数据也就变化;数据变化,视图也就变化
	const obj={}
    Object.defineProperty(obj,'hello',{
        get:function(){
            console.log("调用get方法")
        },
        set:function(newVal){
            console.log("调用set方法"+newVal)
        }
    })
    obj.hello; // 调用get方法
    obj.hello='hi'; // 调用set方法hi

详情


watch与computed,methods三者区别

作用机制上

  1. 侦听属性watch与计算属性computed都是以vue的依赖追踪机制(订阅者模式)为基础的,只有当一个函数变化时,才会调用相关的函数
  2. methods里面是用来定义函数的,需要手动调用才会执行

作用性质上

  1. methods中定义的是函数,你需要进行函数调用(func())
  2. computed是计算属性,他在使用了和data是对象中的数据类型是同一类
  3. watch类似于监听机制+事件机制
watch: {
   firstName: function (val) {  this.fullName = val + this.lastName }
}
/\*
\* 只有当firstName触发后才执行,而firstName对应的函数就相当于监听到事件发生后执行的方法
\*/

watch与computed异同

相同
  1. 他们都是在依赖数据发生改变的情况下,被依赖的数据根据预先定义好的函数,发生自动的变化
不同
  1. watch擅长处理
    1. 一个数据影响多个数据
    2. 当需要在数据变化时执行异步或开销较大的操作时
    3. watch可以监听新值与旧值
    4. watch属性还可以用来监听路由router的变化,只是这里的监听元素是固定的
  2. computed擅长处理(计算属性不能异步,异步函数return是没有任何意义的)
    1. 多个数据影响一个数据
    2. 当存在复杂逻辑、需要用到缓存时

methods

  1. methods与watch和computed是一种不同的概念,methods指的是方法也就是定义要用的函数,他不会主动触发需要在被调用时才会触发
  2. methods与computed看不出来差别,但要注意computed是存在缓存的

参考


Vue组件为啥需要name属性

  1. 当项目使用keep-alive缓存时,可用组件name属性进行缓存过滤
  2. 当组件有name时,就可以组件自己调用自己(递归调用)
  3. 使用vue-tool时,工具中组件名就是name属性决定的

Vue传值方式

props与$emit(在封装的组件中很常用)

  1. 在父组件中通过属性传值与子组件,子组件在props接收
  2. 子组件通过this.$emit(‘事件名’,值),父组件通过@事件名="事件处理函数"接受父组件传递的值

Vue.prototype.$bus

  1. 在mian.js中定义
  new Vue({
  	......
  	beforeCreate() {
  		Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm
  	}
  })

  1. 要传值的组件定义this. b u s . bus. bus.emit(‘事件名’,值)
  2. 接收值的组件定义this. b u s . bus. bus.on(‘事件名’,回调函数),也可以通过this. b u s . bus. bus.off(‘事件名’)解绑事件

VueX

  1. state 用来存储数据
  2. mutation 用来对state进行成员操作
  3. getters 加工state成员给外界
  4. actions 也是用来对state进行成员操作不过是异步操作而且需要提交给mutation,在action中不能直接更改
  5. modules 模块化状态管理

ref传值

  • 在父组件中在子组件上绑定ref,通过this.$refs.ref名字.子组件接受方法(数据)
 <Test ref="test"/>
 ...
 this.$refs.test.childMethod('val12343');

  • 子组件定义方法接受子组件接受方法(val){ // val就是传过来的值}
methods: {
    childMethod(val) {
      console.log(val); // val就是传过来的值 val12343
    },
  },

LoaclStorage(存储于本地)

  1. 先将需要的值存储于本地localStorage.setItem(“存储名”,存储值)
  2. 获取值通过localStorage.getItem(“存储名”)
  3. 不需要时也可以删除localStorage.removeItem(“存储名”)

Vue-router路由实现原理

Hash路由模式

  1. hash虽然标记在url中,但他不会发送网络请求,对服务端完全无用
  2. 通过window.location.hash实现的

History路由模式

  1. 通过window.history实现的
  2. 分别通过调用history.pushState()和history.replaceState()方法来实现push和replace方法
  3. pushState和replaceState方法修改浏览器历史记录栈后,虽然当前URL改变了,但浏览器不会立即发送请求该URL,这为更新视图但不重新发送请求提供了基础
  4. Hash与History两种模式的go、back和forward三个方法都是通过window.history.go()来实现的

Abstract模式

vue-router为非浏览器环境准备了一个abstract模式,其原理为用一个数组stack模拟出浏览器历史记录栈的功能


Vue-router传参

router-link传参

params
<router-link :to="{name:'home',params:{id:1}}" />
<router-link :to="{path:'home',params:{id:1}}" />
// 取参 this.$route.params.id 

query
<router-link :to="{name:'home',query:{id:1}}" /> 
<router-link :to="{path:'home',query:{id:1}}" /> 
// 取参 this.$route.query.id

this.$router.push()传参

params
this.$router.push({name:'home',params:{id:1,age:2}})

query
this.$router.push({path:'/home',query:{id:1,age:2}}) 
this.$router.push(`/home?id=1`);

参考


Vue-router有那些守卫

全局前置守卫beforeEach

  • 在router中进行配置
  • router.beforeEach((to,from,next)=>{})
    • to要到哪个路由去
    • from从哪个路由来
    • next下一步(无论失败与否都要调用,否则会阻止程序继续执行)

全局后置守卫 afterEach

  • router.afterEach((to,from)=>{})

组件内守卫(相当于给组件增加生命周期)

beforeRouteEnter 进入组件之前

beforeRouterEnter(to,from,next){}

beforeRouteUpdate 组件被复用时调用

beforeRouterUpdate(to,from,next){}

beforeRouteLeave 离开组件时调用

beforeRouteLeave(to,from,next){}


移动端防止事件穿透

事件穿透原理

  1. 假如页面上有两个元素A和B,B元素在A元素之上,B为遮罩层mask。我们在B元素上绑定 touchstart事件,作用是隐藏B元素;A元素上绑定click事件。我们发现,当我们点击B元素,B元素被隐藏了,随后,也触发了A元素的click事件
  2. 事件执行的顺序是 touchstart >touchmove>touched ==>click,click事件有300ms的延迟,当 touchstart事件把B元素隐藏之后,隔了300ms,浏览器触发了click事件,但是此时B元素不见了,所以该事件被派发到了A元素身上。如果A元素是一个链接,那此时页面就会意外地跳转了

解决方案

禁用缩放

click事件的延迟300ms是为了移动端缩放

 <!-- 1.禁用缩放 user-scalable=no -->
 <meta name="viewport" content="width=device-width, initial-scale=1.0,user-scalable=no">

延迟上层元素消失

touchstart后延迟350ms后再隐藏,先把透明度设为0,解决视觉层面效果,在设置定时器延迟,让元素消失

$('.mask').on('touchstart',function() {
    console.log('mask-touchstart');
    $(this).css('opacity', 0); // 设置视觉层面消失
    setTimeout(function() {
        $('.mask').css('dispaly', 'none'); // DOM节点消失
    },350);
})

pointer-events,让被覆盖元素短时间内无法触发click
  • pointer-events两个属性
    • auto默认值,鼠标默认值不会穿透当前层
    • none使元素无法触发事件
$('.mask').on('touchstart',function() {
    console.log('mask-touchstart');
    $(this).css('display', 'none');
    $('.box').css('pointer-events', 'none');   // 让被覆盖元素无法响应click
    setTimeout(function() {
        $('.box').css('pointer-events', 'auto');   // 恢复被覆盖元素
    },300);
})

使用fastclick库

使用fastclick库后,所有点击事件都使用click,没有350ms延时,也没有样式穿透问题

<script type='application/javascript' src='/path/to/fastclick.js'></script>
...
if ('addEventListener' in document) {
	document.addEventListener('DOMContentLoaded', function() {
		FastClick.attach(document.body);
	}, false);
}

针对安卓的,android:clickable=“true”

在上层的布局中增加android:clickable="true"的属性,这样下层的事件就不会被触发了

详情


移动端适配

viewport适配

//获取meta节点
var metaNode = document.querySelector('meta[name=viewport]');
 
//定义设计稿宽度为375
var designWidth = 375;
 
//计算当前屏幕的宽度与设计稿比例
var scale = document.documentElement.clientWidth/designWidth;
 
//通过设置meta元素中content的initial-scale值达到移动端适配
meta.content="initial-scale="+scale+",minimum-scale="+scale+",maximum-scale="+scale+",user-scalable=no";

借助media实现rem适配

//对屏幕大小划分了html不同的font-size
@media screen and (min-width: 320px) {html{font-size:50px;}}
@media screen and (min-width: 360px) {html{font-size:56.25px;}}
@media screen and (min-width: 375px) {html{font-size:58.59375px;}}
@media screen and (min-width: 400px) {html{font-size:62.5px;}}
@media screen and (min-width: 414px) {html{font-size:64.6875px;}}
@media screen and (min-width: 440px) {html{font-size:68.75px;}}
@media screen and (min-width: 480px) {html{font-size:75px;}}
@media screen and (min-width: 520px) {html{font-size:81.25px;}}
@media screen and (min-width: 560px) {html{font-size:87.5px;}}
@media screen and (min-width: 600px) {html{font-size:93.75px;}}
@media screen and (min-width: 640px) {html{font-size:100px;}}
@media screen and (min-width: 680px) {html{font-size:106.25px;}}
@media screen and (min-width: 720px) {html{font-size:112.5px;}}
@media screen and (min-width: 760px) {html{font-size:118.75px;}}
@media screen and (min-width: 800px) {html{font-size:125px;}}
@media screen and (min-width: 960px) {html{font-size:150px;}}

JS配合修改配合rem适配

var designWidth = 375;  		// 设计稿宽度
var remPx = 100;               // 在屏幕宽度375px,的时候,设置根元素字体大小 100px
var scale = window.innerWidth / designWidth; //计算当前屏幕的宽度与设计稿比例
// 根据屏幕宽度 动态计算根元素的 字体大小
document.documentElement.style.fontSize = scale\*remPx + 'px';

JS动态修改配合CSS预处理器(less)

	// 计算屏幕宽度
var screen = document.documentElement.clientWidth;
	// 计算字体大小,取屏幕宽度的16分之一
var size = screen / 16;
	// 设置根元素字体大小
document.documentElement.style.fontSize = size + 'px';

js获取当前屏幕的宽度,将屏幕宽度的16分之一设置给html的font-size

// 此时设计稿的宽度为375,定义一个less变量等于16分之一的设计稿宽度
@rem: 375/16rem;

JS动态修改配合rem适配

	// 计算屏幕宽度
var screen = document.documentElement.clientWidth;
	// 设置根元素字体大小
document.documentElement.style.fontSize = screen + 'px';

详情

借助插件适配

  • 让不同设备的视口取得最佳CSS像素
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">

  • 安装 postcss,postcss-pxtorem,postcss-loader,postcss-import,postcss-url一系列插件
npm install postcss@8.2.6 --save

npm install postcss-import@14.0.0 --save

npm install postcss-loader@5.0.0 --save

npm install postcss-pxtorem@5.1.1 --save

npm install postcss-url@10.1.1 --save

  • 在项目根目录下添加 .postcssrc.js 文件
module.exports = {
  plugins: {
    'postcss-pxtorem': {
      rootValue: 32, //根目录的字体大小设为32px
      propList: ['\*'], //proplist:是那些属性需要转换成rem,全部
      minPixelValue: 2 //最小转换单位.2px
    }
  }
};

  • 在项目中index.html的头部添加下面这段js代码:
<script>(function () {
    function autoRootFontSize() {
      document.documentElement.style.fontSize = Math.min(screen.width, document.documentElement
        .getBoundingClientRect().width) / 470 \* 32 + 'px';
      // 取screen.width和document.documentElement.getBoundingClientRect().width的最小值;
      // 除以470,乘以32;意思就是相对于470大小的32px;
    }
    window.addEventListener('resize', autoRootFontSize);
    autoRootFontSize();
  })();</script>

注:设计师制作的效果图常为750px,为了方便直接读取UI标好的像素大小,根目录的字体大小就设置为32px;
详情


适配低版本浏览器

Babel

Babel默认只转换新的JavaScript句法(syntax),而不转换新的API.(不转换新的API.的意思是:对极少部分浏览器无法支持的api,转换之后的也无法正常的使用,比如:import,export)

使用

单文件转换
  1. 全局安装babel的命令行工具
cnpm install babel-cli -g

  1. 执行命令,安装babel-preset-env到本地
cnpm install babel-preset-env --save-dev

  1. 在项目根目录下创建 .babelrc 文件,文件中的内容如下
{
 "presets": ["env"]
}

  1. 执行命令将es6转换为es5
文件到文件:babel js文件路径 -o 转译后生成的文件路径

在package.json文件的脚本中配置转义命令
  1. 安装babel-cli命令行工具
cnpm install babel-cli babel-preset-env --save-dev 

  1. 创建 .babelrc 文件,写入以下代码
{
 "presets": ["env"]
}

  1. 在你的 package.json 中添加一个 “scripts” 属性并将 babel 命令放在它的 build 属性中
"scripts": {
     "build": "babel src -d lib"
   },

  1. 终端运行命令开始转译
npm run build

详情


前端项目优化

普通项目优化

页面加载阶段
  1. dns预解析
  2. 使用cdn
  3. 静态资源压缩与合并
  4. 减少http请求
  5. 异步加载defer(页面解析完成后执行代码),async(当前JS解析完成后执行代码)
  6. 服务端渲染ssr
  7. 多使用内存和缓存
页面渲染阶段
  1. css放前面,js放后面
  2. 减少DOM查询,多次使用的保存为变量
  3. 减少DOM操作,统一通过DOM片段操作
  4. 事件函数的防抖和节流
  5. 图片懒加载

Vue项目优化

代码层面优化
  1. Object.freeze(data)(方法可以冻结对象的属性)对于一些查询类的页面,调取接口回来后的数据可 不进行数据劫持
  2. v-if和v-for不要在一起使用。v-if的条件通过函数来处理
  3. v-for中加上key,对于虚拟dom树查找提高性能
  4. computed和watch注意区分使用场景。前者是有缓存的。后者是监听到数据变化后的回调无缓存
  5. created中发起请求,mounted钩子中代表页面dom加载完成可以进行dom操作
  6. 长列表性能优化,只渲染可视区域的列表
  7. 长表格性能优化,通过canvers来绘制表格
  8. 合理使用$nextTick去操作dom,适用于更新了数据马上就要操作dom的场景
  9. 操作dom不要使用js原生的方式来操作。用vue提供的三种方式来操作 比如,ref、自定义指令el、事件中的话用e.target获取dom
  10. 尽量不要在前端进行大量的数据处理
  11. 合理使用keep-alive来缓存页面数据,跳过created,mounted钩子,他有自己特定的钩子activted等
  12. 路由懒加载通过import配合箭头函数,还有其他的方式require
  13. 组件懒加载,异步加载
  14. 尽量少用float,可以用flex布局
  15. 频繁切换的使用v-show,不频繁的使用v-if
  16. 不要在模板中写过多的样式
  17. 服务端渲染ssr,优化seo,与首屏白屏问题
  18. 通过addEventListenr添加的事件,需要自行销毁
  19. 把组件中的css提取成单独的文件
  20. 少使用闭包与递归,递归可做尾递归的优化
  21. 使用字体图标或者svg来代替传统的Png等格式的图片
  22. 在Js中避免“嵌套循环”和“死循环”
webpack优化
  1. 去除无用代码treeShaking
  2. babel编译es6到es5的时候,会有多余代码产生
  3. 减小app.js的体积,提取公共代码
  4. 减少vendor.js的体积,通过按需引入第三方库,或者有些资源可以通过script标签引入
  5. 代码切割,有一些组件没必要都打包到一起
  6. 使用chunck
  7. 使用SouceMap,来还原线上代码,更方便的去定位线上问题
  8. 构建结果,通过可视化插件,进行分析
  9. webpack对图片进行压缩等处理
  10. 图片可以使用webp,优雅降级处理
  11. 编译优化
  12. 模板预编译,使用vue-template-loader,把模板编译成渲染函数
  13. thread-loader多线程打包
// webpack.base.js
 
{
        test: /\.js$/,
        use: [
          'thread-loader',
          'babel-loader'
        ],
      }

WEB层面
  1. 浏览器缓存的使用
  2. 开启gzip压缩
  3. CDN的使用,减少路由转发的次数,就近访问资源
  4. 使用chrome的性能分析工具,查找性能瓶颈
  5. dns预解析
  6. 静态资源的压缩与合并
  7. 减少https请求
  8. 异步加载defer,async
  9. 静态资源和服务不要放在同一台机器上。多个域名去并行加载解析

安全层面

XSS跨站请求攻击

XSS 前端替换关键字,建议后端也替换,如<>的替换,避免脚本的执行

XSRF跨站请求头伪造

XSRF增加验证流程,比如输入密码,指纹,短信验证码,人脸识别等

详情


模块化

概念

  1. 有组织的将一个大文件拆分成多个独立并相互依赖的小文件
  2. 模块内部有许多私有属性,只向外暴露一部分公开的接口

模块化方法

  1. AMD
  2. CMD
  3. CommonJS(Node JS中采用该规范)通过module.exports暴露模块,通过require引入模块
    1. 代码运行在模块作用域,不会污染全局
    2. 加载模块顺序是按照词法解析顺序来加载
    3. 加载模块是同步的
    4. 单例加载:模块会被缓存起来,再次运行直接加载,除非手动清除
    5. 加载模块得到的结果是深拷贝
  4. ES6模块export暴露模块,import … from … 引入模块
    1. 尽量静态化,编译时就确定模块的依赖关系
    2. 加载模块的值是引用,所以在全局只有一份
    3. 加载模块也是异步的

小程序生命周期

应用的生命周期,在app.js中进行调用

生命周期说明

img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

需要这份系统化资料的朋友,可以戳这里获取

就近访问资源
4. 使用chrome的性能分析工具,查找性能瓶颈
5. dns预解析
6. 静态资源的压缩与合并
7. 减少https请求
8. 异步加载defer,async
9. 静态资源和服务不要放在同一台机器上。多个域名去并行加载解析

安全层面

XSS跨站请求攻击

XSS 前端替换关键字,建议后端也替换,如<>的替换,避免脚本的执行

XSRF跨站请求头伪造

XSRF增加验证流程,比如输入密码,指纹,短信验证码,人脸识别等

详情


模块化

概念

  1. 有组织的将一个大文件拆分成多个独立并相互依赖的小文件
  2. 模块内部有许多私有属性,只向外暴露一部分公开的接口

模块化方法

  1. AMD
  2. CMD
  3. CommonJS(Node JS中采用该规范)通过module.exports暴露模块,通过require引入模块
    1. 代码运行在模块作用域,不会污染全局
    2. 加载模块顺序是按照词法解析顺序来加载
    3. 加载模块是同步的
    4. 单例加载:模块会被缓存起来,再次运行直接加载,除非手动清除
    5. 加载模块得到的结果是深拷贝
  4. ES6模块export暴露模块,import … from … 引入模块
    1. 尽量静态化,编译时就确定模块的依赖关系
    2. 加载模块的值是引用,所以在全局只有一份
    3. 加载模块也是异步的

小程序生命周期

应用的生命周期,在app.js中进行调用

生命周期说明

[外链图片转存中…(img-idRqrQuE-1714496011745)]
[外链图片转存中…(img-cx9qv8JU-1714496011745)]
[外链图片转存中…(img-1QdnqZXA-1714496011746)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

需要这份系统化资料的朋友,可以戳这里获取

  • 7
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值