H5
h5新特性
- 拖拽 释放 drap drop
- 自定义属性 data-id 通过获取dom获取
- 特殊的语义化标签
- 音频 视频标签
- 画布
- Geolocation 定位技术
- websocket 实时通信技术
- webworker 多线程技术 重要
- 本地存储 localStorage 和 sessionStorage
h5 Input元素 type有哪些属性值
button:定义可点击按钮
checkbox:定义复选框
color:定义拾色器
date:定义日期
month:定义月
time:定义时间
email、file、hidden、image、nuber、password 等
行内与块级元素
行内元素的宽高由内容决定,默认横向排布
块级元素独立占一行,默认继承父级的宽度
网页的三层结构
结构层、表示层、行为层
结构层由 html 创建、表示层由css创建、行为层由js控制
视频、音频标签的使用
视频标签:audio src属性为路径、controls提供播放控件、autoplay自动播放 、loop循环播放
音频标签:video src属性为路径、controls提供播放控件 标签内插入的内容为浏览器不支持时的提示信息
CSS3
css3新特性
- 文字阴影
- 圆角边框,边框阴影 ※
- 新盒子模型 ※
- 背景
- 渐变
- 自定义动画
- 媒体查询布局 ※
- 2d/3d转换
- 弹性布局 ※
- 字体图标
有内到外(字体图标,文字阴影,背景,渐变,圆角边框,边框阴影,新盒模型,2d/3d转换,弹性布局,媒体查询布局)
如何快速让一个盒子居中
- 定位:子绝父相,设置top50% left50% 再设置自身长和宽的-50%marginTop和left
- margin:auto
- 父级盒子设置 display:table-cell
- 父级盒子设置 display:flex
- 直接计算margin 不使用定位
- transform:子绝父相,设置top50% left50% 再设置transform: translate(-50%, -50%)
内边距和外边距有什么区别
他们的作用对象不同,padding针对于自身,margin作用域外部对象
vw和百分比有什么区别
vw为屏幕可视区域的宽度,与父节点的宽度无关
百分比继承自父节点的宽度
双飞翼布局
- 父级盒子设置左右padding 子级两个盒子的宽度等于padding的值 再定位到两边 中间盒子自适应
- 使用flex布局,父级盒子设置display:flex 左右盒子设置固定宽高,中间盒子设置flex:1
盒子模型
- 标准盒子模型:width为内容的区域的长度 height为内容区域的高度 box-sizing:content-box
- 怪异盒子模型:width为内容宽度+border+padding height同理 box-sizing:border-box
选择器权重
!important > 行内样式 > id选择器 > 类选择器 > 标签选择器 >通配符选择器 > 继承属性 >浏览器默认属性
css哪些属性可以继承、哪些不可以
能继承的: 文字属性、文本属性、元素可见性
不能继承的:大部分都不能继承
css单位中px、em、rem的区别
px:逻辑像素、em相对当前对象的文本字体长度、rem相对于根元素字体的长度
display:none 和 visibility:hidden的区别
前者是直接删除了元素 后者 是隐藏了元素 并且依然占位
position的值有哪些
- static静态定位,top、right、bottom、left属性不生效
- absolute绝对定位,找到最近的一级带有带定位的父级元素进行位置移动如果找不到,那么相对于浏览器窗口进行定位
- relative 相对定位,参照物元素偏移之前的位置
- fixed 参照浏览器窗口定位
为什么要清除浮动,如何清除
- 子元素的浮动会导致父级元素无法被正常撑开
- 与浮动元素同级的非浮动元素会跟随其后
清除浮动
- 给子元素设置clear:both
- 给父元素添加clearfix
- 使用伪元素
- 给父元素设置overflow:hidden 或 overflow:auto
简述 rem布局
rem布局是通过动态计算html的font-size 将根元素的px换算成rem
根据页面的宽度除以一个系数,把算出的这个值赋给 html 的 font-size 属性,rem换算值是根据 psd 设计图的宽度/系数的 rem 系数
var width=html.getBoundingClientRect().width;
html.style.fontSize=width/16+'px'; // html 的 font-size = 20px
简述flex布局
Flex 布局,即弹性盒布局
容器的属性
- display: flex 容器设置为flex布局
- flex-direction 定义主轴方向,默认是x轴
- justify-content 定义主轴上的对其方式
- align-items 定义交叉轴的对其方式
- flex-wrap 定义在主轴方向上是否换行
子项属性
- order 定义项目的排列顺序,数值越小,排列越靠前。默认值为 0
- flex-grow 定义项目的放大比例
- flex-shrink 定义项目的缩小比例
- flex
flex-grow
、flex-shrink
和flex-basis
的缩写。例如,flex: 1 1 200px
表示项目放大比例为 1,缩小比例为 1,初始大小为 200 像素。
::before 和::after 中双冒号和单冒号有什么区别、作用?
单冒号(:)用于 CSS3 的伪类 双冒号(::)用于 CSS3 的伪元素
伪类 用于向某些选择器添加特殊的效果
伪元素 用于创建一些不在文档树中的元素,并为其添加样式。
css3新增的伪类,以及伪元素?
新增伪类
p:first-of-type 选择属于其父元素的首个元素
p:last-of-type 选择属于其父元素的最后元素
p:nth-child(n) 选择属于其父元素的第 n 个子元素并且必须是元素
p:nth-last-child(n) 选择属于其父元素的倒数第 n 个子元素并且必须是元素
p:nth-of-type(n) 选择属于其父元素第 n 个元素
p:nth-last-of-type(n) 选择属于其父元素倒数第 n 个元素
p:last-child 选择属于其父元素最后一个子元素的并且必须是元素
新增伪元素
::first-letter 将样式添加到文本的首字母
::first-line 将样式添加到文本的首行
::before 在某元素之前插入某些内容
::after 在某元素之后插入某些内容
什么是渐进增强和优雅降级?它们有什么不同?
优先考虑低版本浏览器则为渐进增强,优先考虑低版本浏览器就为优雅降级
“优雅降级”观点认为应该针对那些最高级、最完善的浏览器来设计网站
“渐进增强”观点则认为应关注于内容本身
iframe是什么?有哪些优点?
iframe是html中的一个标签,用于再html文档中嵌入另一个html文档,被嵌入的文档和父级文档互相独立,拥有自己的dom和脚本
优点:
- 模块复用
- 隔离性,两个文档独立加载
- 渐进式加载,由于隔离性,可以提高整体效率,例如新闻网站中,文章主体部分和实时评论部分可以分别加载
如何让css支持小于12px的字体
针对谷歌浏览器内核,加 webkit 前缀,用 transform:scale()这个属性进行缩放!
Js基础
js基本数据类型有哪些?引用类型有哪些?null 和 undefined的区别?
基本数据类型:Number、String、Boolean、null、undefined、bigint、symbol
引用数据类型:Function、Object、Array、Error等...
区别:undefined表示变量声明但未初始化的值,null是一个空指针
js中能访问哪些对象
原生对象:Object、Function、Array、String、Date、Boolean、Number、RegExp、Error 内置对象:Global、Math 全局对象:window
js数组有哪些方法
遍历方法,不会影响原数组的方法
方法名 | 格式 | 备注 | 返回值 |
---|---|---|---|
forEach | arr.forEach((value,index)=>{}) | 遍历 | 无 |
map | arr.map((value,index)=>{return 条件}) | 映射 | 新数组 |
filter | arr.filter((value,index)=>{return 条件}) | 过滤 | 新数组 |
find | arr.find((value,index)=>{return 条件}) | 正序查找 | 符合的元素构成的数组 |
findIndex | arr.findIndex((value,index)=>{return 条件}) | 正序查找索引 | 符合条件的索引 |
reduce | arr.reduce((sum,value,index)=>{return 下一轮sum的初始值},0) | 运算迭代 | 最后的运算结果 |
every | arr.every((value,index)=>{return 条件}) | 测试所有 | 所有条件满足返回true否则false |
some | arr.some((value,index)=>{return 条件}) | 测试所有 | 只要有一个条件满足就返回true |
操作方法,不会修改原数组,会返回一个新的数组
方法名 | 格式 | 备注 | 返回值 |
---|---|---|---|
slice | arr.slice(起始,个数) | 从起始位置开始,截取n个元素 | 截取后的数组 |
jion | arr.jion(符号) | 以某个符号把数组拼接成字符串 | 字符串 |
flat | arr.flat() | 深度遍历数组,并将其完全展开 | 展开后的数组 |
indexOf | arr.indexOf(值) | 正序查找值 | 下标,没找到为-1 |
lastIndexOf | arr.lastIndexOf(值) | 倒序查找值 | 下标,没找到为-1 |
concat | arr.concat(arr1,arr2...) | 拼接数组 | 拼接后的数组 |
includes | arr.includes(值) | 数组中是否包含某个元素 | 是则返回true否则false |
修改方法,会直接对原数组进行修改
方法名 | 格式 | 备注 | 返回值 |
---|---|---|---|
push | arr.push(值) | 末尾插入 | 新数组长度 |
pop | arr.pop() | 末尾删除 | 删除的元素 |
unshift | arr.unshift(值) | 头部插入 | 新数组长度 |
shift | arr.shift(值) | 头部删除 | 删除的元素 |
reverse | arr.reverse | 数组倒序排列 | 倒序后的数组 |
splice | arr.splice(起始,个数) | 从起始位置删除n个数元素 | 返回被删除的数组 |
sort | arr.sort((a,b) =>{return语句}) | 数组排序 | 排序后的数组 |
js数学对象Math有哪些方法
方法 | 功能 |
---|---|
Math.floor(9.999) == 9 | 向下取整 |
Math.ceil(9.001) == 10 | 向上取整 |
Math.round(3.14) == 3 | 四舍五入 |
Math.random() | 生成一个0到1不包括1的随机数 |
Math.max(...arr) | 找一组数的最大值 |
Math.min(...arr) | 找一组数的最小值 |
Math.abs() | 绝对值 |
Math.sqrt(81) == 9 | 开根号 |
Math.pow(2,10) == 1024 | 基数,次方数 |
js字符串有哪些方法
js的字符串方法本质是String包装对象的方法合集,都不会修改原字符串,会返回一个新的值
方法 | 描述 |
---|---|
charAt() | 返回在指定位置的字符。 |
charCodeAt() | 返回在指定的位置的字符的 Unicode 编码。 |
concat() | 连接两个或更多字符串,并返回新的字符串。 |
endsWith() | 判断当前字符串是否是以指定的子字符串结尾的(区分大小写)。 |
fromCharCode() | 将 Unicode 编码转为字符。 |
indexOf() | 返回某个指定的字符串值在字符串中首次出现的位置。 |
includes() | 查找字符串中是否包含指定的子字符串。 |
lastIndexOf() | 从后向前搜索字符串,并从起始位置(0)开始计算返回字符串最后出现的位置。 |
match() | 查找找到一个或多个正则表达式的匹配。 |
repeat() | 复制字符串指定次数,并将它们连接在一起返回。 |
replace() | 在字符串中查找匹配的子串,并替换与正则表达式匹配的子串。 |
replaceAll() | 在字符串中查找匹配的子串,并替换与正则表达式匹配的所有子串。 |
search() | 查找与正则表达式相匹配的值。 |
slice() | 提取字符串的片断,并在新的字符串中返回被提取的部分。 |
split() | 把字符串分割为字符串数组。 |
startsWith() | 查看字符串是否以指定的子字符串开头。 |
substr() | 从起始索引号提取字符串中指定数目的字符。 |
substring() | 提取字符串中两个指定的索引号之间的字符。 |
toLowerCase() | 把字符串转换为小写。 |
toUpperCase() | 把字符串转换为大写。 |
trim() | 去除字符串两边的空白。 |
toLocaleLowerCase() | 根据本地主机的语言环境把字符串转换为小写。 |
toLocaleUpperCase() | 根据本地主机的语言环境把字符串转换为大写。 |
valueOf() | 返回某个字符串对象的原始值。 |
toString() | 返回一个字符串。 |
如何判断JavaScript的数据类型?
- 使用 Object.prototype.toString.call().slice(8, -1) 能够精准的获取类型信息
- typeof 操作符 用于判断除了 null 以外的基本数据类型
- instanceof 操作符 用于判断对象类型
- Array.isArray:专门用来判断是否是数组
简述创建函数的几种方式
- 函数声明 function sum1
- 函数表达式 var sum2 = function ()
- 函数对象 var sum3 = new Function("num1","num2","return num1+num2")
js创建对象的几种方式
- 简单对象的创建 使用对象字面量的方式 {}
- 构造函数的方式 ,使用
this
关键字来定义对象的属性和方法。 - 工厂函数创建 工厂函数返回一个对象,用工厂函数来接受对象的属性
- ES6 class的方式 使用 new关键字创建对象实例
请指出 JavaScript 宿主对象和原生对象的区别?
原生对象:js语言本身所定义的对象类型
内置对象:内置对象是原生对象的一部分,可以直接再代码中使用无需引入 如 Array、Object、Function、RegExp、Math等
宿主对象:js所运行的环境中的对象
浏览器:window、document
node.js:global、require
js常用的内置对象有哪些?
- object对象
- Array对象
- String对象
- Math对象
- Date对象
- Function对象
- RegExp对象
- Error对象
- Arguments对象,在函数中使用,比较特殊 不是全局使用的
== 和 === 的区别
==存在隐式转化,只要值相等就返回true
===必须值和类型都相等
js中什么情况下会返回undefined值?
- 声明了对象但未赋值,后访问了它
- 访问对象中不存在的属性
- 访问函数的参数,未被传值时
- 访问任何被设置为undefined值的变量
- 没有定义return的函数隐式返回undefined
- 函数return后没有接任何内容的时候
如何区分数组和对象
- Object.prototype.toString.call()方法
- instanceof方法
- Array.isArray()
- constructor会指向创建该对象的构造函数,因此也可以使用
由于用字面量创建的数组或对象,在操作的时候会先转化成对应的包装对象
包装对象通过 _ _ proto_ _ 找到其原型对象,最后通过原型对象上的constructor找到其构造函数
function isArrayOrObject(value) {
if (value.constructor === Array) {
console.log('这是一个数组');
} else if (value.constructor === Object) {
console.log('这是一个对象');
} else {
console.log('既不是数组也不是对象');
}
}
多维数组降维的几种方法
- falt 使用 Infinity 作为深度,展开任意深度的嵌套数组 arr3.flat(Infinity);
- 数组字符化后再使用split方法
如何判断两个对象相等
- 浅比较:使用oject.is() 判断两个对象时否使用同一个引用地址
- 深比较:对于复杂的对象,需要递归地比较对象的属性和子属性、或者第三方的库如 Lodash的isEqual 只要内容相同就判定为相等
列举三种强制类型转换和两种隐式类型转换?
强制转化
- 字符串 toString() String()
- 数字 Numer() parseInt() parseFloat()
- 布尔 Boolean()
隐式
-
拼接字符串
-
字符串运算 - * / % ==
let str = '18' console.log(typeof (str / 3)) //number
-
==判断
js中如何获取当前日期的月份
使用new Date() 获取当前的日期
js中有哪些伪数组
伪数组拥有length属性,可以通过索引访问元素,但没有真正的数组方法
常见的伪数组有
- arguments
- dom操作返回的对象nodelist
- 手动创建
const arrayLikeObject = {
0: 'a',
1: 'b',
2: 'c',
length: 3
};
遍历对象的属性
Object,keys() fon in 循环
如何使用原生js给一个按钮绑定多个onClick事件?
获取dom对象之后,使用 addEventListene 添加多个监听事件
var btn=document.getElementById(‘btn’); //事件监听 绑定多个事件
var btn4 = document.getElementById("btn4");
btn4.addEventListener("click",hello1);
btn4.addEventListener("click",hello2);
function hello1(){ alert("hello 1"); }
function hello2(){ alert("hello 2"); }
JavaScript 中的作用域、预解析与变量声明提升?
作用域:能访问到变量或常量的代码区域
- 全局作用域
- 函数级作用域
- let和const的块级作用域
预解析:
- 变量声明提升:使用
var
声明的变量会被提升到其所在作用域的顶部。这意味着可以在变量声明之前使用变量,但是此时变量的值是undefined
。let
和const
不存在这个过程。 - 函数声明提升:函数声明会被提升到其所在作用域的顶部,这意味着可以在函数声明之前调用函数
什么是作用域链
- 作用域链是在代码执行过程中,通过将当前执行上下文的变量对象与上层执行上下文的变量对象链接起来形成的。
- 例如,当在函数内部查找一个变量时,首先会在函数自身的变量对象中查找,如果没有找到,就会沿着作用域链向上一级执行上下文的变量对象中查找,依次类推,直到全局执行上下文的变量对象。如果在整个作用域链中都没有找到该变量,就会导致 “ReferenceError: 变量未定义” 的错误。
Js高级
typeof 和 instanceof 区别
typeof
可以检测基本数据类型和函数等,但对于object
类型的区分不够精细,无法准确区分不同类型的对象(除了函数)。instanceof
主要用于检测对象是否是特定构造函数的实例,可以更准确地判断对象的类型,但不能用于检测基本数据类型。
-
对
null
和undefined
的处理:
typeof null
返回"object"
,这是不准确的结果。typeof undefined
返回"undefined"
。instanceof
对于null
和undefined
都返回false
。
什么是闭包?如何形成?有什么优缺点?
闭包:函数能够访问并操作其外部作用域变量的一种机制,即使在函数执行完后,外部作用域变量也不会被销毁
形成条件:函数嵌套返回+内部函数引用了外部好书的变量
优点:保护数据
缺点:内存占用、代码阅读体验差、性能开销大
应用场景:模拟私有变量、防抖和节流
js为什么要有闭包?哪些场景用到闭包
1.私有化变量、避免变量被污染 2.保存变量,常驻内存 应用场景:防抖和节流,库的封装(保证数据的私有性)
什么是内存泄漏
在 JavaScript 中,内存泄漏是指由于某些错误或不当的编程方式,导致程序无法回收不再使用的内存空间,从而随着时间的推移占用越来越多的内存。
哪些操作可以导致内存泄漏?
- 意外的全局变量
- 闭包使用不当
- 未清除的事件监听
对prototype的理解
原型的基本概念
- 每个函数都有一个
prototype
(原型)属性,这个属性是一个对象,它包含了可以被该函数的实例共享的属性和方法。 - 当创建一个函数的实例(对象)时,该实例会继承函数的
prototype
对象上的属性和方法。
function Person() {}
Person.prototype.name = "John";
Person.prototype.age = 30;
Person.prototype.sayHello = function() {
console.log("Hello!");
};
let person1 = new Person();
let person2 = new Person();
console.log(person1.name); // 输出 "John"
console.log(person2.name); // 也输出 "John",因为它们共享了原型上的属性
person1.sayHello(); // 输出 "Hello!",可以调用原型上的方法
__proto__
属性
每个对象(除了 null
)都有一个内置的 __proto__
属性,它指向该对象的原型。
在上面的例子中,person1
和 person2
都有一个 __proto__
属性,它指向 Person.prototype
。
通过 person1.__proto__ === Person.prototype
可以验证这一点。
原型链
当访问一个对象的属性或方法时,如果该对象本身没有这个属性或方法,JavaScript 会沿着原型链向上查找,直到找到该属性或方法,或者到达原型链的顶端(即 Object.prototype
,它是所有对象的最终原型)。
例如,如果 Person.prototype
也没有某个属性或方法,JavaScript 会继续沿着 Person.prototype.__proto__
(通常是 Object.prototype
)继续查找。
对this的理解
this存在在任何地方,不同环境代表的含义不同,取决于调用时的环境
事件处理函数中的this,是触发事件的对象
普通函数中的this 指向window
箭头函数中的this,指向其执行级上下文中父级的this
有call 和 apply调用函数的时候,this会指向特定的对象
构造函数中的this 指向new关键字创建的实例
数组中forEach和map的区别
forEach用来做遍历的 map用来做映射的
for in 和 for of 的区别
for in 用来遍历对象的key 而 for of用来遍历对象的值
call、apply、bind的作用
call是函数的方法,用来改变函数内this的指向,可以直接调用函数 fun.call(),第一个参数是需要改变的指向,后续为正常函数的参数
apply和call一样也是函数的方法,只是正常的参数变为了一个数组
bind和call方法唯一的区别就是不会主动调用,其余的一模一样,会返回一个新函数
Event Loop(事件循环)的概念
首先 js是单线程语言,就意味着任何时候只能执行一个任务,为了实现异步操作
js引擎引入事件循环的概念
- 首先,JavaScript 引擎从主线程开始执行同步任务,直到同步任务全部执行完毕。
- 当同步任务执行过程中遇到异步任务时,例如调用
setTimeout
,JavaScript 引擎会将setTimeout
的回调函数放入定时器对应的任务队列中;当网络请求完成时,相关的回调函数会放入事件触发对应的任务队列中。 - 一旦主线程上的同步任务执行完毕,JavaScript 引擎就会从任务队列中取出最先进入队列的异步任务的回调函数,并将其放入主线程中执行。这个过程不断重复,即主线程不断地从任务队列中取出任务并执行,这就是事件循环。
简言之,先执行同步,再根据顺序执行异步的过程就是事件循环
什么是防抖和节流,怎么实现,有哪些场景
防抖:单位时间内,频繁触发事件,只执行最后一次 实现:利用定时器,每次触发之前清除之前的定时器 场景:搜索框搜素输入,视频进度自动保存
节流:单位时间内,频繁触发事件,只执行一次 实现:利用定时器,等待定时器执行完成后,才开启新的定时器 场景:高频事件:快速点击、鼠标滑动、滚动事件
开发中一般用lodash库中封装好的防抖和节流
js的new操作符做了什么事情(空对象,p-p,this,constr执行)
- 创建了一个新的对象
- 让这个空对象的__proto__指向其构造函数的原型对象
- 将新对象作为构造函数的上下文,(改变this指向)
- 对构造函数返回值的判断
宏任务与微任务
涉及到js的事件循环机制
首先:事件循环的最小单位是一个完整的宏任务,而一个完整的宏任务执行过程中,可能会产生新的宏任务或新的微任务,同样一个微任务的执行过程中也可能产生新的宏任务或新的微任务
当一个宏任务执行完毕后 浏览器的渲染进程 会检查队列中是否有微任务,如果有则会吧所有的微任务执行掉 再触发渲染 若没有则直接渲染
以js代码为视角 常见的宏任务有:
- js脚本代码
- 定时器相关
- 请求相关
- dom事件相关
常见的微任务有:
primise的then、catch、finally的回调相关
ES6
es6有哪些新特性
- 变量声明:var 变为 let 和 const
- 模板字符串
- 解构赋值
- 展开运算符 ...
- 箭头函数
- 函数默认值
- Promise async await 异步处理
- 对象class用法
- set数据结构
ES6 的继承和 ES5 的继承有什么区别
ES6 的继承和 ES5 的继承的区别
ES5 的继承是通过原型或者是构造函数机制来实现 ES6 用过 class 关键字定义类,里面有构造方法,类之间通过 extends 关键字实现,子类必须 在 constructor 方法中调用 super 方法
var、let、const
- var可以重复声明,可以先使用后声明,存在变量提升
- let不可以重复声明,不可以先使用后声明,作用于块级作用域
- const必须声明后就复制,其余的和let一样
module、export、import 有什么作用
一、module
(模块系统)
在 ES6 之前,JavaScript 没有原生的模块系统,开发中通常使用一些库(如 RequireJS、SeaJS 等)来实现模块功能。ES6 引入了原生的模块系统,使用 module
关键字来标识一个模块。
在浏览器环境中,module
默认为严格模式运行,这意味着变量必须显式声明,不能使用 with
语句等。在 Node.js 中,module
是一个对象,代表当前模块,它有一些重要的属性和方法用于模块间的交互和管理。
二、export
(导出)
export
用于将模块中的变量、函数、类等成员导出,使其可以在其他模块中使用。
三、import
(导入)
import
用于从其他模块中导入已导出的成员,使当前模块能够使用其他模块中定义的功能。
箭头函数和普通函数的区别
- this指向:箭头函数指向其执行及上下文中的this,普通函数指向window
- arguments:箭头函数没有自己的arguments对象,可以通过...arg来获取参数数组,普通函数有
- 构造函数:箭头函数不能使用new关键字,因为他没有自己的this,普通函数可以使用
模板字符串
最关键的为可以嵌入表达式 和 无需使用\n换行
介绍下 Set、Map 的区别
- set是一个存储值的集合,类似于数组,但成员的值都是唯一的,没有重复的值。它使用哈希表(Hash Table)来实现,内部通过对元素进行哈希运算来快速判断元素是否存在,具有较快的查找和插入 / 删除性能。
- 是一个键值对的集合,其中键和值都可以是任何类型的数据,并且键是唯一的(类似于对象的属性名,但可以是任意类型,而对象的属性名只能是字符串或 Symbol)
Promise 有几种状态,什么时候会进入 catch?
Promise 有几种状态
三个状态:pending等待、fulfilled成功、reject失败
两个过程:padding -> fulfilled、padding -> rejected
Promise 什么时候会进入 catch
当 pending 为 rejected 时,会进入 catch
什么是 class,class 的作用
ES6 的 class 可以看作是一个语法糖,它的绝大部分功能 ES5 都可以做到,新的 class 写法只 是让对象原型的写法更加清晰、更像面向对象编程的语法
class类中方法this的指向
如果直接通过实例调用 this指向为该方法类的实例对象
class MyClass {
constructor() {
this.value = 10;
}
printValue() {
console.log(this.value);
}
}
const obj = new MyClass();
obj.printValue(); // 输出 10,这里 this 指向 obj
类中使用定义,以及function赋值的属性 默认都在局部开启了严格模式,如果是通过赋值语句后直接调用 则this指向为undefined,但箭头函数没有自己的this 其内部的this指向为该方法的实例对象
class MyClass {
constructor() {
this.value = 10;
}
printValue () {
console.log(this);
}
printValue2 = function () {
console.log(this)
}
printValue3 = () => {
console.log(this)
}
}
const obj = new MyClass();
const func = obj.printValue;
const func2 = obj.printValue2;
const func3 = obj.printValue3;
func(); // 输出 undefined 因为 this 不再指向 obj
func2() // 输出 undefined 因为 this 不再指向 obj
func3() // 输出 obj实例对象 因为箭头函数的指向为其执行及上下文中的this
class类中写赋值语句是什么意思
直接给类的实例对象追加属性
如何获取多个 Promise 最后整体结果
使用Promise.all()方法
其接受一个由promis实例构成的数组
当所有promise执行成功的时候 将结果按照数组的顺序依次返回
只要有promise执行失败,就会立刻返回失败的promise信息
Promise.all的使用场景
- 在前端开发中,经常会同时发起多个异步请求(如从不同的 API 端点获取数据),使用
Promise.all
可以在所有请求都完成后一次性处理结果,而不是一个一个地处理。 - 并行执行多个独立任务,可以将多个独立的、不相互依赖的任务使用
Promise.all
并行执行,当所有任务都完成后再进行后续的操作,提高程序的执行效率。
ES6 如何转化为 ES5,为什么要转化
Babel 是什么
babel 是一个 ES6 转码器,可以将 ES6 代码转为 ES5 代码,以便兼容那些还没支持 ES6 的 平台
Promise 中 reject 和 catch 处理上有什么区别(高薪常问)
reject 是用来抛出异常,catch 是用来处理异常
reject
是在 Promise 构造函数内部使用,用于明确地拒绝 Promise。catch
是在 Promise 链的.then()
方法后面使用,用于捕获 Promise 链中后续操作(包括在.then()
方法中执行的异步操作)出现的错误。
promise中代码的执行顺序
const Promise = new Promise((resolve, reject) => {
console.log(2); resolve(); console.log(333); })
Promise.then(() => {
console.log(666); })
console.log(888);
Promise 新建后立即执行,所以会先输出 2,333,而 Promise.then()内部的代码在 当 次 事件循环的 结尾 立刻执行 ,所以会继续输出 888,最后输出 666
Promise和下一帧哪个快一些?
取决于promise内部的方法执行的时间,一般来说若promise内部都是同步方法,则会比下一帧快,但若内部是一个请求或者是一个超过一帧执行时间的方法,就会比下一帧慢
使用结构赋值,实现两个变量的值的交换
let a = 1;
let b = 2;
[a,b] = [b,a];
WebApi
什么是dom
1、DOM 是 W3C(万维网联盟)的标准
2、DOM 定义了访问 HTML 和 XML 文档的标准
dom的常见操作
1、创建新节点
createDocumentFragment() //创建一个 DOM 片段
createElement() //创建一个具体的元素
createTextnode() //创建一个文本节点
2、添加、移除、替换、插入
appendChild()
removeChild()
replaceChild()
3、查找
getElementsByTagName() //通过标签名称
getElementsByName() //通过元素的 Name 属性的值(IE 容错能力较强, 会得到一个数组,其中包括 id 等于 name 值的)
getElementById() //通过元素 Id,唯一性
浏览器渲染页面的过程
- dom树和css结构体 并行构建
- 构建完成后生成render tree
- 计算布局信息
- ui引擎渲染程用户所见页面
性能优化(比较开放的问题)
- 加载
a. 减少http请求(精灵图、文件的合并)
b. 减小文件的大小(资源压缩,图片、视频、音频、代码的压缩)
c. CDN(引用第三方库,大文件,大图 通过链接来引入)
d. ssr服务端渲染,预渲染
e. 懒加载(按需加载)
f. 减少和后台的交互,前端能计算的值,只需要后台的一些关键值去交互 g. v-if v- show 的使用
h.vue中组件的复用
- 性能
a. 减少dom操作
什么是事件冒泡,它是如何工作的?
事件冒泡是一种事件传播机制,当一个元素上的事件被触发的时候,该事件会从具体的元素向上传播,直至抵达文档的根节点
如何阻止事件冒泡
调用事件实例的 stopProgation()方法 或 ie的 cancleBubble()方法
如何阻止事件默认行为
调用事件实例的 preventDefault()方法 或 ie的 returnValue()方法
event 对象的常见应用
event.target 获取触发事件的具体元素
event.preventDefault() 阻止事件的默认行为
event.stopPropagation() 阻止事件冒泡
获取鼠标位置
event.clientX
和event.clientY
:获取鼠标相对于浏览器窗口可视区域的 X 和 Y 坐标。event.pageX
和event.pageY
:获取鼠标相对于整个文档的 X 和 Y 坐标。
键盘事件处理
event.key
:获取按下的键的名称。event.keyCode
(不推荐使用,逐渐被event.key
取代):获取按下的键的键码。
dom事件流
事件发生后的一些利处理叫dom事件流
- 事件捕获:将事件从最外层的window开始一步一步向子元素传递,捕获事件目标
- 事件冒泡,从事件处理目标开始处理事件,处理完成后依次向父元素传递
事件委托是什么?如何确定源?
利用事件冒泡机制,将事件处理委托给父元素
事件源是event.target对象
JavaScript 中的定时器有哪些?他们的区别及用法是什么?
setTimeout 和 setInterval
延迟执行 和 间歇执行,两个参数 第一个参数是处理函数,第二个参数是时间,时间单位是毫秒
document.write 和 innerHTML 的区别
document.write
:
- 用于向文档中写入 HTML 内容。如果在页面加载完成后调用
document.write
,它会擦除当前页面的所有内容并写入新的内容。
innerHTML
:
- 用于获取或设置一个元素的 HTML 内容。它允许在不擦除整个页面的情况下修改特定元素的内容。
什么是 window 对象?什么是 document 对象?
window 对象
-
概述:
window
对象代表浏览器的一个窗口或标签页,它是全局对象,在浏览器环境中所有的 JavaScript 代码都可以直接访问它。- 可以省略
window
关键字直接调用其属性和方法,例如alert()
实际上是window.alert()
。
-
主要功能和属性:
-
浏览器窗口控制:
window.open()
:打开一个新的浏览器窗口或标签页。window.close()
:关闭当前窗口。
-
定时器:
setTimeout()
:在指定的毫秒数后执行一次函数。setInterval()
:按照指定的时间间隔重复执行函数。
-
对话框:
alert()
:显示一个带有消息和一个 “确定” 按钮的警告框。confirm()
:显示一个带有消息和 “确定”“取消” 按钮的确认框,返回布尔值。prompt()
:显示一个带有消息和输入框的提示框,返回用户输入的内容。
-
窗口位置和大小:
window.innerHeight
和window.innerWidth
:获取当前窗口的高度和宽度(不包括浏览器边框和工具栏)。window.scrollX
和window.scrollY
:获取当前窗口的水平和垂直滚动位置。
-
ajax请求
XMLHTTPRequest ocument 对象
-
-
概述:
document
对象代表整个 HTML 文档,它可以通过window.document
访问,但通常直接使用document
。它提供了对文档内容的访问和操作方法。
-
主要功能和属性:
- 文档结构访问:
document.getElementById()
:通过元素的 ID 获取单个元素。document.getElementsByTagName()
:通过标签名获取一组元素。document.getElementsByClassName()
:通过类名获取一组元素。
- 文档内容修改:
innerHTML
:设置或获取元素的 HTML 内容。textContent
:设置或获取元素的文本内容。
- 创建和插入元素:
document.createElement()
:创建一个新的 HTML 元素。appendChild()
:将一个元素添加到另一个元素的子节点列表中。
- 文档信息获取:
document.title
:获取或设置文档的标题。document.URL
:获取当前文档的 URL。
- 样式操作:
document.styleSheets
:访问文档中的样式表列表。element.style
:直接设置元素的内联样式。
- 文档结构访问:
如何获取url地址中的内容
window.location.search
什么是重绘 Repaint 和重排 (回流 reflow)
重绘:当元素的一部分属性发生改变,如外观、背景、颜色等不会引起布局变化,只需要浏览器 根据元素的新属性重新绘制,使元素呈现新的外观叫做重绘。
重排(回流):当 render 树中的一部分或者全部因为大小边距等问题发生改变而需要 DOM 树重新 计算的过程
**Ajax/**计算机网络相关
什么是Ajax
ajax是指一种创建交互式网页应用的网页 开发技术。沟通客户端与服务器,可以在不必刷新整个浏览器的情况下,与服务器进 行 异步通讯的技术。
Ajax 的原理
通过 XMLHTTPRequest 对象来向服务器发异步请求,从服务器获得数据,然后用 javascript 来操作 DOM 而更新页面。
Ajax 的优点
1、最大的一点是页面无刷新,用户的体验非常好。
2、使用异步方式与服务器通信,具有更加迅速的响应能力。
3、可以把以前一些服务器负担的工作转嫁到客户端,利用客户端闲置的能力来处理, 减 轻服务器和带宽的负担,节约空间和宽带租用成本。并且减轻服务器的负担,Ajax 的原则是“按 需取数据”,可以最大程度的减少冗余请求,和响应对服务器造成的负担。
4、基于标准化的并被广泛支持的技术,不需要下载插件或者小程序。
什么是fetch
fetch
是现代浏览器中用于网络请求的一种 API,它提供了一种更加简洁和强大的方式来进行 HTTP 请求,相比传统的XMLHttpRequest
有一些优势。
常见的 HTTP 状态码
200( OK):请求已成功,请求所希望的响应头或数据体将随此响应返回。
400( Bad Request):请求格式错误。 1) 语义有误,当前请求无法被服务器理解。除非进行 修改,否则客户端不应该重 复提交这个请求;
- 请求参数有误。
404( Not Found):请求失败,请求所希望得到的资源未被在服务器上发现。
500( Internal Server Error):服务器遇到了一个未曾预料的状况,导致了它无法完成对请 求的处理
网络中使用最多的图片格式有哪些
jpg、png、gif
什么是跨域,如何解决
跨域是一种浏览器的保护机制,只允许网页请求同一域名下的服务 同一域名的要求是:协议、域名、端口 都必须保持一只才能请求,有一项不同都会产生跨域问题
解决方法
- 后端设置cors允许跨域
- 前端使用jsonp,script 标签的 src 属性中的链接却可以访问跨域的 js 脚本,利用这个特性,服 务端不在返回 json 格式的数据,而是返回调用某个函数的 js 代码,在 src 中进行了调用,这样就 实现了跨域
- 使用nginx反向代理
get和post
区别
1、Get 使用 URL 或 Cookie 传参。而 Post 将数据放在 body 中
2、Get 的 URL 会有长度上的限制,则 Post 的数据则可以非常大
3、Post 比 Get 安全,因为数据在地址栏上不可见
http协议和https协议的区别
- 加密方式:
- HTTP:数据以明文形式传输,不进行加密,容易被窃听和篡改。
- HTTPS:使用 SSL(Secure Sockets Layer)或 TLS(Transport Layer Security)协议对数据进行加密。在客户端和服务器之间建立加密连接,确保数据在传输过程中的安全性。
- 证书验证:
- HTTPS:服务器需要向证书颁发机构(CA)申请数字证书,客户端在连接时会验证服务器的证书有效性。证书包含服务器的身份信息,确保用户连接到的是真实可靠的服务器,防止中间人攻击。
在浏览器中输入url并回车后都发生了生什么
url:全名统一资源定位符,俗称网址
https:传输协议(http和tcp之间加了一层 tsl 或者 ssl的安全层)
www:服务器
baidu.con:域名
发生了以下四步
- www.baidu.com
- 访问DNS域名系统
- 拿到真实的IP地址(二次访问的时候浏览器会直接读取缓存的IP)
- 建立连接(TCP 的 三次握手)
- 拿数据渲染页面
- 四次挥手断开连接
三次握手和四次挥手
- 客户端向服务端发送请求(等待服务端确认)
- 服务器回复一个指令(代表此时服务器已经做好了准备接受数据)
- 客户端收到服务端的指令后,向服务器发送确认包(表示向服务器转达已经确定了服务器状态) 完成三次握手,客户端开始向武器传送数据,
http请求方式
HTTPRequestMethod 共计 17 种
1、GET 请求指定的页面信息,并返回实体主体。
2、HEAD 类似于 get 请求,只不过返回的响应中没有具体的内容,用于获取报头
3、POST 向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被 包含在请求体中。POST 请求可能会导致新的资源的建立和/或已有资源的修改。
4、PUT 从客户端向服务器传送的数据取代指定的文档的内容。
5、DELETE 请求服务器删除指定的页面。
6、CONNECT HTTP/1.1 协议中预留给能够将连接改为管道方式的代理服务器。
7、OPTIONS 允许客户端查看服务器的性能。
8、TRACE 回显服务器收到的请求,主要用于测试或诊断。
9、PATCH 实体中包含一个表,表中说明与该 URI 所表示的原内容的区别。
10、MOVE 请求服务器将指定的页面移至另一个网络地址。
11、COPY 请求服务器将指定的页面拷贝至另一个网络地址。
12、LINK 请求服务器建立链接关系。
13、UNLINK 断开链接关系。
14、WRAPPED 允许客户端发送经过封装的请求。
15、LOCK 允许用户锁定资源,比如可以再编辑某个资源时将其锁定,以防别人同时对 其进行编辑。
16、MKCOL 允许用户创建资源
17、Extension-mothed 在不改动协议的前提下,可增加另外的方法。
vue相关
**vue的核心是什么
MVVM架构,数据双向绑定,核心是:数据驱动、组件系统
简述你对vue的理解
vue 是mvvm架构的渐近式框架 vue 通过vm这一层虚拟dom来实现视图和数据的双向绑定,使得程序对页面的修改更为简便 vue 的mvvm架构很好的实现了前后端分离 vue 的组件系统,对开发很有好,类似的需求可以通过组件定制化开发进行完成 vue 的指令系统对 页面的操作非常便捷
***简述一下vue的单项数据流
单项数据流是指:数据一般从父组件传递给子组件,子组件没有权利直接修改父组件的数据,子组件想修改父组件的数据 只能请求父组件修改,父组件属性值的更新会自动下行流动到子组件中。
***vue有哪些指令
v-once:相应的dom元素只能被渲染一次,之后不会随着数据的变化而变化 v-html:用来渲染 innerHTML v-text:用来渲染文本,一般不用 直接用 mastache语法 v-pre:停止解析源码,直接渲染源码 v-cloak:在vue解析之后移除的属性 v-bind:控制标签属性的指令,语法糖是直接一个冒号 : v-on:控制事件监听 v-if、v-show:控制dom渲染 v-model:表单双向绑定
vue如何实现自定义指令
使用Vue.directive来注册全局的自定义指令,在组件中,可以使用directives对象进行注册
v-text与{{}}与v-html的区别
v-text和{{}} 都是用来显示网页中的纯文本内容,数据量多的时候用v-text v-html用于innerHTML的渲染
***vue有哪些常见的修饰符 ※
vue有五大类修饰符
- v-model 表单修饰符:v-model.lazy(失去交点的时候赋值)、trim自动过滤空格、number自动转number
- 事件修饰符:stop阻止事件冒泡、prevent阻止事件的默认行为
- 鼠标按键修饰符:left左键点击 right右键点击 middle中键点击
- 键值修饰符:keyCode @keyup.keyCode、
- v-bind修饰符:async对props双向绑定
常用的:
- .stop:阻止事件冒泡
- .native:绑定原生事件
- .once:事件只执行一次
- .self :将事件绑定在自身身上,相当于阻止事件冒泡
- .prevent:阻止默认事件
- .caption:用于事件捕获
- .once:只触发一次
- .keyCode:监听特定键盘按下
- .right:右键
vue的循环中key有什么作用
用于提高更新dom时候的效率,用于提高性能 当有数据需要更新的时候可以是用key来精准的判断一个节点是否需要更新
为什么不能使用index作为key
举个例子,当某个数组的最前方插入一条数据的时候,相较于原来的数组,所有数据的索引值都会发生变化,那么在vue看来,就是所有的数据都需要更新,会引发严重的效率问题
什么是计算属性
计算属性是一个监听过程,当被监听的值发生变化的时候,自动计算需要更新的值 被监听值没有发生变化的时候,计算属性会有缓存不会重复计算,以提高效率
*vuex是什么?怎么使用?在哪种场景下使用
vuex是全局的状态管理模式,用于管理一些全局状态的值,并实现组件数据共享。 state用于声明全局变量、mutation用于修改变量、action用于异步修改、getter用于修饰变量 需要大量的规范化的组件数据共享的场景下使用
使用vuex统一管理状态的好处
- 能够在vuex中集中管理共享数据,易于开发和维护
- 能够高效地实现组件之间的数据共享,提高程序性能
- 存储在vuex中的数据都是响应式的,能够实时的保持数据与页面的同步
vue中路由跳转方式
- router-link 标签跳转,相当于a标签跳转
- this.$router.push() 编程跳转,记录history,可以使用浏览器的前进后退进行跳转
- this.$router.replace() 编程跳转,不记录history,不可以使用浏览器的前进后退进行跳转
vue跨域的解决方式
在项目的config目录下的indxe文件中配置proxy服务器代理设置
简述vue的生命周期 **
- 实例化一个vue对象,此时调用beforeCreate
- 初始化完成后调用 created
- 创建虚拟dom节点,挂载到真实的dom上 在挂载之前调用beforeMount
- 挂载完成后调用mounted
- 监测页面更新,更新前调用beforeUpdate
- 更新完毕后调用updated
- 离开页面销毁前调用beforeDestyroy,完全销毁后调用destroyed
*dom渲染在哪个生命周期阶段内完成
在monted阶段完成
vue的路由的实现
vue路由在不重新请求页面的情况下,更新页面视图
实现方式是:浏览器hash模式 和 浏览器历史记录模式
简述一下路由模式 hash 和 history
hash模式是使用,页面锚点进行页面重定向从而实现路由的
history模式是对页面进行记录,将页面位置、滚动、进度等信息存储起来实现的路由
vue路由传参的两种方式,param和query方式与区别
params 使用 name 和 params 传递参数,用于数据量较多的时候,页面手动刷新会丢失参数,如果需要不丢失需要在路由中配置特殊格式,访问方式 this.router.query
vue数据绑定的几种方式
普通插值表达式 mastache语法或叫双大括号语法,或者使用v-text用于绑定文字输出 v-bind用来绑定标签的属性 v-model用来绑定表单数据
vue如何注册一个全局组件
使用vue.use接受一个common.js 在common.js中使用Vue.component()方式引入全局组件
vue的导航守卫
导航守卫是用于监听路由的跳转过程,并做一定的处理
三种守卫:
全局导航守卫:注册在路由实例中,
路由独享守卫:注册在路由配置的数组中,
组件内守卫:注册在组件中
vue中如何实现动态路由
动态路由是通过url进行传参,可以用params 和 query 两种方式
vue中如何获取一个dom
给dom元素加id,使用document.getElementById()获取 给dom元素加ref属性,使用this.$refs.name获取
vue如何实现一个过滤器
在mastache语法中,使用一个竖杠来调用过滤器,会将竖杠前的值作为参数传递给过滤器 在filters中定义过滤函数
vue中keep—alive的作用
keep-alive是用来做组件缓存的,被keep-alive中的组件状态会被保留 具体实现方法可以用keep-alive标签,或在路由中配置meta属性 缓存的组件被切换的时候 调用activated、deactivated回调函数
如何让组件中的css在当前组件生效
在style前面加上scoped,就不会继承根组件的样式
mvc和mvvm有什么区别
mvc通过控制层,来操作数据和dom mvvm通过vm来取代控制层c,让开发者更专注于数据的处理
vue组件中的data为什么是函数
vue中组件是用来复用的,data中的数据以函数的返回值形式定义,这样每一次复用都会生成新的data,用有自己的作用域,不会互相影响
vue双向绑定的原理
vue2内部使用object.definProperty方法给所有数据加上了getter和setter方法,在数据发生改变的时候,发送给订阅者,从而触发监听回调
vue3使用
Proxy
对象对数据对象进行代理,当访问或修改代理对象的属性时,会触发相应的get
和set
拦截器。
vue中组件怎么传值
父向子传值:通过v-bind绑定属性,在子组件中使用props接受 子向父传值:通过v-on绑定自定义事件,在子组件中使用this.on 注册事件 emit发送事件,这是一个异步传值 更大的数据共享传值:使用vuex 也可以使用sessionStorage和localStorage进行传值
vue中的watch有什么作用,请简述
wacth监听器,是当被监听的数据发生改变的时候,可以将被监听值的新旧值都传入到函数中,从而进行一些特定的操作
计算属性与watch的区别
计算属性调用的时候需要在模板中渲染,修改计算所依赖的元数据 watch在调用时只需要修改元数据 计算属性默认深度观测,wacth默认浅度观测 计算属性不可以用作异步,wacth适合作异步操作
简述一下proxy
proxy是一种代理服务,将访问之前增加一层拦截机制,将访问进行改写和过滤 vue中的proxy定义在config/idnex文件中,也就是代理的配置
React相关
react类组件生命周期函数(旧)
- 初始化阶段
- constructor
- componentWillMount
- render
- componentDidMount 常用,在这个钩子中做一些初始化的事情,如 开启定时器、发请求、订阅消息
- 更新阶段
- props更新 => componentWillReceiveProps=>shouldComponentUpdate=>componentWillUpdate
- state更新=> shouldComponentUpdate=>componentWillUpdate
- 强制手动更新=>componentWillUpdate
- render
- componentDidUpdate
- 卸载阶段 ReactDom.unmountComponentAtNode()调用卸载
- componentWillUnmount 常用,一般用来做一些收尾工作,关闭定时器,取消订阅消息,其三个参数是先前的props和先前的state 以及 新生命周期函数getSnapshotBeforeUpdate 传递的快照值
react类组件生命周期函数(新)
在react16.4以后的版本中使用新的生命周期函数钩子,在旧的钩子函数中,除了componentWillUnmount 所有带will的生命周期钩子 前面只需要加上UNSAFE_ 前缀 就可以继续使用
两个新钩子
getDerivedStateFromProps :
- state的值在任何时候都取决于props的话,使用此函数会很方便
- 必须使用static修饰,会收到两个参数,props 和 state
- 必须要有返回值,null 或者 state都可以
getSnapshotBeforeUpdate (在更新之前获取快照):
1. 会收到两个参数 prevProps 和 prevState
2. 必须要有return值 return值是你希望componentDidMount 收到的快照
新生命周期
- 初始化阶段:由ReactDom.render()触发--初次渲染
- constructor
- getDerivedStateFromProps
- render
- componentDidMount
- 更新阶段: 由组件内部的this.state() 或 父组件重新render触发
- getDerivedStateFromProps
- shouldComponentUpdate
- render
- getSnapshotBeforeUpdate
- 卸载组件 由:ReactDom.unmountComponentAtNode触发
- componentWillUnmont
react 类组件生命新旧周期钩子有什么变化
即将废除 componentWillMount、componentWillReceiveProps、componentWillUpdate 三个钩子
添加了 getDerivedStateFromProps 和 getSnapshotBeforeUpdate 钩子 需要使用static修饰使用 并且必须要有返回值
但新提出的这两个并不常用 常用的依然是componentDidMount 和 componentWillUnmount
react组件通讯
⽗组件向⼦组件通讯: ⽗组件可以向⼦组件通过传 props 的⽅式,向⼦组件进⾏通讯
⼦组件向⽗组件通讯: props+回调的⽅式,⽗组件向⼦组件传递props进⾏通讯,此props为作⽤域为⽗组件⾃身的函 数,⼦组件调⽤该函数,将⼦组件想要传递的信息,作为参数,传递到⽗组件的作⽤域中。
兄弟组件通信: 找到这两个兄弟节点共同的⽗节点,进行状态提升,结合上⾯两种⽅式由⽗节点转发信息进⾏通信
跨层级通信:
- props层层传递回调函数。
- Context 设计⽬的是为了共享那些对于⼀个组件树⽽⾔是“全局”的数据,例如当前认证的⽤户、主题或⾸选语⾔,对于跨越多层的全局数据通过 Context 通信再适合不过。