面试题
大约 8 分钟
面试题
网络相关
安全相关
技术相关
浏览器的帧、requestIdleCallback和requestAnimationFrame详解
Vue 与 react
前端场景相关
计算机基础相关
代码题
CSS相关
JS相关
算法相关
杂记
有效的括号
var isValid = function(s) {
const n = s.length;
if (n % 2 === 1) {
return false;
}
const pairs = new Map([
[')', '('],
[']', '['],
['}', '{']
]);
const stk = [];
for (let ch of s){
if (pairs.has(ch)) {
if (!stk.length || stk[stk.length - 1] !== pairs.get(ch)) {
return false;
}
stk.pop();
}
else {
stk.push(ch);
}
};
return !stk.length;
};
判断this
var length = 10;
function fn() {
return this.length + 1;
}
var obj1 = {
length: 5,
test1: function() {
return fn()
}
}
obj1.test2 = fn;
let res1 = obj1.test1.call()
let res2 = obj1.test1()
let res3 = obj1.test2.call()
let res4 = obj1.test2()
console.log(res1, res2, res3, res4); // 11 11 11 6
原型链判断
Object.prototype.__proto__; //null
Function.prototype.__proto__; //Object.prototype
Object.__proto__; //Function.prototype
Object instanceof Function; //true
Function instanceof Object; //true
Function.prototype === Function.__proto__; //true
比较版本号
function compare(version1, version2) {
let arr1 = version1.split('.'), arr2 = version2.split('.')
const len1 = arr1.length, len2 = arr2.length;
const len = Math.max(len1, len2);
while(arr1.length < len) {
arr1.push(0);
}
while(arr2.length < len) {
arr2.push(0);
}
for (let i = 0; i < len; i++) {
let num1 = parseInt(arr1[i]), num2 = parseInt(arr2[i]);
if (num1 < num2) {
return -1;
} else if (num1 > num2) {
return 1;
}
}
return 0;
}
console.log(compare("0.1", "1.1"));
console.log(compare("1.0.1", "1"));
console.log(compare("7.5.2.4", "7.5.3"));
console.log(compare("1.01", "1.001"));
console.log(compare("1.0", "1.0.0"));
说输出
// ./a.js
let count = 1;
setCount = () => {
count++;
}
setTimeout(() => {
console.log('a', count)
}, 1000);
module.exports = {
count,
setCount
}
//b.js
const obj = require('./a.js');
obj.setCount();
console.log('b', obj.count)
setTimeout(() => {
console.log('b next', obj.count);
}, 2000);
// b 1
// a 2
// b next 1
说输出2
function test(a,b) {
console.log(b)
return {
test:function(c){
return test(c,a);
}
};
}
var retA = test(0);
retA.test(2);
retA.test(4);
retA.test(8);
var retB = test(0).test(2).test(4).test(8);
var retC = test('good').test('bad');
retC.test('good');
retC.test('bad');
// undefined
// 0
// 0
// 0
// undefined
// 0
// 2
// 4
// undefined
// good
// bad
// bad
mul函数
写一个mul函数,使用方法如下:
console.log(mul(2)(3)(4)); // output : 24
console.log(mul(4)(3)(4)); // output : 48
答案直接给出:
function mul (x) {
return function (y) { // anonymous function
return function (z) { // anonymous function
return x * y * z;
};
};
}
mul 返回一个匿名函数,运行这个匿名函数又返回一个匿名函数,最里面的匿名函数可以访问 x,y,z 进而算出乘积返回即可。
对于JavaScript中的函数一般可以考察如下知识点:
- 函数是一等公民
- 函数可以有属性,并且能连接到它的构造方法
- 函数可以像一个变量一样存在内存中
- 函数可以当做参数传给其他函数
- 函数可以返回其他函数
二分查找
// 一般二分
export function search(nums: number[], target: number): number {
let left: number = 0;
let right: number = nums.length - 1;
while (left <= right) {
let middle: number = Math.floor((left + right) / 2);
if (nums[middle] === target) {
return middle;
} else if (nums[middle] < target) {
left = middle + 1;
} else if (nums[middle] > target) {
right = middle - 1;
}
}
return -1
};
// 左右二分
export function searchRange(nums: number[], target: number): number[] {
let left: number = searchBound(nums, target, true);
let right: number = searchBound(nums, target, false);
return [left, right];
};
export function searchBound(nums: number[], target: number, isLeft: boolean): number {
let left: number = 0;
let right: number = nums.length - 1;
let result = -1;
while (left <= right) {
let mid = Math.floor((left + right) / 2);
if (nums[mid] === target) {
result = mid;
if (isLeft) {
right = mid - 1;
} else {
left = mid + 1;
}
} else if (nums[mid] > target) {
right = mid - 1;
} else if (nums[mid] < target) {
left = mid + 1;
}
}
return result;
}
// test
let res = searchRange([5, 7, 7, 8, 8, 10], 8);
console.log(res);
寻找单链表的倒数第 k 个元素
class ListNode {
val: number;
next: ListNode | null;
constructor(val?: number, next?: ListNode | null) {
this.val = (val === undefined ? 0 : val);
this.next = (next === undefined ? null : next);
}
}
// 画图一目了然整个过程
export function findIndex(head: ListNode | null, k: number): ListNode | null {
let fast: ListNode | null;
let slow: ListNode | null;
fast = slow = head;
while ( k-- > 0) {
fast = fast!.next;
}
while (fast !== null) {
fast = fast.next
slow = slow!.next;
}
return slow;
}
寻找无环单链表的中点
class ListNode {
val: number;
next: ListNode | null;
constructor(val?: number, next?: ListNode | null) {
this.val = (val === undefined ? 0 : val);
this.next = (next === undefined ? null : next);
}
}
export function isMiddle(head: ListNode | null): ListNode | null {
let fast: ListNode | null;
let slow: ListNode | null;
fast = slow = head;
// 因为 fast 始终是 slow 的两倍,所以当 fast 走完的时候,slow 刚好是 fast 的一半,即链表中点
while (fast !== null && fast.next !== null) {
fast = fast.next.next;
slow = slow!.next;
}
return slow;
}
有效三角形的个数
let triangleNumber = function(nums) {
if(!nums || nums.length < 3) return 0
let count = 0
// 排序
nums.sort((a, b) => a - b)
for(let k = nums.length - 1; k > 1; k--){
let i = 0, j = k - 1
while(i < j){
if(nums[i] + nums[j] > nums[k]){
count += j - i
j--
} else {
i++
}
}
}
return count
}
浮点数相乘
function mut(num1, num2) {
const numStr1 = num1.toString(), numStr2 = num2.toString();
if (numStr1.indexOf(".") == -1 && numStr2.indexOf(".") == -1)
return num1 * num2;
const [a1, a2 = 1] = numStr1.split(".").map((item) => parseInt(item));
const [b1, b2 = 1] = numStr2.split(".").map((item) => parseInt(item));
let res1 = a1 * b1;
let res2 = a2 * b2;
let res = res1 + "." + res2;
return parseFloat(res);
}
防抖和节流
// 防抖函数
const debounce = (fn, delay) => {
let timer = null;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, args);
}, delay);
};
};
// 节流函数
const throttle = (fn, delay = 500) => {
let flag = true;
return (...args) => {
if (!flag) return;
flag = false;
setTimeout(() => {
fn.apply(this, args);
flag = true;
}, delay);
};
};
转化为驼峰命名
var s1 = "get-element-by-id"
// 转化为 getElementById
复制代码var f = function(s) {
return s.replace(/-\w/g, function(x) {
return x.slice(1).toUpperCase();
})
}
模拟new
new操作符做了这些事:
- 它创建了一个全新的对象
- 它会被执行[[Prototype]](也就是__proto__)链接
- 它使this指向新创建的对象
- 通过new创建的每个对象将最终被[[Prototype]]链接到这个函数的prototype对象上
- 如果函数没有返回对象类型Object(包含Functoin, Array, Date, RegExg, Error),那么new表达式中的函数调用将返回该对象引用
// objectFactory(name, 'cxk', '18')
function objectFactory() {
const obj = new Object();
const Constructor = [].shift.call(arguments);
obj.__proto__ = Constructor.prototype;
const ret = Constructor.apply(obj, arguments);
return typeof ret === "object" ? ret : obj;
}
顺时针打印矩阵
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。
可以模拟打印矩阵的路径。初始位置是矩阵的左上角,初始方向是向右,当路径超出界限或者进入之前访问过的位置时,顺时针旋转,进入下一个方向。
判断路径是否进入之前访问过的位置需要使用一个与输入矩阵大小相同的辅助矩阵 \textit{visited}visited,其中的每个元素表示该位置是否被访问过。当一个元素被访问时,将 \textit{visited}visited 中的对应位置的元素设为已访问。
如何判断路径是否结束?由于矩阵中的每个元素都被访问一次,因此路径的长度即为矩阵中的元素数量,当路径的长度达到矩阵中的元素数量时即为完整路径,将该路径返回。
var spiralOrder = function(matrix) {
if (!matrix.length || !matrix[0].length) {
return [];
}
const rows = matrix.length, columns = matrix[0].length;
const visited = new Array(rows).fill(0).map(() => new Array(columns).fill(false));
const total = rows * columns;
const order = new Array(total).fill(0);
let directionIndex = 0, row = 0, column = 0;
const directions = [[0, 1], [1, 0], [0, -1], [-1, 0]];
for (let i = 0; i < total; i++) {
order[i] = matrix[row][column];
visited[row][column] = true;
const nextRow = row + directions[directionIndex][0], nextColumn = column + directions[directionIndex][1];
if (!(0 <= nextRow && nextRow < rows && 0 <= nextColumn && nextColumn < columns && !(visited[nextRow][nextColumn]))) {
directionIndex = (directionIndex + 1) % 4;
}
row += directions[directionIndex][0];
column += directions[directionIndex][1];
}
return order;
};