2017年是JavaScript生态快速演进的一年,ES6全面普及、Node.js进入v8时代、React/Vue生态爆发。企业对前端工程师的要求从“会用”转向“懂原理、能优化、可维护”。具体表现在以下三方面:
据考ACCA网对2017年主流招聘平台(BOSS直聘、拉勾网、猎聘)中2017 web前端js面试题的抽样分析(样本量>1200份),ES6相关问题占比达78%,异步编程(Promise/async)跃居第二(63%),原型链与闭包仍为经典“拦路虎”(均超55%)。
企业考察重点从“能否画图”转向“能否解释行为”。例如:
真题:以下代码输出什么?为什么?
function Foo() {} Foo.prototype.a = "proto"; let obj = new Foo(); obj.a = "own"; console.log(obj.hasOwnProperty("a")); // true console.log(obj.a); // "own" delete obj.a; console.log(obj.a); // "proto" console.log(obj.hasOwnProperty("a")); // false
解析:对象自有属性优先级高于原型属性;delete仅移除自有属性,原型属性不受影响。
2017年考题更关注闭包的实际应用与陷阱:
真题:用闭包实现计数器,并避免变量污染。
const createCounter = function() { let count = 0; return { increment: function() { count++; }, getValue: function() { return count; } }; }; const counter = createCounter(); counter.increment(); console.log(counter.getValue()); // 1
延伸:若将let改为var,多个计数器是否共享状态?——是!因var无块级作用域,闭包捕获的是同一变量。
2017年是Promise成为标准、async/await兴起之年:
真题:用Promise封装setTimeout,并实现三个异步任务顺序执行。
const delay = function(ms) { return new Promise(function(resolve) { setTimeout(resolve, ms); }); }; delay(1000) .then(function() { console.log('A'); }) .then(function() { return delay(500); }) .then(function() { console.log('B'); });
陷阱:若.then()中未return Promise,后续任务将立即执行(非等待)。
2017年考题常结合箭头函数考察:
真题:输出结果?
var name = "global"; var obj = { name: "obj", log: function() { console.log(this.name); } }; var log = obj.log; var log2 = () => { obj.log(); }; log(); // "global" log.call(obj); // "obj" log2(); // "obj"
原理:箭头函数无自身this,继承外层作用域的this(此处为全局),但obj.log()调用时,log内部的this仍指向obj。
2017年Node.js v8普及,事件循环成为必考项:
真题:输出顺序?
console.log('1'); setTimeout(function() { console.log('2'); }, 0); Promise.resolve().then(function() { console.log('3'); }); console.log('4');
答案:1 → 4 → 3 → 2
解析:宏任务(setTimeout)→ 微任务(Promise.then)→ 下一轮宏任务。微任务优先级高于宏任务。
2017年考题强调“避免重排重绘”的实践能力:
真题:优化以下代码(添加1000个li):
// ❌ 差方案:每次append触发重排 for (let i = 0; i < 1000; i++) { let li = document.createElement('li'); li.textContent = "Item " + i; document.getElementById('list').appendChild(li); }
优解:使用DocumentFragment或字符串拼接后一次性插入:
const fragment = document.createDocumentFragment(); for (let i = 0; i < 1000; i++) { let li = document.createElement('li'); li.textContent = "Item " + i; fragment.appendChild(li); } document.getElementById('list').appendChild(fragment);
2017年考题常考“三性”:作用域(块级 vs 函数级)、变量提升(无 vs 有)、重复声明(禁止 vs 允许)。
真题:以下代码输出?
console.log(a); // undefined console.log(b); // ReferenceError: b is not defined var a = 1; let b = 2;
延伸:Temporal Dead Zone(TDZ)——let/const声明的变量在声明前处于“临时死区”,访问即报错。
真题:输出结果?
let { a = 1, b = a + 1 } = {}; console.log(a, b); // 1, 2 let { x = 1, y = x + 1 } = { x: 2 }; console.log(x, y); // 2, 3
原理:默认值仅在属性值为undefined时生效。注意x为2(非undefined),故不使用默认值。
真题:用ES5实现继承,并指出instanceof原理。
function Parent(name) { this.name = name; } Parent.prototype.sayName = function() { console.log(this.name); }; function Child(name, age) { Parent.call(this, name); // 构造函数继承(属性) this.age = age; } Child.prototype = new Parent(); // 原型链继承(方法) Child.prototype.constructor = Child; // 修正构造函数指向
instanceof原理:检查左侧对象的原型链中是否存在右侧构造函数的prototype。
真题:实现深拷贝(考虑函数、循环引用)。
function deepClone(obj, hash = new WeakMap()) { if (typeof obj !== 'object' || obj === null) return obj; if (hash.has(obj)) return hash.get(obj); let clone = Array.isArray(obj) ? [] : {}; hash.set(obj, clone); for (let key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { clone[key] = deepClone(obj[key], hash); } } return clone; }
关键点:使用WeakMap防止循环引用导致栈溢出。
真题:用async/await实现并发请求,并捕获单个失败。
async function fetchData() { try { const [a, b] = await Promise.all([ fetch('/api/a').then(r => r.json()), fetch('/api/b').then(r => r.json()) ]); console.log(a, b); } catch (e) { console.error('部分请求失败', e); } }
注意:Promise.all中任一失败即进入catch;若需独立处理,可用Promise.allSettled(ES2020)或分别包裹try/catch。
真题:输出顺序?
console.log('start'); setTimeout(function() { console.log('timeout'); }, 0); const p = Promise.resolve(); p.then(() => console.log('promise1')); p.then(() => console.log('promise2')); queueMicrotask(() => console.log('microtask')); console.log('end');
答案:start → end → promise1 → promise2 → microtask → timeout
说明:queueMicrotask与Promise.then同属微任务队列,按注册顺序执行;宏任务最后。
2017年企业特别关注“首屏渲染速度”,JS性能优化成为高频考点:
import()动态导入(ES2020),减少首屏JS体积。真题:如何检测JS性能瓶颈?
答:使用Chrome DevTools的Performance面板录制,关注“Main”线程中JS执行时间、函数调用栈、重排重绘频率。关键指标:Scripting时间占比、Rendering耗时。
2017年安全事件频发,JS层面防护成为加分项:
textContent替代innerHTML)。meta标签或HTTP头限制脚本来源。2017年是模块化方案大融合之年,理解历史脉络助答“为什么用某种方案”:
| 方案 | 特点 | 适用场景 | 2017年使用率 |
|---|---|---|---|
| AMD(RequireJS) | 异步加载,依赖前置声明 | 浏览器端多模块 | 32% |
| CJS(Node.js) | 同步加载,运行时执行 | 服务端模块 | 87% |
| UMD | 兼容AMD/CJS/全局变量 | 通用库分发 | 28% |
| ESM(ES6 Module) | 静态分析,编译时确定依赖 | 现代前端工程 | 61% |
关键点:ESM的import是静态的,不能动态拼接路径;而import()是动态的,返回Promise。
async/await(无需标志位)。optional chaining(?.)纳入Stage 2,为2020年正式提案铺路。这些事件直接影响2017年下半年招聘要求——企业开始要求候选人熟悉async/await和React 16新特性。
async/await模拟(基于Promise)的公司极少(约5%),但要求解释其原理的达78%。重点掌握:1)async函数返回Promise;2)await暂停执行直到Promise完成;3)错误冒泡机制。Foo → Foo.prototype → Object.prototype)。高级岗可能要求解释__proto__与prototype的区别,以及instanceof的实现逻辑。Object.values、Object.entries、String.padStart等ES2017新增API出现频率达41%。建议熟悉ES2015-ES2017常用API。• ES6刚发布,面试题仍以ES5为主
• “闭包”“原型链”为绝对核心
• 异步仅考setTimeout,Promise极少
• 无“模块化”考察
• ES6成为新热点,let/const、箭头函数开始出现
• Promise在阿里、腾讯等大厂面试中占比升至35%
• React/Vue崛起,但问题仍聚焦JS基础(如virtual DOM原理)
• 2017 web前端js面试题全面升级:
- ES6+成为必考(默认参数、解构、rest/spread)
- async/await首次进入大厂面试
- 性能优化(防抖/节流、重排重绘)占比达48%
• 安全问题(XSS/CSRF)从“加分项”变为“门槛项”
• TypeScript普及,JS基础题减少,类型系统考察增加
• Webpack成为工程化必考
• SSR(服务端渲染)问题增多
• Optional Chaining、Nullish Coalescing等新语法进入面试
• PWA、Web Components等前沿技术考察
• 算法题占比上升(LeetCode Easy-Medium)