笔试题
[toc]
JavaScript
下面代码中 a 在什么情况下会打印 1?
var a = ?;if(a == 1 && a == 2 && a == 3){console.log(1);}//解答var a = {value: 0,valueOf() {return ++this.value;}};
实现一个 sleep 函数?
比如 sleep(1000) 意味着等待1000毫秒,可从 Promise、Generator、Async/Await 等角度实现
// Promisefunction sleep1(time) {return new Promise(resolve => {setTimeout(() => {resolve();}, time);})}sleep1(1000).then(() => console.log("sleep1"));// Generatorfunction* sleep2(time) {return yield sleep1(time);}const s = sleep2(1500);s.next().value.then(() => console.log("sleep2"));// Async/Awaitasync function sleep3(time) {await sleep1(time);}(async () => {await sleep3(2000);console.log("sleep3")})()
实现 (5).add(3).minus(2) 功能?
Number.prototype.add = function(n) {return this + n;}Number.prototype.minus = function(n) {return this - n;}
instanceof 的实现原理
while (x.__proto__) {if (x.__proto__ === y.prototype) {return true;}x.__proto__ = x.__proto__.__proto__;}if (x.__proto__ === null) {return false;}
考察this指向的题
//请给出如下代码的打印结果function Foo() {Foo.a = function() {console.log(1)}this.a = function() {console.log(2)}}Foo.prototype.a = function() {console.log(3)}Foo.a = function() {console.log(4)}Foo.a();let obj = new Foo();obj.a();Foo.a();打印结果: 4 2 1
考察赋值表达式
var a = {n: 1};var b = a;a.x = a = {n: 2};console.log(a.x)console.log(b.x)//输出{n:2}undefined
// example 1var a={}, b='123', c=123;a[b]='b';a[c]='c';console.log(a[b]);---------------------// example 2var a={}, b=Symbol('123'), c=Symbol('123');a[b]='b';a[c]='c';console.log(a[b]);---------------------// example 3var a={}, b={key:'123'}, c={key:'456'};a[b]='b';a[c]='c';console.log(a[b]);//输出:c b c
考察事件循环
async function async1() {console.log('async1 start');await async2();console.log('async1 end');}async function async2() {console.log('async2');}console.log('script start');setTimeout(function() {console.log('setTimeout');}, 0)async1();new Promise(function(resolve) {console.log('promise1');resolve();}).then(function() {console.log('promise2');});console.log('script end');//运行结果:script startasync1 startasync2promise1script endasync1 endpromise2setTimeout
考察作用域
var b = 10;(function b(){b = 20;console.log(b);})();//输出:ƒ b(){b = 20;console.log(b);}//原因:作用域:执行上下文中包含作用域链;特性:声明提前:一个声明在函数体内都是可见的,函数声明优先于变量声明;在非匿名自执行函数中,函数变量为只读状态无法修改。
var a = 10;(function () {console.log(a)a = 5console.log(window.a)var a = 20;console.log(a)})()//输出 undefined 10 20
手写 bind、call、apply
Function.prototype.MyCall = function (context) {const args = [...arguments].slice(1);context.fn = this;const result = context.fn(...args);delete context.fn;return result;}Function.prototype.MyApply = function (context) {const args = arguments[1] || [];context.fn = this;const result = context.fn(...args);delete context.fn;return result;}Function.prototype.MyBind = function (context) {const args = [...arguments].slice(1);return function () {context.MyApply(context, args);}}
模拟 new 的实现
function myNew(fn) {const newObj = Object.create(fn.prototype);result = fn.apply(newObj, [...arguments].slice(1));return typeof result === "object" ? result : newObj;}
数据结构
数据处理1(数组基础)
某公司 1 到 12 月份的销售额存在一个对象里面 如下:{1:222, 2:123, 5:888},请把数据处理为如下结构:[222, 123, null, null, 888, null, null, null, null, null, null, null]
//解答:function convert(obj) {return Array.from({ length: 12 }).map((item, index) => obj[index] || null).slice(1);}
数据处理2(数组基础)
var obj = {'2': 3,'3': 4,'length': 2,'splice': Array.prototype.splice,'push': Array.prototype.push}obj.push(1)obj.push(2)console.log(obj)输出:[,,1,2] length 为 4解释:Array.prototype.push 将根据 length 将元素填充到对应位置并修改 length 属性 +1,所以输出的结果就是上述结果
数据处理3(消息队列)
要求设计 LazyMan 类,实现以下功能。
LazyMan('Tony');// Hi I am TonyLazyMan('Tony').sleep(10).eat('lunch');// Hi I am Tony// 等待了10秒...// I am eating lunchLazyMan('Tony').eat('lunch').sleep(10).eat('dinner');// Hi I am Tony// I am eating lunch// 等待了10秒...// I am eating dinerLazyMan('Tony').eat('lunch').eat('dinner').sleepFirst(5).sleep(10).eat('junk food');// Hi I am Tony// 等待了5秒...// I am eating lunch// I am eating dinner// 等待了10秒...// I am eating junk food//实现:class LazyManClass {constructor(name) {this.name = name;this.fns = [];console.log(`Hi I am ${this.name}`);setTimeout(() => {this.next();});return this}sleep(time) {const fn = () => {setTimeout(() => {console.log(`等待了${time}秒...`)this.next();}, time * 1000)}this.fns.push(fn);return this;}sleepFirst(time) {const fn = () => {setTimeout(() => {console.log(`等待了${time}秒...`)this.next();}, time * 1000)}this.fns.unshift(fn);return this;}eat(food) {const fn = () => {console.log(`I am eating ${food}`);this.next();}this.fns.push(fn);return this;}next() {const fn = this.fns.shift();fn && fn();}}const LazyMan = (name) => {return new LazyManClass(name);}// LazyMan('Tony');// Hi I am Tony// LazyMan('Tony').sleep(10).eat('lunch');// Hi I am Tony// 等待了10秒...// I am eating lunch// LazyMan('Tony').eat('lunch').sleep(10).eat('dinner');// Hi I am Tony// I am eating lunch// 等待了10秒...// I am eating dinerLazyMan('Tony').eat('lunch').eat('dinner').sleepFirst(5).sleep(10).eat('junk food');// Hi I am Tony// 等待了5秒...// I am eating lunch// I am eating dinner// 等待了10秒...// I am eating junk food
数据处理4(数组扁平化)
//已知如下数组:var arr = [ [1, 2, 2], [3, 4, 5, 5], [6, 7, 8, 9, [11, 12, [12, 13, [14] ] ] ], 10];//编写一个程序将数组扁平化去并除其中重复部分数据,最终得到一个升序且不重复的数组// 思路型function flatten(arr) {let flattenArr = [];for (let i = 0; i < arr.length; i++) {const item = arr[i];if (Array.isArray(item)) {flattenArr = flattenArr.concat(flatten(item))} else {if (flattenArr.indexOf(item) > -1) continue;flattenArr.push(item);}}return flattenArr.sort((a, b) => a - b);}// API 型function flatten2(arr) {return Array.from(new Set(arr.flat(Infinity))).sort((a, b) => a - b);}
数据处理5(数组大小写取反)
//如何把一个字符串的大小写取反(大写变小写小写变大写),例如 ’AbC' 变成 'aBc'function convert(str) {let convertStr = "";for (let i = 0; i < str.length; i++) {const code = str.charCodeAt(i);if (code >= 97) {convertStr += String.fromCharCode(code - 32);} else {convertStr += String.fromCharCode(code + 32);}}return convertStr;}console.log(convert("AbC"));
数据处理6(数组随机排序)
//如何实现数组的随机排序?// 随机数排序function random1(arr) {return arr.sort(() => Math.random() - .5);}// 随机插入排序function random2(arr) {const cArr = [...arr];const newArr = [];while (cArr.length) {const index = Math.floor(Math.random() * cArr.length);newArr.push(cArr[index]);cArr.splice(index, 1);}return newArr;}// 洗牌算法,随机交换排序function random3(arr) {const l = arr.length;for (let i = 0; i < l; i++) {const index = Math.floor(Math.random() * (l - i)) + i;const temp = arr[index];arr[index] = arr[i];arr[i] = temp;}return arr;}
网络
使用 Proxy + Fetch 实现类似于 axios 的基础 API
const fetch = require("node-fetch");const axiosOriginal = {methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS']};const axios = new Proxy(axiosOriginal, {set() {throw new Error("Can't set any property");},get(target, name) {const method = name.toLocaleUpperCase();if (target.methods.indexOf(method) === -1) throw new Error(`Can't support method ${method}`);return (url, options) => {return fetch(url, {method,...options}).then(res => res.text())}}});axios.get("http://www.baidu.com").then(res => console.log(res));axios.post("http://www.baidu.com").then(res => console.log(res));
React
考察setState
class Example extends React.Component {constructor() {super();this.state = {val: 0};}componentDidMount() {this.setState({val: this.state.val + 1});console.log(this.state.val); // 第 1 次 logthis.setState({val: this.state.val + 1});console.log(this.state.val); // 第 2 次 logsetTimeout(() => {this.setState({val: this.state.val + 1});console.log(this.state.val); // 第 3 次 logthis.setState({val: this.state.val + 1});console.log(this.state.val); // 第 4 次 log}, 0);}render() {return null;}};//输出0 0 2 3//解答:第一次和第二次都是在 react 自身声明周期内,触发时 isBatchingUpdates 为 true,所以并不会直接执行更新 state,而是加入了 dirtyComponents,所以打印的时获取的都是更新前的状态 0;两次 setState,获取到 this.state.val 都是 0,所以执行时都是将 0 设置成 1,在 react 内部会被合并掉,只执行一次,设置完成后 state.val 值为 1.setTimeout 中的代码,触发时 isBatchingUpdate 为 false,所以能够直接进行更新,所以连着输出 2、3
