1.高级 WEB 面试会让你手写一个Promise,Generator 的 PolyFill(一段代码);
2.在写之前我们简单回顾下他们的作用;
3.手写模块见PolyFill.
源码地址请戳,原创码字不易,欢迎 star
如果觉得看文章太啰嗦,可以直接 git clone ,直接看代码
Promise 大家应该都用过,ajax 库就是利用 Promise封装的;
作用主要是解决地狱回调问题.
new Promise((resolve,reject)=>{
resolve('这是第一个 resolve 值')
}).then((data)=>{
console.log(data) //会打印'这是第一个 resolve 值'
}).catch(()=>{
})
new Promise((resolve,reject)=>{
reject('这是第一个 reject 值')
}).then((data)=>{
console.log(data)
}).catch((data)=>{
console.log(data) //会打印'这是第一个 reject 值'
})
Promise.resolve('这是第二个 resolve 值').then((data)=>{
console.log(data) // 会打印'这是第二个 resolve 值'
})
Promise.reject('这是第二个 reject 值').then((data)=>{
console.log(data)
}).catch(data=>{
console.log(data) //这是第二个 reject 值
})
表示多个 Promise 都进入到 FulFilled 或者 Rejected 就会执行
const pOne = new Promise((resolve, reject) => {
resolve(1);
});
const pTwo = new Promise((resolve, reject) => {
resolve(2);
});
const pThree = new Promise((resolve, reject) => {
resolve(3);
});
Promise.all([pOne, pTwo, pThree]).then(data => {
console.log(data); // [1, 2, 3] 正常执行完毕会执行这个,结果顺序和promise实例数组顺序是一致的
}, err => {
console.log(err); // 任意一个报错信息
});
表示多个 Promise 只有一个进入到 FulFilled 或者 Rejected 就会执行
const pOne = new Promise((resolve, reject) => {
resolve(1);
});
const pTwo = new Promise((resolve, reject) => {
resolve(2);
});
const pThree = new Promise((resolve, reject) => {
// resolve(3);
});
Promise.race([pOne, pTwo, pThree]).then(data => {
console.log(data); // 1 只要碰到FulFilled 或者 Rejected就会停止执行
}, err => {
console.log(err); // 任意一个报错信息
});
1.Promise接受一个函数handle作为参数,handle包括resolve和reject两个是函数的参数
2.Promise 相当于一个状态机,有三种状态:pending,fulfilled,reject,初始状态为 pending
3.调用 resolve,状态由pending => fulfilled
4.调用reject,会由pending => rejected
5.改变之后不会变化
1.接受两个参数,onFulfilled和onRejected可选的函数
2.不是函数必须被忽略
3.onFullfilled:
A.当 promise 状态变为成功时必须被调用,其第一个参数为 promise 成功状态传入的值( resolve 执行时传入的值;
B.在 promise 状态改变前其不可被调用
C.其调用次数不可超过一次
4.onRejected:作用和onFullfilled类似,只不过是promise失败调用
5.then方法可以链式调用
A.每次返回一个新的Promise
B.执行规则和错误捕获:then的返回值如果是非Promise直接作为下一个新Promise参数,如果是Promise会等Promise执行
// 返回非Promise
let promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve()
}, 1000)
})
promise2 = promise1.then(res => {
// 返回一个普通值
return '这里返回一个普通值'
})
promise2.then(res => {
console.log(res) //1秒后打印出:这里返回一个普通值
})
// 返回Promise
let promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve()
}, 1000)
})
promise2 = promise1.then(res => {
// 返回一个Promise对象
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('这里返回一个Promise')
}, 2000)
})
})
promise2.then(res => {
console.log(res) //3秒后打印出:这里返回一个Promise
})
C. onFulfilled 或者onRejected 抛出一个异常 e ,则 promise2 必须变为失败(Rejected),并返回失败的值 e
let promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success')
}, 1000)
})
promise2 = promise1.then(res => {
throw new Error('这里抛出一个异常e')
})
promise2.then(res => {
console.log(res)
}, err => {
console.log(err) //1秒后打印出:这里抛出一个异常e
})
D.onFulfilled 不是函数且 promise1 状态为成功(Fulfilled), promise2 必须变为成功(Fulfilled)并返回 promise1 成功的值
let promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success')
}, 1000)
})
promise2 = promise1.then('这里的onFulfilled本来是一个函数,但现在不是')
promise2.then(res => {
console.log(res) // 1秒后打印出:success
}, err => {
console.log(err)
})
E.onRejected 不是函数且 promise1 状态为失败(Rejected),promise2必须变为失败(Rejected) 并返回 promise1 失败的值
let promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('fail')
}, 1000)
})
promise2 = promise1.then(res => res, '这里的onRejected本来是一个函数,但现在不是')
promise2.then(res => {
console.log(res)
}, err => {
console.log(err) // 1秒后打印出:fail
})
F.resolve和reject结束一个Promise的调用
G.catch方法,捕获异常
其实复杂的是在then的异常处理上,不过不用急,边看边理解
1.静态resolve方法
参照1.3.1用法2
2.静态reject方法
参照1.3.1用法2
3.静态all方法
参照1.3.1用法3
4.静态race方法
参照1.3.1用法4
5.自定义done方法
Promise 对象的回调链,不管以then方法或catch方法结尾,要是最后一个方法抛出错误,都有可能无法捕捉到(因为 Promise内部的错误不会冒泡到全局)
因此,我们可以提供一个done方法,总是处于回调链的尾端,保证抛出任何可能出现的错误
Promise.prototype.done = function (onFulfilled, onRejected) {
this
.then(onFulfilled, onRejected)
.catch(function (reason) {
// 抛出一个全局错误
setTimeout(() => {
throw reason
}, 0)
})
}
6.自定义finally方法
finally方法用于指定不管Promise对象最后状态如何,都会执行的操作
它与done方法的最大区别,它接受一个普通的回调函数作为参数,该函数不管怎样都必须执行
Promise.prototype.finally = function (callback) {
let P = this.constructor
return this.then(
value => P.resolve(callback()).then(() => value),
reason => P.resolve(callback()).then(() => {
throw reason
})
)
}
class MyPromise {
constructor (handle) {
// 判断handle函数与否
if (typeof handle!=='function') {
throw new Error('MyPromise must accept a function as a parameter')
}
// 添加状态
this._status = 'PENDING'
// 添加状态
this._value = undefined
// 执行handle
try {
handle(this._resolve.bind(this), this._reject.bind(this))
} catch (err) {
this._reject(err)
}
}
// 添加resovle时执行的函数
_resolve (val) {
if (this._status !== 'PENDING') return
this._status = 'FULFILLED'
this._value = val
}
// 添加reject时执行的函数
_reject (err) {
if (this._status !== 'PENDING') return
this._status = 'REJECTED'
this._value = err
}
}
回顾一下,初级版实现了1,2,3这三点功能,怎么样还是so-easy吧!
1.由于 then 方法支持多次调用,我们可以维护两个数组,将每次 then 方法注册时的回调函数添加到数组中,等待执行
在初级的基础上加入成功回调函数队列和失败回调队列和then方法
this._fulfilledQueues = []
this._rejectedQueues = []
2.then方法
将
then (onFulfilled, onRejected) {
const { _value, _status } = this
switch (_status) {
// 当状态为pending时,将then方法回调函数加入执行队列等待执行
case 'PENDING':
this._fulfilledQueues.push(onFulfilled)
this._rejectedQueues.push(onRejected)
break
// 当状态已经改变时,立即执行对应的回调函数
case 'FULFILLED':
onFulfilled(_value)
break
case 'REJECTED':
onRejected(_value)
break
}
// 返回一个新的Promise对象
return new MyPromise((onFulfilledNext, onRejectedNext) => {
})
}
3.then方法规则完善
// 添加then方法
then (onFulfilled, onRejected) {
const { _value, _status } = this
// 返回一个新的Promise对象
return new MyPromise((onFulfilledNext, onRejectedNext) => {
// 封装一个成功时执行的函数
let fulfilled = value => {
try {
if (typeof onFulfilled!=='function') {
onFulfilledNext(value)
} else {
let res = onFulfilled(value);
if (res instanceof MyPromise) {
// 如果当前回调函数返回MyPromise对象,必须等待其状态改变后在执行下一个回调
res.then(onFulfilledNext, onRejectedNext)
} else {
//否则会将返回结果直接作为参数,传入下一个then的回调函数,并立即执行下一个then的回调函数
onFulfilledNext(res)
}
}
} catch (err) {
// 如果函数执行出错,新的Promise对象的状态为失败
onRejectedNext(err)
}
}
// 封装一个失败时执行的函数
let rejected = error => {
try {
if (typeof onRejected!=='function') {
onRejectedNext(error)
} else {
let res = onRejected(error);
if (res instanceof MyPromise) {
// 如果当前回调函数返回MyPromise对象,必须等待其状态改变后在执行下一个回调
res.then(onFulfilledNext, onRejectedNext)
} else {
//否则会将返回结果直接作为参数,传入下一个then的回调函数,并立即执行下一个then的回调函数
onFulfilledNext(res)
}
}
} catch (err) {
// 如果函数执行出错,新的Promise对象的状态为失败
onRejectedNext(err)
}
}
switch (_status) {
// 当状态为pending时,将then方法回调函数加入执行队列等待执行
case 'PENDING':
this._fulfilledQueues.push(fulfilled)
this._rejectedQueues.push(rejected)
break
// 当状态已经改变时,立即执行对应的回调函数
case 'FULFILLED':
fulfilled(_value)
break
case 'REJECTED':
rejected(_value)
break
}
})
}
4.当 resolve 或 reject 方法执行时,我们依次提取成功或失败任务队列当中的函数开始执行,并清空队列,从而实现 then 方法的多次调用
// 添加resovle时执行的函数
_resolve (val) {
if (this._status !== PENDING) return
// 依次执行成功队列中的函数,并清空队列
const run = () => {
this._status = FULFILLED
this._value = val
let cb;
while (cb = this._fulfilledQueues.shift()) {
cb(val)
}
}
// 为了支持同步的Promise,这里采用异步调用
setTimeout(() => run(), 0)
}
// 添加reject时执行的函数
_reject (err) {
if (this._status !== PENDING) return
// 依次执行失败队列中的函数,并清空队列
const run = () => {
this._status = REJECTED
this._value = err
let cb;
while (cb = this._rejectedQueues.shift()) {
cb(err)
}
}
// 为了支持同步的Promise,这里采用异步调用
setTimeout(run, 0)
}
5.当 resolve 方法传入的参数为一个 Promise 对象时,则该 Promise 对象状态决定当前 Promise 对象的状态
// 添加resovle时执行的函数
_resolve (val) {
const run = () => {
if (this._status !== PENDING) return
this._status = FULFILLED
// 依次执行成功队列中的函数,并清空队列
const runFulfilled = (value) => {
let cb;
while (cb = this._fulfilledQueues.shift()) {
cb(value)
}
}
// 依次执行失败队列中的函数,并清空队列
const runRejected = (error) => {
let cb;
while (cb = this._rejectedQueues.shift()) {
cb(error)
}
}
/* 如果resolve的参数为Promise对象,则必须等待该Promise对象状态改变后,
当前Promsie的状态才会改变,且状态取决于参数Promsie对象的状态
*/
if (val instanceof MyPromise) {
val.then(value => {
this._value = value
runFulfilled(value)
}, err => {
this._value = err
runRejected(err)
})
} else {
this._value = val
runFulfilled(val)
}
}
// 为了支持同步的Promise,这里采用异步调用
setTimeout(run, 0)
}
6.catch方法
// 添加catch方法
catch (onRejected) {
return this.then(undefined, onRejected)
}
1.静态 resolve 方法
// 添加静态resolve方法
static resolve (value) {
// 如果参数是MyPromise实例,直接返回这个实例
if (value instanceof MyPromise) return value
return new MyPromise(resolve => resolve(value))
}
2.静态 reject 方法
// 添加静态reject方法
static reject (value) {
return new MyPromise((resolve ,reject) => reject(value))
}
3.静态 all 方法
// 添加静态all方法
static all (list) {
return new MyPromise((resolve, reject) => {
/**
* 返回值的集合
*/
let values = []
let count = 0
for (let [i, p] of list.entries()) {
// 数组参数如果不是MyPromise实例,先调用MyPromise.resolve
this.resolve(p).then(res => {
values[i] = res
count++
// 所有状态都变成fulfilled时返回的MyPromise状态就变成fulfilled
if (count === list.length) resolve(values)
}, err => {
// 有一个被rejected时返回的MyPromise状态就变成rejected
reject(err)
})
}
})
}
4.静态 race 方法
// 添加静态race方法
static race (list) {
return new MyPromise((resolve, reject) => {
for (let p of list) {
// 只要有一个实例率先改变状态,新的MyPromise的状态就跟着改变
this.resolve(p).then(res => {
resolve(res)
}, err => {
reject(err)
})
}
})
}
5.done方法
作用:不管以then方法或catch方法结尾,要是最后一个方法抛出错误,都有可能无法捕捉到(因为 Promise 内部的错误不会冒泡到全局);处于回调链的尾端,保证抛出任何可能出现的错误
目前 Promise 上还没有 done,我们可以自定义一个
Promise.prototype.done = function (onFulfilled, onRejected) {
console.log('done')
this.then(onFulfilled, onRejected)
.catch((reason)=> {
// 抛出一个全局错误
setTimeout(() => {
throw reason
}, 0)
})
}
Promise.resolve('这是静态方法的第一个 resolve 值').then(()=>{
return '这是静态方法的第二个 resolve 值'
}).then(()=>{
throw('这是静态方法的第一个 reject 值')
return '这是静态方法的第二个 resolve 值'
}).done()
6.finally方法
作用:不管 Promise 对象最后状态如何,都会执行的操作
与done方法的最大区别,它接受一个普通的回调函数作为参数,该函数不管怎样都必须执行
finally (cb) {
return this.then(
value => MyPromise.resolve(cb()).then(() => value),
reason => MyPromise.resolve(cb()).then(() => { throw reason })
);
};
7.完整代码 请戳,源码地址,欢迎 star!
1.Generator可以理解为一个状态机,内部封装了很多状态,同时返回一个迭代器Iterator对象;
2.迭代器Iterator对象:定义标准方式产生一个有限或无限序列值,迭代器有next()对象;
3.多次返回可以被 next多次调用,最大特点是可以控制执行顺序;
2.是一种特殊的函数
function* gen(x){
const y = yield x + 6;
return y;
}
// yield 如果用在另外一个表达式中,要放在()里面
// 像上面如果是在=右边就不用加()
function* genOne(x){
const y = `这是第一个 yield 执行:${yield x + 1}`;
return y;
}
整个 Generator 函数就是一个封装的异步任务,或者说是异步任务的容器。异步操作需要暂停的地方,都用 yield 语句注明
1.普通执行
const g = gen(1);
//执行 Generator 会返回一个Object,而不是像普通函数返回return 后面的值
g.next() // { value: 7, done: false }
//调用指针的 next 方法,会从函数的头部或上一次停下来的地方开始执行,直到遇到下一个 yield 表达式或return语句暂停,也就是执行yield 这一行
// 执行完成会返回一个 Object,
// value 就是执行 yield 后面的值,done 表示函数是否执行完毕
g.next() // { value: undefined, done: true }
// 因为最后一行 return y 被执行完成,所以done 为 true
2.next 方法传参数
const g = gen(1);
g.next() // { value: 7, done: false }
g.next(2) // { value: 2, done: true }
// next 的参数是作为上个阶段异步任务的返回结果
3.嵌套执行
必须用到yield*关键字
function* genTwo(x){
yield* gen(1)
yield* genOne(1)
const y = 这是第 二个 yield 执行:${yield x + 2};
return y;
}
const iterator=genTwo(1)
// 因为 Generator 函数运行时生成的是一个 Iterator 对象,所以可以直接使用 for...of 循环遍历
for(let value of iterator) {
console.log(value)
}
相同点:
1.都能返回语句后面的那个表达式的值
2.都可以暂停函数执行
区别:
一个函数可以有多个 yield,但是只能有一个 return
yield 有位置记忆功能,return 没有
抛出错误,可以被try...catch...捕捉到
g.throw('这是抛出的一个错误');
// 这是抛出的一个错误
// 要求:函数valOne,valTwo,valThree 以此执行
function* someTask(){
try{
const valOne=yield 1
const valTwo=yield 2
const valThree=yield 3
}catch(e){
}
}
scheduler(someTask());
function scheduler(task) {
const taskObj = task.next(task.value);
console.log(taskObj)
// 如果Generator函数未结束,就继续调用
if (!taskObj.done) {
task.value = taskObj.value
scheduler(task);
}
}
原理图
实现一个迭代器(Iterator)
// 源码实现
function createIterator(items) {
var i = 0
return {
next: function() {
var done = (i >= items.length)
var value = !done ? items[i++] : undefined
return {
done: done,
value: value
}
}
}
}
// 应用
const iterator = createIterator([1, 2, 3])
console.log(iterator.next()) // {value: 1, done: false}
console.log(iterator.next()) // {value: 2, done: false}
console.log(iterator.next()) // {value: 3, done: false}
console.log(iterator.next()) // {value: undefined, done: true}
实现可迭代(Iterable)
1.可以通过 for...of...遍历的对象,即原型链上有Symbol.iterator属性;
2.Symbol.iterator:返回一个对象的无参函数,被返回对象符合迭代器协议;
3.for...of接受一个可迭代对象(Iterable),或者能强制转换/包装成一个可迭代对象的值(如’abc’),遍历时,for...of会获取可迭代对象的'Symbol.iterator',对该迭代器逐次调用next(),直到迭代器返回对象的done属性为true时,遍历结束,不对该value处理;
const a = ['a', 'b', 'c', 'd', 'e']
for (let val of a) {
console.log(val)
}
// 'a' 'b' 'c' 'd' 'e'
// 等价于
const a = ["a", "b", "c", "d", "e"]
for (let val, ret, it = a[Symbol.iterator]();
(ret = it.next()) && !ret.done;
) {
let = ret.value
console.log(val)
}
// "a" "b" "c" "d" "e"
4.yield* 可返回一个 Iterable对象;
5.源码改造
function createIterator(items) {
let i = 0
return {
next: function () {
let done = (i >= items.length)
let value = !done ? items[i++] : undefined
return {
done: done,
value: value
}
}
[Symbol.iterator]: function () {
return this
}
}
}
let iterator = createIterator([1, 2, 3])
...iterator // 1, 2, 3
1.for...of...接收可迭代对象,能强制转换或包装可迭代对象的值;
2.遍历时,for...of会获取可迭代对象的'Symbol.iterator',对该迭代器逐次调用next(),直到迭代器返回对象的done属性为true时,遍历结束,不对该value处理;
3.所以可以利用 for...of...封装到原型链上.
Object.prototype[Symbol.iterator] = function* () {
for (const key in this) {
if (this.hasOwnProperty(key)) {
yield [key, this[key]]
}
}
}
1.async 函数返回的是一个 Promise 对象
在函数中 return 一个直接量,async 会把这个直接量通过 Promise.resolve() 封装成 Promise 对象
async function testAsync() {
return "hello async";
}
const result = testAsync();
console.log(result); //Promise 对象
2.async和then
async返回一个Promise,所以可以通过then获取值
testAsync().then(v => {
console.log(v); // 输出 hello async
});
所以async里面的函数会马上执行,这个就类似Generator的‘*’
1.await后面可以是Promise对象或其他表达式
function getSomething() {
return "something";
}
async function testAsync() {
return Promise.resolve("hello async");
}
async function test() {
const v1 = await getSomething();
const v2 = await testAsync();
console.log(v1, v2); //something 和 hello async
}
test();
2.await后面不是Promise对象,直接执行
3.await后面是Promise对象会阻塞后面的代码,Promise 对象 resolve,然后得到 resolve 的值,作为 await 表达式的运算结果
4.所以这就是await必须用在async的原因,async刚好返回一个Promise对象,可以异步执行阻塞
1.主要是处理Promise的链式回调或函数的地狱回调
回到Generator中要求函数valOne,valTwo,valThree函数依次执行
function valOne(){}
function valTwo(){}
function valThree(){}
async ()=>{
await valOne()
await valTwo()
await valThree()
}
2.处理异常
try...catch...
或者await .catch()
1.async是内置执行器,Generator 函数的执行必须依靠执行器,无需手动执行next()
2.更广的适用性。co模块约定,yield命令后面只能是 Thunk 函数或 Promise 对象,而await后面可以是任意表达式,都会返回一个Promise对象
// Thunk函数:是能将执行结果传入回调函数,并将该回调函数返回的函数
function f(m) {
return m * 2;
}
f(x + 5);
// 等同于
var thunk = function () {
return x + 5;
};
function f(thunk) {
return thunk() * 2;
}
3.返回Promise,而Generator返回 Iterator
4.async 函数就是 Generator 函数的语法糖
async就相当于Generator的*,await相当于yield,用法有很多相似之处
实现执行器两种方式:
回调函数(Thunk 函数)
Promise 对象
async function fn(args) {
// ...
}
// 等价于
function fn(args) {
return spawn(function* () {
// ...
});
}
function spawn(gen){
let g = gen();
function next(data){
let result = g.next(data);
if (result.done) return result.value;
result.value.then(function(data){
next(data);
});
}
next();
}
function spawn(genF) { //spawn函数就是自动执行器,跟简单版的思路是一样的,多了Promise和容错处理
return new Promise(function(resolve, reject) {
const gen = genF();
function step(nextF) {
let next;
try {
next = nextF();
} catch(e) {
return reject(e);
}
if(next.done) {
return resolve(next.value);
}
Promise.resolve(next.value).then(function(v) {
step(function() { return gen.next(v); });
}, function(e) {
step(function() { return gen.throw(e); });
});
}
step(function() { return gen.next(undefined); });
});
}
1.代码对比:
场景:假定某个 dom 元素上面,部署了一系列的动画,前一个动画结束,才能开始后一个。如果当中有一个动画出错,就不再往下执行,返回上一个成功执行的动画的返回值。
A.Promise
function chainAnimationsPromise(elem, animations) {
// 变量ret用来保存上一个动画的返回值
let ret = null;
// 新建一个空的Promise
let p = Promise.resolve();
// 使用then方法,添加所有动画
for(let anim of animations) {
p = p.then(function(val) {
ret = val;
return anim(elem);
});
}
// 返回一个部署了错误捕捉机制的Promise
return p.catch(function(e) {
/* 忽略错误,继续执行 */
}).then(function() {
return ret;
});
}
B.Generator
function chainAnimationsGenerator(elem, animations) {
return spawn(function*() {
let ret = null;
try {
for(let anim of animations) {
ret = yield anim(elem);
}
} catch(e) {
/* 忽略错误,继续执行 */
}
return ret;
});
}
C.async
async function chainAnimationsAsync(elem, animations) {
let ret = null;
try {
for(let anim of animations) {
ret = await anim(elem);
}
} catch(e) {
/* 忽略错误,继续执行 */
}
return ret;
}
对比可以看出 async...await...代码更优雅
async 和 await 是在 Generator 的基础上封装了自执行函数和一些特性;
具体对比见没个 api 的 PolyFill
来道头条的面试
console.log('script start')
async function async1() {
await async2()
console.log('async1 end')
}
async function async2() {
console.log('async2 end')
}
async1()
setTimeout(function() {
console.log('setTimeout')
}, 0)
new Promise(resolve => {
console.log('Promise')
resolve()
})
.then(function() {
console.log('promise1')
})
.then(function() {
console.log('promise2')
})
console.log('script end')
// 旧版 Chrome 打印
// script start => async2 end => Promise => script end => promise1 => promise2 => async1 end => setTimeout
// 新版 Chrome 打印
// script start => async2 end => Promise => script end => async1 end => promise1 => promise2 => setTimeout
// 这里面其他的顺序除了async1 end , promise1 , promise2 这几个顺序有点争议,其他应该没有什么问题
// 新版是因为V8 团队将最新的规范进行了修改,await变得更快了,这道题细节分析不再赘述,上面原理都有讲到
原创码字不易,欢迎 star!
很多童鞋可能年后有自己的一些计划,比如换份工作环境,比如对职业目标有了新的打算。当然面试这一关不得不过,大概又不可能系统性的复习,这里罗列一些 重点 面试的知识点和文章,
什么是webpack和grunt和gulp有什么不同?什么是bundle,什么是chunk,什么是module?什么是Loader?什么是Plugin?如何可以自动生成webpack配置?webpack-dev-server和http服务器如nginx有什么区别?
多问问应聘者高层次的知识点,如果能讲清楚这些概念,就说明即使应聘者没怎么接触过 JavaScript,也能够在短短几个星期之内就把语言细节和语法之类的东西弄清楚。
面试比棘手的技术问题要多,这篇文章整理了37个JavaScript基本面试问题和解答,这些仅仅是作为指导。希望对前端开发的你有所帮助!
React常见面试题:React中调用setState之后发生了什么事情?React中Element与Component的区别?优先选择使用ClassComponent而不是FunctionalComponent?React中的refs属性的作用是什么?React中keys的作用是什么?
题目大意为:JS 环境下,如何让 a == 1 && a == 2 && a == 3 这个表达式返回 true ?这道题目乍看之下似乎不太可能,因为在正常情况下,一个变量的值如果没有手动修改,在一个表达式中是不会变化的。
10道JavaScript题目:累加函数addNum、实现一个Person类、实现一个arrMerge 函数、实现一个toCamelStyle函数、setTimeout实现重复调用、实现一个bind函数、实现一个Utils模块、输出一个对象自身的属性
面试开场白总缺少不了自我介绍,一方面是面试官想听听你对自己的介绍,顺便有时间看看简历上的描述,是否与口述一致。另一方面就是看看你简历上做过什么项目,用到了哪些技术栈,一会儿好提问你。
把面试当做学习,这个过程你会收益很大。前端知识很杂,可能实际工作中用到的技术,像框架都是跟着公司的要求走的,像我最近也在看React啦,Vue和React都对比着再学习
vue上手可以说是比较轻松而且简单,如果你用过angular,react,你也会很喜欢vue。vue的核心思想依旧是:构建用户界面的渐进式框架,关注视图的变化。这也是为什么新建的文件是结构是template script style
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!