文章同步在公众号:萌萌哒草头将军,欢迎关注!!!
一面
es6新特性有哪些
新增了let
、const
关键字替换var
声明变量,因为var
会导致变量提升,在申明之前可以访问变量。新增了箭头函数,新增了promise
、sync await
、还有一些高级特性,比如像proxy
。
Promise静态方法有哪些?
any
、 race
、 all
等,(reject
resolve
也是静态方法)。
然后面试官让我说下,all
的用法。我当时说多个Promise
全部执行为才会调用all
。但是面试官让我手写下用法,我给写错了,面试官耐心的引导我写出正确的,正确如下:
Promise.all([new Promise(), new Promise(), new Promise()])
.then(values => console.log(values))
.catch(err => console.log(err))
这时面试官又问我,如果把catch
里的方法。作为then
里的第二个参数,和现在的写法有啥区别吗?
Promise.all([new Promise(), new Promise(), new Promise()])
.then(
values => console.log(values),
err => console.log(err)
)
如果任何一个Promise
被拒绝,会调用then
的第二个参数,而不会触发catch
,因为错误在then
中已经处理了。
普通函数和箭头函数的区别
普通函数内部是有this
指向的,指向调用函数的对象,箭头函数本身是没有的,里面的this
是箭头函数第一个有this
的父作用域里的this
。
例如:
let obj = {}
function fun1 () {
console.log(this)
}
const fun2 = () => console.log(this)
obj.a = fun1 // this指向obj
obj.b = fun2 // this指向window
接着面试官又问了我一个问题,箭头函数的this
是调用的时候确定的还是定义的时候确定的?
我回答调用的时候,调用的时候,对象属于哪个作用域,就是确定为哪个。
实际是定义的时候确定的。
map和set的作用
你知道map和set的作用吗?
我回答了一半,Map
允许使用任意类型的键,包括对象,而对象的键只能是字符串或符号。
其实除此之外,Map
保留键值对的插入顺序,遍历时按插入顺序返回。Map
在增删查操作上有更好的性能,特别是当键不是字符串时。
Set
自动去除重复元素,确保集合内的值唯一,Set
也提供了更高效的值查找、添加和删除操作。
js类定义静态方法
使用static
关键字
js继承类怎么在方法内部调用方法
class A {
a() {
console.log('Method a from class A');
}
}
class B extends A {
b() {
// this.a()
super.a()
}
}
const instance = new B();
instance.b(); // 输出: Method a from class A
ts的泛型
有个加法函数,有两个参数,参数类型和返回值类型始终一致,string
或者number
怎么实现,我说可以用泛型实现。
const add = <T extends number | string>(a: T, b: T): T => a + b
返回值的类型可以不写,因为会自动推断
然后面试官问我,还有别的方法可以实现吗?我想了半天说没有。
其实还有别的方法,利用函数重载机制。
function add(a: number, b: number): number;
function add(a: string, b: string): string;
function add(a: any, b: any): any {
return a + b;
}
ts参数可选
可以使用?
指定可选,或者使用工具函数指定可选。Omit
怎么防止多个文件打包进去多余的js和类,按需加载
分割代码,多使用动态import
语句
const PageA = lazy(() => import("./Module/PageA"))
使用tree-shaking
优化,不过需要使用ESModule
,而不是CommondJS
。tree-shaking
可以通过AST
语法树,分析代码中没有使用的模块,或者无用代码的导出。
按需引入代码,而不是引入所有
import { Button } from "antd/lib/Button"
下面的代码有啥问题,怎么修复
function C () {
const [p, setP] = useState(10)
return <Page p={[10, 20,]} onChange={(v) => {setP(v); fetch(p)}} />
}
发送请求时,都是使用上次的值,而不是最新值,这是因为useState
更新值时是异步的。(react
会进行一次批处理优化)解决方法有两种
// 方法1
function C () {
const [p, setP] = useState(10)
return <Page p={[10, 20,]} onChange={(v) => {setP(v); fetch(v)}} />
}
//方法2
function C () {
const [p, setP] = useState(10)
useEffect(() => fetch(p), [p]}
return <Page p={[10, 20,]} onChange={(v) => {setP(v)}} />
}
二面
找代码问题
聊完我的项目经历后,立马问我,下面的代码会出现什么问题,怎么解决。
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
const id = setInterval(() => {
setCount(count + 1);
}, 1000);
return () => clearInterval(id);
}, []);
return <h1>{count}</h1>;
}
上面的代码更新count
有问题,因为每次更新count
都使用旧的值,而不是最新的,
setCount((prevCount) => prevCount + 1); // 使用函数式更新
接着又给我下面的例子,问我有啥问题
function Counter() {
const [count, setCount] = useState(0);
const [step, setStep] = useState(1);
useEffect(() => {
const id = setInterval(() => {
setCount(c => c + step);
}, 1000);
return () => clearInterval(ref.current);
}, [step]);
return (
<>
<h1>{count}</h1>
<input value={step} onChange={e => setStep(Number(e.target.value))} />
</>
);
}
上面的组件出现的问题是:每次当step
的值发生改变时,都会重新创建新的定时器
修改方法如下,
function Counter() {
const [count, setCount] = useState(0);
const [step, setStep] = useState(1);
const ref = useRef(null)
useEffect(() => {
if (!ref.current) {
ref.current = setInterval(() => {
setCount(c => c + step);
}, 1000);
}
// const id = setInterval(() => {
// setCount(c => c + step);
// }, 1000);
return () => clearInterval(ref.current);
}, [step]);
return (
<>
<h1>{count}</h1>
<input value={step} onChange={e => setStep(Number(e.target.value))} />
</>
);
}
实现ts里的Picker 和 Omit
这个问题我当时是不会的,说实话,这两个工具函数我用的都很少,更别说实现了
webpack 和 vite 的 tree chunking的区别
tree chunking
是一种清除无用代码的方案,主要是通过分析静态的代码导出和导入关系,寻找无用代码,然后删除它,不将它打包,从而优化代码体积,webpack
需要开启es module
,并且开启optimization
选项,而vite
天生就是支持es module
的,不过vite
是使用依赖的es模块加载机制优化的。
webpack loader 和 plugin 的区别
功能区别:Webpack
原生只能理解js
和JSON
文件,而Loader
扩展了其能力,使其能够处理非js
文件,Loader
是文件级别的转换工具,允许你在模块打包时对文件进行处理。plugin
是对Webpack
的整个构建过程进行扩展和增强.
原理的区别:webpack
的loader
拿到的是特定类型文件,而plugin
可以根据hook
处理webpack
的各个流程。另外插件的本质是个是个带有apply
方法的类,apply
方法中参数是complier
对象,可以通过这个对象提供的hook
监听特定文件的变化进行编译。而loader
是个函数,参数是匹配到的文件源码。
module.exports = class MyPlugin {
apply(compiler) {
compiler.hooks.done.tap('MyPlugin', (Compilation) => {
console.log('MyPlugin: Compilation finished!');
});
}
}
用法区别:loader
需要根据正则匹配特定的类型的文件,plugin
需要实例化插件
commondJS 和 ESModule 的区别
commondJS
是动态的,只有在运行时才能确定具体导入的内容,而ESModule
是静态的,在编译时就能确定引入和导出的模块
commondJS
导入使用的是require
,require
可以出现在代码任何地方(所以是动态的),导出使用exports
或者module.exports
ESModule
导入语句是import
,import
只能出现在文件顶部。(所以是静态的,import方法除外),导出使用export
commondJS
实际上是被包裹在一个函数中,可以使用this
,而ESModule
没有this
commondJS
会缓存模块。重新加载返回的是缓存,ESModule
也是缓存,后续的导入将共享相同的实例
下面的输出内容是啥
// a.js
const b = require('./b');
console.log(exports.x);
exports.x = 'x';
require('./c');
// b.js
const a = require('./a');
console.log(a);
a.x = 'y';
// c.js
const a = require('./a');
console.log(a.x);
输出结果如下
{}
undefined
x
分析步骤:
加载a.js,首先遇到require('./b')
,它会开始加载b.js
。此时a.js
尚未完全加载完成,它处于未完成的状态,因此,Node.js
会将一个空的exports
对象暴露给b.js
接着加载b.js
,在b.js
中,有const a = require('./a')
,由于a.js
还在加载中(未完成),所以 a 会是当前的未完成a.js
的导出对象,此时是空的 {}。所以执行console.log(a)
,打印结果为{}
,
接着执行a.x = 'y'
,这会修改a
模块的导出内容,将{}
变为 { x: 'y' }
。但是由于 a.js
还未完成执行,exports.x = 'x'
; 还没有执行,所以x
暂时变为y
。
然后回到a.js
继续执行:
首先执行console.log(exports.x)
,此时 exports
对象还没有被赋值,exports.x
是undefined
。
然后执行exports.x = 'x'
,将a.js
的exports
对象更新为{ x: 'x' }
,覆盖了之前b.js
中将x
赋值为'y'
的修改。
接着,加载require('./c')
,开始加载 c.js。
在c.js
中,有const a = require('./a')
。由于a.js
已经完成了加载,因此a
的值是最终导出的 exports
对象{ x: 'x' }
。
执行console.log(a.x)
,此时a.x
是'x'
,所以会输出 'x'
。
写一个加法函数sum,支持sum(1)(2)(3,4)(5,6,7....), console.log(sum(1,2,3)(4)) => 输出 10
我当时写完的函数是这样的,虽然差不多意思,但是不太对。有知道的大佬可以在告诉我下。
const sum = (...args) => {
const add = (x) =>{
if (x === undefined) {
return args.reduce((a, b) => a + b, 0)
} else {
args.push(x)
return add(...args)
}
}
return add(...args)
}
三面
简单问了下项目经历,聊了下项目里的亮点,四十分钟不到结束了。这里就不多赘述了。
总结
从结果来看虽然通过了,但是真的觉得自己很菜,很多底层的东西,可能只是知其然,并没有知其所以然。
虽然es6
已经横空出世好多年了,但是里面的内容真的搞懂的不是很多。所以后续还需要多翻书,多研究细节。
最后希望这篇文章可以帮到找工作的同学,也可以翻看我前面的几篇面试文章。
文章中出现错误的地方欢迎指正!