fezaoduke / fe-practice-hard Goto Github PK
View Code? Open in Web Editor NEW晚练课
晚练课
题目:
已知有如下 HTML 结构,请编写函数 format
,实现点击对应按钮后对 #str 文本进行加粗、斜体、上标或下标格式化。
提示:请使用 HTML 格式替代方法
<span id="str">儿童节快乐!</span>
<p>
<button onclick="format(1)">加粗</button>
<button onclick="format(2)">斜体</button>
<button onclick="format(3)">上标</button>
<button onclick="format(4)">下标</button>
</p>
参考答案:
function format(type) {
let text = str.innerText || str.textContent;
let result;
switch(type) {
case 1:
result = text.bold();
break;
case 2:
result = text.italics();
break;
case 3:
result = text.sup();
break;
case 4:
result = text.sub();
break;
default:
result = text;
}
str.innerHTML = result;
}
题目:
已知有如下 HTML 结构:
<ol>
<li>第 1 项</li>
<li>第 2 项</li>
<li>第 3 项</li>
<li>第 4 项</li>
</ol>
默认情况下会渲染出一个从1到4的数字序号列表,如将列表第 2 项的 style 设置为 display: none
,则其后面的序号是否会变动?如换成设为 visibility: hidden
呢?为什么?
参考答案:
应用 display: none
的元素不可见、不占据空间、辅助设备无法访问,而且该元素不会被渲染,所以计数器自动跳过第二项,第二项后面的序号从 2 开始递增。
应用 visibility: hidden
的元素虽然在视觉上不可见,但依旧占据布局空间,其计数器仍在运行,不会影响计数器的计数。
类型:常用技巧
难度:★
为 JSON.parse()
方法写一个 polyfill,使其可以兼容低版本浏览器(IE8)
参考答案:
!(window.JSON && window.JSON.parse) && !function(win) {
win.JSON = {
parse: function(str) {
return new Function('return ' + str)()
}
}
}(window);
本期优秀回答者: @AMY-Y
题目:
请使用纯 CSS 实现图片等比缩放最大化匹配父容器尺寸的效果:使下面的图片自适应填充父容器尺寸,同时保持宽高比。
要求:
.box {
width: 300px;
height: 200px;
padding: 10px;
border: 1px solid #ccc;
}
<div class="box">
<img src="https://fezaoduke.github.io/fe-practice-hard/assets/pics/cartoon.png">
</div>
参考答案:
.box > img {
width: 100%;
height: 100%;
object-fit: contain;
}
拓展阅读:object-fit
题目:
请使用 二分搜索算法 编写函数 binarySearch
,实现类似 indexOf
的功能。要求:
/**
* @param {array} arr - 按升序排列的普通数组
* @param {number} target - 需要在 arr 中搜索的目标值
* @return {number} 返回 target 在 arr 中的索引值,如不存在,则返回 -1
*/
function binarySearch(arr, target) {
// 你的代码
}
测试数据:
const test = [1, 3, 8, 9, 27, 34, 128, 275];
binarySearch(test, 128); // 6
binarySearch(test, 30); // -1
参考答案:
function binarySearch(arr, target) {
let startIdx = 0;
let endIdx = arr.length - 1;
let midIdx = Math.floor((startIdx + endIdx) / 2);
while(endIdx - startIdx > 1) {
switch(target) {
case arr[startIdx]: return startIdx;
case arr[endIdx]: return endIdx;
case arr[midIdx]: return midIdx;
}
if (target > arr[midIdx]) { // 右边找
startIdx = midIdx;
} else if (target < arr[midIdx]) { // 左边找
endIdx = midIdx;
}
midIdx = Math.floor((startIdx + endIdx) / 2);
}
return -1;
}
题目:
请使用SVG画一个如下图所示的“西瓜”:
尺寸:200×200
颜色:#E92F34(红)、#32AC1A(绿)
<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
<!-- 请开始你的表演 -->
</svg>
参考答案:
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 208 208">
<circle
fill="#E92F34"
stroke="#32AC1A"
stroke-width="8"
cx="104"
y="100"
r="100"
></circle>
</svg>
题目:
有一个条目数量不定的列表,其 HTML 结构如下:
<ul id="list" style="height: 300px; overflow: auto">
<li>列表项1</li>
<li>列表项2</li>
<li>列表项3</li>
<li>...</li>
<li>列表项n</li>
</ul>
要求:请使用 javascript 写一个表达式,让列表的最后一项默认可见(滚动到可视区域)
参考答案:
document.querySelector('#list > li:last-child').scrollIntoView();
类型:基础知识
难度:★
有以下 html 文件,请问运行后浏览器控制台输出结果是什么?为什么?
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<script>
function fn() {
console.log(1);
}
fn();
function fn() {
console.log(2);
}
fn();
</script>
<script>
function fn() {
console.log(3);
}
fn();
</script>
</body>
</html>
参考答案:
控制台依次输出:2, 2, 3
原因:JavaScript 解释器在执行页面脚本时,是按块执行的。具体来讲,就是当浏览器在解析 HTML 文档时,如果遇到一个 script 标签,则 JavaScript 解释器会等到这个代码块都加载完成后(读取到当前代码块的 </script>
标签),先对代码块进行预编译,然后再执行。执行完毕后,浏览器会继续解析下面的 HTML 文档,同时JavaScript 解释器也准备好处理下一个代码块……
上面代码的执行步骤:
<script></script>
中的代码,下同);fn
函数,后面一个会覆盖前面一个函数的定义);fn()
,故依次输出 2
,2
);3
题外话:如果 script 标签带有 defer
、async
属性,情况有所不同。
相关资料:https://www.jianshu.com/p/bda5266755a2
本期优秀回答者: @AMY-Y
题目:
请编写一个js对象深拷贝的函数,需要考虑循环引用的情况。
参考答案:
function deepCopy(obj) {
// hash表,记录所有的对象的引用关系
let map = new WeakMap();
function dp(obj) {
let result = null;
let keys = Object.keys(obj);
let key = null,
temp = null,
existobj = null;
existobj = map.get(obj);
// 如果这个对象已经被记录则直接返回
if (existobj) {
return existobj;
}
result = {}
map.set(obj, result);
for (let i = 0; i < keys.length; i++) {
key = keys[i];
temp = obj[key];
if (temp && typeof temp === 'object') {
result[key] = dp(temp);
}else {
result[key] = temp;
}
}
return result;
}
return dp(obj);
}
来源:原创题
难度:★
使用纯 CSS 将下面的图片进行菱形剪切。
注:图片不允许拉伸、挤压。实现代码越简单越好。
<!-- html -->
<img src="https://fezaoduke.github.io/fe-practice-hard/assets/pics/cartoon.png">
/* css */
img {
/* 你的代码 */
}
参考效果图:
本期优秀回答者: @liwenkang
题目:
HTTP有哪些方法?具体作用分别是什么?
参考答案:
HTTP1.0 定义了三种请求方法: GET, POST 和 HEAD方法
HTTP1.1 新增了五种请求方法:OPTIONS, PUT, DELETE, TRACE 和 CONNECT
具体作用:
题目:
请编写一个方法 mapArr
,实现与 ES5 规范中数组的 map 方法同样的功能
测试用例:
var data = [1, 2, 3, 4];
var result = data.mapArr(function(it, idx, arr) {
console.log('当前项', it);
console.log('当前索引', idx);
console.log('数组', arr);
return it * 2;
});
console.log(result); // [2, 4, 6, 8]
参考答案:
Array.prototype.mapArr = function(fn, context) {
var arr = Array.prototype.slice.call(this);
var mappedArr = [];
for (var i = 0; i < arr.length; i++) {
mappedArr.push(fn.call(context, arr[i], i, this));
}
return mappedArr;
}
来源:面试题
难度:★★
封装一个函数,接收1个整数参数 n
,返回 斐波那契数列 前 n 项
/**
* @param {number} n - 整数
*/
function fibonacci(n) {
// 你的代码
}
参考答案:
function fibonacci(n) {
var a = 0;
var b = 1;
var c = a + b;
var arr = [c];
for (var i = arr.length; i < n; i++) {
arr.push(c);
a = b;
b = c;
c = a + b;
}
return arr;
}
// 再来个使用递归+ES6实现的(原创)
function fibonacci(n) {
let fn = i => i < 3 ? 1 : fn(i - 1) + fn(i - 2);
return [...Array(n).keys()].map(it => fn(it + 1));
}
本期优秀回答者: @AMY-Y
这是前端晚练课的第 1 期,希望大家在参与前先去「达成以下 2 个成就」:
来源:原创题
难度:★★
封装一个多重排序函数 multiSort
,功能要求:
例如:
['field2', 'field1', 'field3']
首先按
field2
进行排序,如果field2
的值相等,则按field1
排序;如果field1
的值仍相等,则按field3
排序……
实战用例:
根据员工数据,评选出月度最佳员工
[
{ "name": "Sweet", "education": 4, "sales": 10, "duty": 24, "workAge": 2 },
{ "name": "Tough", "education": 3, "sales": 15, "duty": 24, "workAge": 6 },
{ "name": "Yummy", "education": 4, "sales": 10, "duty": 24, "workAge": 4 },
{ "name": "Ghost", "education": 6, "sales": 15, "duty": 23, "workAge": 8 },
{ "name": "Flora", "education": 5, "sales": 15, "duty": 24, "workAge": 6 }
]
按 销售额(sales)、出勤天数(duty)、工龄(workAge)、教育程度(education)
的顺序 降序
排名:
[
{ "name": "Flora", "education": 5, "sales": 15, "duty": 24, "workAge": 6 },
{ "name": "Tough", "education": 3, "sales": 15, "duty": 24, "workAge": 6 },
{ "name": "Ghost", "education": 6, "sales": 15, "duty": 23, "workAge": 8 },
{ "name": "Yummy", "education": 4, "sales": 10, "duty": 24, "workAge": 4 },
{ "name": "Sweet", "education": 4, "sales": 10, "duty": 24, "workAge": 2 }
]
参考答案:
function multiSort(arr, rules, reversal) {
return [...arr].sort((o, p) => {
let a, b;
for (let rule of rules) {
a = o[rule];
b = p[rule];
if (a !== b) {
return (a - b) * (reversal ? -1 : 1);
}
}
});
}
// 或者挂载到 Array 构造函数的原型上
Array.prototype.multiSort = function(rules, reversal) {
return [...this].sort((o, p) => {
// 逻辑代码同上,此处略
});
}
本期优秀回答者: @cnyballk
来源:leetcode
难度:★★★
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:
输入: "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:
输入: "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
本期优秀回答者: @cnyballk
入门级
难度★
请分别写出在浏览器控制台执行以下代码的结果
function b(x,y,a){
arguments[2]=10;
console.log(a);
};
b(1,2,3);
function b(x,y,a){
'use strict'
arguments[2]=10;
console.log(a);
};
b(1,2,3);
来源:原创题
难度:★
封装一个函数,接收一个整数参数 n
,返回 n 以内的随机整数 m (0 <= m < n)
/**
* @param {number} n - 随机数最大范围
*/
function getRandomNum(n) {
// 你的代码
}
参考答案:
// 方法1
function getRandomNum(n) {
return Math.floor(Math.random() * n);
}
// 方法2
function getRandomNum(n) {
return ~~(Math.random() * n);
}
// 方法3
function getRandomNum(n) {
return (Math.random() * n) >>> 0;
}
// 方法4
function getRandomNum(n) {
return Math.random() * n | 0;
}
本期最佳回答者: @AMY-Y
题目:
使用 a 标签分别实现下面的功能:
<img src="images/photo.jpg">
4006-066-066
的界面(手机端)10086
的界面(手机端)[email protected]
的界面参考答案:
<!-- 第 1 题 -->
<a href="images/photo.jpg" download>
<img src="images/photo.jpg">
</a>
<!-- 第 2 题。也可省略中划线,效果都一样 -->
<a href="tel:4006-066-066">4006-066-066</a>
<!-- 第 3 题 -->
<a href="sms:10086">发短信给10086</a>
<!-- 第 4 题 -->
<a href="mailto:[email protected]">[email protected]</a>
题目:
请实现一个按千分位格式化金额数值的功能
function formatMoney(num) {
// 你的代码
}
测试用例:
formatMoney(100); // 100.00
formatMoney(1000); // 1,000.00
formatMoney(1000.56); // 1,000.56
formatMoney(1000000000.25); // 1,000,000,000.25
参考答案:
function formatMoney(num) {
// 字符串捕获(零宽度正预测先行断言)
let regex = /\d{1,3}(?=(\d{3})+$)/g;
// 将字符串拆分成“符号”(如有)、“整数位”和“小数位”三部分
return String(num).replace(/^(-|\+?)(\d+)((\.\d+)?)$/, (s, s1, s2, s3) => (
// $&表示 replace() 函数第一个正则表达式参数所匹配的内容
s1 + s2.replace(regex, '$&,') + s3
));
}
相关资料:正则表达式零宽断言
题目:
<div id="box">
<h2>酱油君1</h2>
<p>酱油君2</p>
<div>
<input type="text">
<button>按钮</button>
</div>
</div>
#box {
position: absolute;
top: 20%;
left: 40%;
width: 300px;
padding: 1em;
background: #f5f5f5;
border: 1px solid #ccc;
}
请编写一个方法,实现点击 #box
(含内部元素)时,浏览器控制台输出 "inner",点击 #box
外部空白处时输出 "outer"
参考答案:
const box = document.getElementById('box');
window.addEventListener('click', e => {
let elm = e.target;
if (elm === box || box.contains(elm)) { // 自身或内部元素
console.log('inner');
} else {
console.log('outter');
}
});
题目:
请实现一个合乎规范的 Promise 方法。
参考答案:
class Promise {
constructor (fn) {
// 三个状态
this.state = 'pending'
this.value = undefined
this.reason = undefined
let resolve = value => {
if (this.state === 'pending') {
this.state = 'fulfilled'
this.value = value
}
}
let reject = value => {
if (this.state === 'pending') {
this.state = 'rejected'
this.reason = value
}
}
// 自动执行函数
try {
fn(resolve, reject)
} catch (e) {
reject(e)
}
}
// then
then(onFulfilled, onRejected) {
switch (this.state) {
case 'fulfilled':
onFulfilled()
break
case 'rejected':
onRejected()
break
default:
}
}
}
/**
* 作者:陈煜仑
* 链接:https://juejin.im/post/5d2ee123e51d4577614761f8
* 来源:掘金
* 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
*/
类型:基础知识
难度:★
实现 isObject(X)
, isString(X)
, isArray(X)
三个方法,用以判断是否为对应类型,返回布尔值
测试用例:
isObject([1, 2]); // false
isString(123); // false
isArray({a: 1}); // false
isObject({a: 1}); // true
isString('123'); // true
isArray([1, 2]); // true
参考答案:
非常有价值的一段代码,类型判断的神码,函数柯里化的入门级教学代码。(来自 seajs 源码)
function isType(type) {
return function(obj) {
return Object.prototype.toString.call(obj) === '[object ' + type + ']'
}
}
var isObject = isType('Object');
var isString = isType('String');
var isArray = Array.isArray || isType('Array');
来源:经典面试题
难度:★
编写一个方法,每隔 1s 依次在控制台打印出 5, 4, 3, 2, 1
参考答案:
请参考 @liwenkang 的答案,很全面。
@Wxh16144 的答案也不错,代码很优雅。
async function display() {
for (let i = 5; i > 0; i--) {
let num = await new Promise(resolve => {
setTimeout(() => {
resolve(i)
}, 1e3)
});
console.log(num)
}
}
display();
本期优秀回答者: @liwenkang
类型:常用技巧
难度:★★☆
封装一个特性检测函数,用以验证当前浏览器是否支持某个 CSS 属性
注:需要兼容 IE8+(含)以及其他主流浏览器
/**
* @param {string} [prop] - 需要验证的 CSS 属性名。
* @return:
1. 当 `prop` 为字符串时,返回当前浏览器是否支持该 CSS 属性的布尔值
2. 当 `prop` 为空时,返回一个由当前浏览器支持的所有 CSS 属性名组成的数组
*/
function supported(prop) {
// 你的代码
}
测试用例: (以下结果来自 Chrome 74)
supported('animation'); // true
supported('background-clip'); // true
supported('mix-blend-mode'); // true
supported('-webkit-user-drag'); // true
supported('-moz-force-broken-image-icon'); // false
supported('-ms-block-progression'); // false
supported('abcd'); // false
supported(); // ["align-content", "align-items", "align-self" ... ]
参考答案:
function supported(prop) {
var result = [];
var format = function(p) {
// 浏览器前缀处理
var prefix = /^([webkit|moz|ms|o]+)[A-Z]+.*/.test(p) ? '-' : '';
return prefix + p.replace(/[A-Z]/g, '-$&').toLowerCase();
};
if (Object.hasOwnProperty('keys') && Object.keys(document.body.style).length) {
result = Object.keys(document.body.style).map(format);
} else {
for (var key in document.body.style) {
result.push(format(key));
}
}
// 兼容 IE8
if (!('indexOf' in Array.prototype)) {
result = String(result);
}
if (typeof prop === 'undefined') {
return typeof result === 'string' ? result.split(',') : result;
}
return result.indexOf(prop) === -1 ? false : true;
}
本期优秀回答者: 无 [哭笑.jpg]
题目:
请写出你所知道的所有 <meta>
标签,例如:
<meta charset="UTF-8"> <!-- 字符集声明 -->
参考答案:
<meta charset="UTF-8"> <!-- 字符集声明 -->
<!-- 视口类 -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="viewport" content="user-scalable=no">
<!-- HTTP 参数类 -->
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <!-- 浏览器渲染版本 -->
<meta http-equiv="cache-control" content="no-cache"> <!-- 缓存控制 -->
<meta http-equiv="expires" content="Sunday 26 October 2016 01:00 GMT"> <!-- 网页过期时间 -->
<meta http-equiv="refresh" content="5;url=http://xx.xx"> <!-- 网页重定向 -->
<meta http-equiv="cleartype" content="on"> <!-- 开启 cleartype,限IE -->
<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests"> <!-- XSS终结者 -->
<!-- 禁止自动转码、翻译 -->
<meta http-equiv="Cache-Control" content="no-transform"> <!-- 进制转码 -->
<meta http-equiv="Cache-Control" content="no-siteapp"> <!-- 进制转码 -->
<meta name="google" value="notranslate"> <!-- 限chrome -->
<!-- SEO 类 -->
<meta name="keywords" content="xxx,xxx,xx">
<meta name="description" content="xxxxxxxx">
<!-- 其他 -->
<meta name="renderer" content="webkit"> <!-- 默认浏览器渲染内核,限360浏览器 -->
<meta name="format-detection" content="telephone=no,email=no,adress=no"> <!-- 禁止电话、邮箱、地址文本自动解析 -->
<meta name="apple-mobile-web-app-capable" content="yes"> <!-- 是否启用 WebApp 全屏模式 -->
<meta name="apple-mobile-web-app-status-bar-style" content="black"> <!-- 上面一条设置启用时生效 -->
题目:
请模拟实现 ES6 中的 Generator 函数
function generator(array) {
// 你的代码
}
测试数据:
const arr = [
{
name: '张三',
age: 18
}, {
name: '李四',
age: 22
}, {
name: '王五',
age: 30
}
];
const it = generator(arr);
console.log(it.next()); // {value: {age: 18, name: "张三"}, done: false}
console.log(it.next()); // {value: {age: 22, name: "李四"}, done: false}
console.log(it.next()); // {value: {age: 30, name: "王五"}, done: false}
console.log(it.next()); // {value: undefined, done: true}
参考答案:
function generator(array) {
let nextIndex = 0;
return {
next: function() {
return nextIndex < array.length ?
{value: array[nextIndex++], done: false} :
{value: undefined, done: true};
}
}
}
题目:
请编写函数 getAttributes
,接收一个 DOM 节点作为参数,输出该节点的所有属性。
测试用例:
<a class="btn btn-link" id="demo" href="page.html" title="页面" role="link">链接</a>
var demo = document.getElementById('demo');
getAttributes(demo);
/**
* 返回数据:
{
class: "btn btn-link",
id: "demo",
href: "page.html",
title: "页面",
role: "link"
}
*/
function getAttributes(el) {
// 你的代码
}
参考答案:
function getAttributes(el) {
var result = {};
[].slice.call(el.attributes).forEach(function(attr) {
result[attr.name] = attr.value;
});
return result;
}
来源:原创题
难度:★★
封装一个函数,接收一个整数参数 n
,返回当天前 n 天的日期(不含当天)
/**
* @param {number} n - 天数
* @return {array} 日期数组,格式:[yyyy-MM-dd, yyyy-MM-dd]
*/
function getPastDays(n) {
// 你的代码
}
测试用例:
getPastDays(7); // 获取前 7 天的日期(今天是:2019年5月13日)
// 运行返回数据:
["2019-05-06", "2019-05-07", "2019-05-08", "2019-05-09", "2019-05-10", "2019-05-11", "2019-05-12"]
参考答案:
function getPastDays(n) {
// 非整数或负数,返回空数组
if (!Number.isInteger(n) || n < 0) {
return [];
}
const oneDay = 24 * 3600 * 1e3; // 将1天时间换算成UTC毫秒数
const preZero = num => num < 10 ? `0${num}` : num; // 1位数前置补零
return (
[...Array(n).keys()]
.map(days => new Date(Date.now() - oneDay * (days + 1)))
.map(day => `${day.getFullYear()}-${preZero(day.getMonth() + 1)}-${preZero(day.getDate())}`)
.reverse() // 反转数组,按从远到近的日期顺序
);
}
本期优秀回答者: @liwenkang @Wxh16144
题目:
有 4 个复选框(checkbox)和一个 button 按钮,点击 button 按钮可实现复选框的全选和反选功能
题目:
JavaScript 中为 DOM 元素绑定事件可使用 addEventListener
来实现,但与之对应的事件移除方法 removeEventListener
却没有那么好用,例如它无法移除绑定的匿名方法。
请结合下面的代码,实现按钮点击一次后,移除自身绑定的匿名方法的功能。
var btn = document.createElement('button');
btn.innerText = '按钮';
btn.addEventListener('click', function() {
btn.innerText = '我被点击了-' + (+new Date());
});
document.body.appendChild(btn);
参考答案:
window.HTMLElement && !function() {
HTMLElement.prototype.addListener = function(type, fn, capture) {
var el = this;
fn = typeof fn === 'function' ? fn : function(){};
var bind = function() {
el.addEventListener(type, fn, capture);
return {
removeListener: function() {
el.removeEventListener(type, fn);
}
}
};
return bind();
}
}();
var btn = document.createElement('button');
btn.innerText = '按钮';
var evt = btn.addListener('click', function() {
btn.innerText = '我被点击了-' + (+new Date());
evt.removeListener();
});
document.body.appendChild(btn);
来源:经典算法题
难度:★★
求一个给定数组中的最大数、最小数
/**
* @param {array} arr - 给定的数组,数组每项为整数
* @return {array} 返回最大数最小数组成的数组,格式:[max, min]
*/
function findMaxMin(arr) {
// 你的代码
}
参考答案: 请参阅 @liwenkang 清脆的回答。
另外,如果需要兼容 ES5,可以写成 Math.max.apply(0, arr)
Math.min.apply(0, arr)
本期优秀回答者: @liwenkang
题目:
请编写一个方法 reduceArr
,实现与 ES5 规范中数组的 reduce 方法同样的功能
相关: 第 22 期(ECMAScript-原型链 继承):实现 Array map
测试用例:
var data = [1, 2, 3, 4];
var result1 = data.reduceArr(function(total, cur, idx, arr) {
return total + cur;
});
var result2 = data.reduceArr(function(total, cur, idx, arr) {
return total + cur;
}, 100);
console.log(result1); // 10
console.log(result2); // 110
参考答案:
Array.prototype.reduceArr = function(fn, initialVal) {
var arr = this;
var result = arr[0];
var startIdx = 1;
if (typeof initialVal !== 'undefined') {
result = initialVal;
startIdx = 0;
}
for (var i = startIdx; i < arr.length; i++) {
result = fn(result, arr[i], i, arr);
}
return result;
};
题目:
汉诺塔是一个益智游戏,塔的设备包括三根柱子和一套直径不一的空心圆盘。开始时源柱子上的所有圆盘都按照较小的圆盘放在较大的圆盘之上的顺序堆叠。目标是通过每次移动一个圆盘到另一根柱子上,最终将一堆圆盘移动到目标柱子上,过程中不可将大的圆盘放置在较小的圆盘之上。
请使用 js 编写汉诺塔算法。(在浏览器控制台输出解法)
/**
* @param {number} n - 圆盘数量
* @param {string} A - 源柱子名称
* @param {string} B - 辅助柱子名称
* @param {string} C - 目标柱子名称
*/
function hanoi(n, A, B, C) {
// 你的代码
}
测试用例:
hanoi(3, 'A', 'B', 'C');
// 控制台输出:
/*
Move disc 1 from A to C
Move disc 2 from A to B
Move disc 1 from C to B
Move disc 3 from A to C
Move disc 1 from B to A
Move disc 2 from B to C
Move disc 1 from A to C
*/
参考答案:
function hanoi(n, A, B, C) {
if (n > 0) {
hanoi(n - 1, A, C, B);
console.log(`Move disc ${n} from ${A} to ${C}`);
hanoi(n - 1, B, A, C);
}
}
题目:
请结合下述代码,实现如下图所示的响应式布局效果:
要求:
.container {
}
.container > div {
text-align: center;
font-size: 32px;
line-height: 50px;
color: #fff;
}
.container > div:nth-child(1) {background: #ADF2B7;}
.container > div:nth-child(2) {background: #FEE87A;}
.container > div:nth-child(3) {background: #5DFDFB;}
.container > div:nth-child(4) {background: #E5B5FC;}
.container > div:nth-child(5) {background: #8DFCCA;}
.container > div:nth-child(6) {background: #FE985F;}
参考答案:
.container {
display: grid;
grid-gap: 5px;
grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
grid-template-rows: repeat(2, 50px);
}
.container > div {
text-align: center;
font-size: 32px;
line-height: 50px;
color: #fff;
}
.container > div:nth-child(1) {background: #ADF2B7;}
.container > div:nth-child(2) {background: #FEE87A;}
.container > div:nth-child(3) {background: #5DFDFB;}
.container > div:nth-child(4) {background: #E5B5FC;}
.container > div:nth-child(5) {background: #8DFCCA;}
.container > div:nth-child(6) {background: #FE985F;}
题目:
以下面的数据为参考,编写一个抽奖算法,其中 chance
字段为当前奖品的获奖概率(0.01-1)
const awards = [
{name: '智能平衡车', chance: 0.12},
{name: '华为 P30 Pro手机', chance: 0.06},
{name: '蓝牙手环', chance: 0.3},
{name: '100元购物卡', chance: 0.5},
{name: 'Mac Book Pro', chance: 0.02}
];
参考答案:
初步思路:将所有奖品的概率转换为百分数(即 chance * 100
),然后按照各自的占比生成一个100长度的数组:
然后从1-100生成一个随机数,看落在哪个区间即可确定抽奖结果。
进阶:上面的思路空间复杂度较高,通过观察可以发现,生成的5个概率区间有5个临界参考点,即 12, 18, 48, 98, 100
,换句话说,我们只需将获取的随机数按这些参考点依次进行比较,找到第一个比随机数大的参考点即可确定结果,而无需划分100长度的数组。
let arr = awards.map(it => ({ name: it.name, chance: it.chance * 100}))
.map((it, idx, array) => {
if (idx > 0) {
it.chance = it.chance + array[idx - 1].chance; // 累加上一个临界参考点
}
return it;
});
// 生成 1-99 随机数
let rdmNum = Math.floor(Math.random() * 99) + 1;
for (let award of arr) {
if (award.chance > rdmNum) {
console.log(`你抽中了 ${award.name}`);
break;
}
}
参考资料:Alias Method离散分布随机取样
题目:
请根据提供的 html 代码实现下述效果:
Alt + H
,控制台输出对应信息;Alt + Y
选中“同意”单选框;Alt + N
选中“不同意”单选框。<button onclick="console.log('这是帮助信息')">帮助信息</button>
<label>
<input type="radio" name="choice"> 同意
</label>
<label>
<input type="radio" name="choice"> 不同意
</label>
参考答案:
<button accesskey="h" onclick="console.log('这是帮助信息')">帮助信息</button>
<label accesskey="y">
<input type="radio" name="choice"> 同意
</label>
<label accesskey="n">
<input type="radio" name="choice"> 不同意
</label>
基础
★
1 请写出四种无返回值的四种语句
2 判断在浏览器控制台执行以下代码的结果,同时写出原因
eval('1+2;var x=5;;;;;;function fn(){};');
eval('1+2;var x=5;;;;;;function fn(){};void 0');
题目:
已知后端某接口可能返回的数据结构如下:
// 正常情况:
{
"status": 200,
"msg": "ok",
"response": {
"data": {
"total": 2,
"pageSize": 10,
"lists": [
{ "id": 1, "name": "hello" },
{ "id": 2, "name": "world" }
]
},
"keyword": "hello world"
}
}
// 请求异常:
{
"status": 400,
"msg": "bad request",
"response": null
}
// 空数据:
{
"status": 200,
"msg": "ok",
"response": {
"data": {
"total": 0,
"pageSize": 10,
"lists": null
},
"keyword": "hack"
}
}
前端需要获取返回数据中的 response.data.lists
来渲染业务页面(伪代码如下),请编写一个通用函数来改善逐级判断属性值的问题。
axios.get('/list?keyword=xxx')
.then(function(res) {
// 为了防止报错,而不得不逐级判断属性值的有效性
if (res.response && res.response.data && res.response.data.lists) {
render(res.response.data.lists);
}
});
/**
* @param {object | array} data - 源数据
* @param {string} fieldRoute - 字段名路径,如 a.b.c
*/
function getDataBy(data, fieldRoute) {
// 你的代码
}
测试数据:
// 正常情况:
getDataBy(res, 'response.data.lists'); // [{"id": 1, "name": "hello"}, {"id": 2, "name": "world"}]
// 请求异常:
getDataBy(res, 'response.data.lists'); // null
// 空数据:
getDataBy(res, 'response.data.lists'); // null
getDataBy(null, 'response.data.lists'); // null
getDataBy([{id: 1}, {id: 2}, {id: 3}, {id: 4}], 1); // {id: 2}
参考答案:
function getDataBy(data, fieldRoute) {
if (fieldRoute == undefined) { // undefined | null
return data
}
var fields = String(fieldRoute).split('.');
return fields.reduce(function(obj, field) {
if (obj) {
return obj[field]
} else {
return null
}
}, data);
}
题目:
请编写一个名为 ImageLoader
的图片加载器,接收一个数组或字符串参数,包含图片的 URL 地址,当所有图片加载完毕时,触发 onReady
回调。
测试用例:
const imgUrls = [
'https://cdn.pixabay.com/photo/2019/04/29/14/50/camogli-4166255__340.jpg',
'https://cdn.pixabay.com/photo/2019/06/24/15/31/forest-4296305__340.jpg',
'https://cdn.pixabay.com/photo/2019/06/27/03/42/beach-4301479__340.jpg',
'https://cdn.pixabay.com/photo/2019/06/19/23/13/plum-blossoms-4285819__340.jpg',
'https://cdn.pixabay.com/photo/2019/05/19/23/47/clouds-4215608__340.jpg',
'https://cdn.pixabay.com/photo/2018/10/18/11/29/waterfowl-3756126__340.jpg'
];
const loader = new ImageLoader(imgUrls);
loader.onReady = () => {
console.log('图片全部加载完毕');
}
参考答案:
class ImageLoader {
constructor(urls) {
this.cache = {};
if (Array.isArray(urls)) {
urls.forEach(url => this.load(url));
} else {
this.load(urls);
}
}
load(url) {
if (this.cache[url]) {
return this.cache[url];
}
let img = new Image();
img.addEventListener('load', _ => {
this.cache[url] = img;
if (this.isReady()) {
this.onReady();
}
});
this.cache[url] = false;
img.src = url;
}
isReady() {
for (let k in this.cache) {
if (!this.cache[k]) {
return false;
}
}
return true;
}
onReady(fn) {
typeof fn === 'function' && fn();
}
}
晚练课已经开了 16 期了,并没有达到预期效果,虽然其中有一些客观因素,但更多的是我在主观方面没有做好前期的规划。
从之前 16 期整体来看,这种东一榔头西一棒槌的做题练习模式知识点太过零散,如果只是做题,还不如直接去 LeetCode,另外 GitHub 上也有不少通过 issue 练题的同质化 repo,与之相比,晚练课目前的开展模式没有多少优势可言。如果要继续开展下去就必须另寻思路,找到差异化细分的切入点,这个切入点,以我个人之见,应该是 “刻意练习” 。
那么问题来了,为什么要去刻意练习?因为随着前端技术的日新月异,前端工程师所需要学的知识体量越来越大了,为“提高”学习效率,我们往往追求快餐式、碎片化的学习方式,这种浅层的学习方式,不足以让知识系统地转化为技能,所以当工作中遇到实际问题,我们经常介于似懂非懂之间,需要翻查各种资料才能解决。要改善这种“一学就会,一写就跪”的情况,光靠学是不够的,还应当进行有针对性的刻意练习,锻炼出“代码肌肉”(有个叫一万小时理论的怎么说得来着?)。
刻意练习些什么呢?应该是专业前端工程师所必备的技能点。我最近收集(骗取)了许多培训班的资料,看了他们的学习线路图和知识体系,很受启发,我觉得可以参照这些知识体系,组织系统化的刻意练习,我已经在做这方面的准备了,请大家拭目以待。
最后欢迎大家积极参与,不断驱动自己去提升,让自己的能力变得更强,早日成为前端专家。
基础篇
难度 ★
//浅拷贝
请写出下列代码的输出结果,为什么?
//1.
var a = 2 ;
var a_copy = a ;
a_copy = 3;
console.log(a);//?
//2.数组1
var arr = [1,2,3,4];
var arr_copy = arr ;
arr_copy.push(5);
console.log(arr);//?
//3.数组2
var arr = [1,2,3,4];
var arr_copy = arr ;
arr_copy = [1,2,3,4,5];
console.log(arr);//?
console.log(arr_copy);//?
//4.对象1
var obj = {
name:'sunshine',
age:18
};
var obj_copy = obj ;
obj_copy.name = '过客';
console.log(obj.name);//?
//5.对象2
var obj = {
name:'sunshine',
age:18
};
var obj_copy = obj ;
obj_copy = {
name:'过客',
age:18
};
console.log(obj.name);//?
console.log(obj_copy.name);//?
题目:
编写一个名为 getAlphabet
的函数,返回一个由 a 到 z 26个字母组成的数组。
参考答案:
思路:利用 ASCⅡ 表中字母编码是连续的这个特点,获得字母 a 的编码作为起始位置,然后累加编号,从而得到字母表。
// 方法1
function getAlphabet() {
return [...Array(26).keys()].map((_, idx) => String.fromCharCode(97 + idx));
}
// 方法2
function getAlphabet() {
return new Array(26).fill().map((_, idx) => String.fromCharCode(97 + idx));
}
// 方法3
function getAlphabet() {
return Array.from({length: 26)).map((_, idx) => String.fromCharCode(97 + idx));
}
// 方法4
function getAlphabet() {
var len = 26;
var result = [];
while(len) {
result.push(String.fromCharCode(97 + (26 - len)));
len--;
}
return result;
}
鄙人昨日夜观天象,但见紫气东凝、七斗璨谧,遂遍查玄学典籍,方知此象为明日大吉之兆!故择明日(5月8号)为本课开启吉日,望诸位赏光莅临!:tada: :tada: :tada:
第 [n] 期([日期]):[标题名称]
第 1 期(2019-05-08):数组的九种写法
格式:
来源:[ 原创题 | 经典算法题 | 大厂面试题 | 其他 …… ]
难度:[ 入门 | 简单 | 中等 | 困难 | 噩梦 ]
/* 题目内容巴拉巴拉…… 要求语言简洁明了,将题意阐述清楚,避免歧义 */
示例:
来源:原创题
难度:★★★★javascript 的数组有九种写法,请依次写出来。
入门(★):基础型的入门题目,答题者了解相关基础知识即可作答;
示例:javascript 中的基本类型有哪些?
简单(★★):将基础知识实际运用的题目,答题者需要对相关基础知识有一定的理解和认识;
示例:将一个对象数组按给定的字段名进行排序
中等(★★★):将基础知识和相关技巧灵活应用的题目,答题者需要扎实的基础功底;
示例:用 ES5 实现
Promise
困难(★★★★):考察底层原理、逻辑思维类的高阶题目,答题者需要清晰的逻辑思维能力和一定的技术知识深度;
示例:不使用递归,中序遍历一个二叉树
噩梦(★★★★★):请自重
示例:面试官:请你清唱一首《忐忑》
使用 markdown 中的代码块语法来插入代码(GitHub 会自动解析并高亮代码);
```js // 这里是 javascript 代码内容 ```
```html // 这里是 HTML 代码内容 ```
```css // 这里是 CSS 代码内容 ```
注意代码块的缩进距离,起始位置从左边缘开始。
~~ 期待大家的参与 ~~
题目:
在网页中,连续英文单词文本默认是不换行的,所以当容器宽度不够显示时,文本内容会溢出。我们通常的做法是使用 word-break:break-all
或word-wrap:break-word
,但这种强制换行的方式具有不可控性,很容易破坏单词的可读性。请尝试解决这个问题。
测试内容如下:
<p>
https://juejin.im/search?query=requestAnimationFrame
</p>
要求:容器宽度足够时,整体一行显示;容器宽度不足时,文本内容自动换行,文本中的单词不会被截行。
参考答案:
https://<wbr>juejin.im<wbr>/search?<wbr>query=<wbr>requestAnimationFrame
<wbr> 是一个神奇的标签,可以实现精确换行:当容器宽度不够时,在 <wbr> 这里换行;宽度足够,还是一行显示,非常智能。
题目:
请使用纯 CSS 实现 .btn
按钮的 loading 动画效果,效果如下图:
相关的 CSS 和 HTML 已写好:
.btn {
display: inline-block;
padding: .5em 1.2em;
font-size: 14px;
color: #fff;
background: #369;
border: 0;
border-radius: 5px;
}
/* 请补充 loading 动画的 CSS 实现 */
<button class="btn">确定</button>
<button class="btn loading">确定</button>
参考答案:
.btn.loading {position: relative;}
.btn.loading:first-line {color: transparent;}
.btn.loading::before {
content: "";
position: absolute;
top: 0; right: 0; bottom: 0; left: 0;
width: 4px;
height: 4px;
margin: auto;
animation: spinZoom 1s steps(8) infinite;
border-radius: 100%;
box-shadow: 0 -10px 0 1px currentColor,
10px 0 currentColor,
0 10px currentColor,
-10px 0 currentColor,
-7px -7px 0 .5px currentColor,
7px -7px 0 1.5px currentColor,
7px 7px currentColor,
-7px 7px currentColor;
}
@keyframes spinZoom {
0% {
transform: scale(.75) rotate(0);
}
100% {
transform: scale(.75) rotate(360deg);
}
}
题目:
以下代码运行后输出什么?为什么?
console.log(Object.getPrototypeOf(Object.prototype));
参考答案:
null
JavaScript 规定,所有对象都有自己的原型对象(prototype)。一方面,任何一个对象,都可以充当其他对象的原型;另一方面,由于原型对象也是对象,所以它也有自己的原型。因此,就会形成一个“原型链”(prototype chain):对象到原型,再到原型的原型……
如果一层层地上溯,所有对象的原型最终都可以上溯到 Object.prototype,即 Object 构造函数的 prototype 属性。也就是说,所有对象都继承了Object.prototype 的属性。这就是所有对象都有 valueOf 和 toString 方法的原因,因为这是从Object.prototype 继承的。
那么,Object.prototype 对象有没有它的原型呢?有,Object.prototype 的原型是 null。null 没有任何属性和方法,也没有自己的原型。因此,原型链的尽头就是 null。
类型:高频面试题
难度:★★
请编写一个数组去重函数(用 ES5、ES6 各写一个)
function removeRepeat(arr) {
// 你的代码
}
参考答案:
// 对象属性法
// 注意:[1, '1'] 形式的数组会产生误差,因为 hash[1] === hash['1']
function removeRepeat(arr) {
var result = [],
hash = {};
for (var i = 0, elem; i < arr.length; i++) {
elem = arr[i];
if (!hash[elem]) {
result.push(elem);
hash[elem] = true;
}
}
return result;
}
// 指针查询法
function removeRepeat(arr) {
var result = [];
arr.map(function(item, idx, array) {
if (array.indexOf(item) === idx) {
result.push(item);
}
});
return result;
}
// 数组反查法
function removeRepeat(arr) {
var result = [];
for (var i = 0; i < arr.length; i++) {
if (!~result.indexOf(arr[i])) {
result.push(arr[i]);
}
}
return result;
}
// 过滤法
function removeRepeat(arr) {
return arr.filter(function(item, idx, array) {
return array.indexOf(item) === idx;
});
}
// ES6 Set 去重法
function removeRepeat(arr) {
return [...new Set(arr)];
}
// 执行效率排行(从高到低):
// 对象属性法 > ES6 Set 去重法 > 数组反查法 > 过滤法 > 指针查询法
本期优秀回答者: @AMY-Y
题目:
已知如下数据:
let country = {
name: 'China',
language: 'Chinese',
population: {
value: 14,
unit: '亿'
}
};
请使用 JSON.stringify
将其转化为以下格式的字符串:
--"name": "China",
--"language": "Chinese",
--"population": "14 亿"
参考答案:
JSON.stringify(country, (k, v) => {
if (k === 'population') {
return `${v.value} ${v.unit}`;
}
return v;
}, '--').replace(/\{|\}/g, '');
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.