几道JS代码手写面试题
(1) 高阶段函数实现AOP(面向切面编程)
Function.prototype.before = function (beforefn) {
let _self = this; // 缓存原函数的引用
return function () { // 代理函数
beforefn.apply(this, arguments); // 执行前置函数
return _self.apply(this, arguments); // 执行原函数
}
}
Function.prototype.after = function (afterfn) {
let _self = this;
return function () {
let set = _self.apply(this, arguments);
afterfn.apply(this, arguments);
return set;
}
}
let func = () => console.log('func');
func = func.before(() => {
console.log('===before===');
}).after(() => {
console.log('===after===');
});
func();
输出结果:
===before===
func
===after===
斐波那契数列
斐波那契数列从第三项开始,每一项都等于前两项之和。指的是这样一个数列:0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144 …
得到数列中第n项指向的数值是多少
1.递归
function fib(n) {
if (n === 1 || n === 2) return n - 1;
return fib(n - 1) + fib(n - 2)
}
console.log(fib(10)); // 34
时间复杂度为O(2^n)
2.非递归
function fib(n) {
let a = 0;
let b = 1;
let c = a + b;
for (let i = 3; i < n; i++) {
a = b;
b = c;
c = a + b;
}
return c;
}
console.log(fib(10)); // 34
时间复杂度为O(n)
一道关于Array push的JS题
// 类数组
let obj = {
'1': 'a',
'2': 'b',
length: 2
};
Array.prototype.push.call(obj, 'c');
console.log(obj); // { '1': 'a', '2': 'c', length: 3 }
push和pop实现
Array的push底层实现是依赖于 数组的length属性的
Array.prototype.push2 = function(){
this.splice.apply(this, [this.length, 0].concat(Array.prototype.slice.apply(arguments)));
return this.length;
}
对于 pop也是一样
Array.prototype.pop2 = function(){
return this.splice(this.length - 1, 1)[0];
}
算法题
1.在一个数组中 找出里面其中两项相加后的和为num,如果存在就返回两个数的索引位置,否则false
function fn(num = 0, ary = []) {
for (let i = 0; i < ary.length; i++) {
let diff = num - ary[i];
let diffIndex = ary.indexOf(diff);
if (diffIndex !== -1) {
return [i, diffIndex];
}
}
return false;
}
let num = 3;
let arr = [-1, 4, 6, 2];
console.log(fn(num, arr)); // [0, 1]
2. 将两个有序数组合并为一个排好序的大数组
function mergeAry(left = [], right = []) {
const result = [];
while (left.length && right.length) {
result.push(left[0] <= right[0] ? left.shift() : right.shift());
}
return result.concat(left, right);
}
console.log(mergeAry([1, 2, 3], [1, 4, 8, 9])); // [ 1, 1, 2, 3, 4, 8, 9 ]
字符串repeat实现
// 原生repeat
'ni'.repeat(3); // 'ninini'
// 实现一
String.prototype.repeatString1 = function (n) {
return Array(n + 1).join(this);
}
console.log('ni'.repeatString1(3));
// 实现二
String.prototype.repeatString2 = function (n) {
return Array(n).fill(this).join('');
}
console.log('ni'.repeatString2(3));
当我们 new 一个类的时候 都发生了什么
/**
* new2 new关键字的代码实现演示
* @param {function} func 被new的类 (构造函数)
*/
function new2(func) {
// 创建了一个实例对象 o,并且这个对象__proto__指向func这个类的原型对象
let o = Object.create(func.prototype);
// (在构造函数中this指向当前实例)让这个类作为普通函数值行 并且里面this为实例对象
let k = func.call(o);
// 最后再将实例对象返回 如果你在类中显示指定返回值k,
// 注意如果返回的是引用类型则将默认返回的实例对象o替代掉
return typeof k === 'object' ? k : o;
}
// 实验
function M() { // 即将被new的类
this.name = 'liwenli';
}
let m = new2(M); // 等价于 new M 这里只是模拟
console.log(m instanceof M); // instanceof 检测实例
console.log(m instanceof Object);
console.log(m.__proto__.constructor === M);
this/bind
let obj = { a: 1};
function fn() {
this.b = 100;
return this.a;
}
let fe = fn.bind(obj);
console.log(fe()); // 1 里面this是obj
console.log(obj); // { a: 1, b: 100 }
console.log(new fe()); // 里面this是当前创建实例对象 { b: 100 }
Object.create 兼容实现
let obj1 = {id: 1};
Object._create = (o) => {
let Fn = function() {}; // 临时的构造函数
Fn.prototype = o;
return new Fn;
}
let obj2 = Object._create(obj1);
console.log(obj2.__proto__ === obj1); // true
console.log(obj2.id); // 1
// 原生的Object.create
let obj3 = Object.create(obj1);
console.log(obj3.__proto__ === obj1); // true
console.log(obj3.id); // 1
一道面试题
解法一:
function CodingMan(name) { // 主要考察的是 面向对象以及JS运行机制(同步 异步 任务队列 事件循环)
function Man(name) {
setTimeout(() => { // 异步
console.log(`Hi! This is ${name}`);
}, 0);
}
Man.prototype.sleep = function(time) {
let curTime = new Date();
let delay = time * 1000;
setTimeout(() => { // 异步
while (new Date() - curTime < delay) {} // 阻塞当前主线程
console.log(`Wake up after ${time}`);
}, 0);
return this;
}
Man.prototype.sleepFirst = function(time) {
let curTime = new Date();
let delay = time * 1000;
while (new Date() - curTime < delay) {} // 阻塞当前主线程
console.log(`Wake up after ${time}`);
return this;
}
Man.prototype.eat = function(food) {
setTimeout(() => { // 异步
console.log(`Eat ${food}~~`);
}, 0)
return this;
}
return new Man(name);
}
// CodingMan('Peter');
// CodingMan('Peter').sleep(3).eat('dinner');
// CodingMan('Peter').eat('dinner').eat('supper');
// CodingMan('Peter').sleepFirst(5).eat('supper');
解法二:
function CodingMan(name) {
function fe() {
fe.flag = true;
console.log(`Hi! This is ${name}`);
}
fe.flag = false;
fe.timer = setTimeout(() => {
clearTimeout(fe.timer);
if (!fe.flag) fe();
}, 0);
return fe;
}
Function.prototype.sleep = function (delay) {
this()
this.await(delay);
return this;
}
Function.prototype.sleepFirst = function (delay) {
this.await(delay);
this();
return this;
}
Function.prototype.eat = function (dinner) {
setTimeout(() => {
console.log(`Eat ${dinner}~`);
});
return this;
};
Function.prototype.await = function (delay) {
delay = isNaN(delay) ? 0 : delay;
let startTime = new Date();
let delayTime = delay * 1000;
while (new Date() - startTime <= delayTime) {
}
console.log(`Wake up after ${delayTime}ms`);
}
// CodingMan('peter')
// CodingMan('peter').sleep(2).eat('hanbao');
// CodingMan('peter').sleepFirst(2).eat('hanbao');
CodingMan('peter').eat('haha').eat('hanbao');
currying 函数柯理化
bind 柯理化
function add(a, b, c) {
return a + b + c;
}
let f1 = add.bind(undefined, 100);
console.log(f1(2, 3)); // 105 = 100 + 2 + 3
let f2 = f1.bind(undefined, 200);
console.log(f2(3)); // 303 = 100 + 200 + 3
curry 柯理化的实现(递归调用 + valueOf)
知识点:对象的valueOf浏览器环境下 隐式转化为基本数据类型(一元操作符+object/字符串拼接 ”+object)时,会调用自身的valueOf方法取值,如果自身也存在toString方法 先调用valueOf 如果valueOf返回值不是基本数据类型 则调用toString, toString的返回值也不是基本数据类型就报错。
valueOf调用场景
let obj = { x: 1, y: 2 };
obj.toString = function() {
return this.x + this.y;
}
obj.valueOf = function() {
return this.x + this.y + 100;
}
// 以下情况下自身valueOf被调用
console.log('' + obj); // 103
console.log(+obj); // 103
function fn() {};
fn.valueOf = () => console.log('valueof');
console.log(fn); // valueof
const mul = x => {
const result = y => mul(x * y); // 递归调用mul
result.valueOf = () => x;
return result;
}
console.log(mul(2)(3)); // 6
// 在上面mul每执行一次,就会返回一个valueOf被改写后的新函数result 并且result执行会在里面调用mul(x * y)
// 在result函数的valueOf里保存着 由上一次x * y 传进来的结果x, 也就是上一次x*y 会作为这一次的输出 依然叫x
// 第一次mul(2) 此时 x为2 return result
result 为 result = y => mul(2 * y);
// 第二次 mul(2)(3) 等价于 第一个mul返回的result(3), result执行 => mul(2 * 3) 再次调用mul 将2*3 = 6 的结果作为mul参数
// 最后mul(6) x = 6 在返回一个新函数result 此时result的valueOf = () => 6
// log(mul(2)(3)) 相当于log的最后返回的result 隐式调用valueOf 返回 6
curry 将多参数函数转换为接收单一参数的函数
function fe(a, b, c) {
return a + b + c;
}
function curry(fe) {
let args = []; // 参数集合
let len = args.length;
return function bar() {
args = [...args, ...arguments]; // 收集参数
if (args.length >= fe.length) {
return fe.apply(this, args);
}
return bar;
}
}
console.log(curry(fe)(1)(2)(3)); // 6
currying 部分求值
// currying 函数柯理化
let currying = function(fn) {
let args = [];
return function fe() {
if (arguments.length === 0) {
return fn.apply(this, args);
}
[].push.apply(args, arguments);
return fe;
}
}
let count = currying(function (...rest) {
return rest.reduce((prev, cur) => prev + cur, 0);
});
console.log(count(100)(200)(10)()); // 310
收集参数 延迟执行 到达指定次数才执行
// 参数收集 指定次数后执行
function fn(...rest) {console.log(rest);};
function after(fn, time = 1) {
let params = [];
return function(...rest) {
params = [...params, ...rest];
if (--time === 0) {
fn.apply(this, params);
}
}
}
let newFn = after(fn, 3); // 执行3次 内部fn才会执行
newFn(2);
newFn(3);
newFn(4);
函数节流
throttle 策略的电梯。保证如果电梯第一个人进来后,50毫秒后准时运送一次,不等待。如果没有人,则待机。
let throttle = (fn, delay = 50) => { // 节流 控制执行间隔时间 防止频繁触发 scroll resize mousemove
let stattime = 0;
return function (...args) {
let curTime = new Date();
if (curTime - stattime >= delay) {
fn.apply(this, args);
stattime = curTime;
}
}
}
函数节流安全版
/**
* @desc 函数防抖
* @param func 函数
* @param wait 延迟执行毫秒数
* @param immediate true 表立即执行,false 表非立即执行
*/
function debounce(func,wait,immediate) {
var timeout;
return function () {
var context = this;
var args = arguments;
if (timeout) clearTimeout(timeout);
if (immediate) {
var callNow = !timeout;
timeout = setTimeout(function(){
timeout = null;
}, wait)
if (callNow) func.apply(context, args)
}
else {
timeout = setTimeout(function(){
func.apply(context, args)
}, wait);
}
}
}
防抖动
debounce 策略的电梯。如果电梯里有人进来,等待50毫秒。如果又人进来,50毫秒等待重新计时,直到50毫秒超时,开始运送。
let debounce = (fn, time = 50) => { // 防抖动 控制空闲时间 用户输入频繁
let timer;
return function (...args) {
let that = this;
clearTimeout(timer);
timer = setTimeout(fn.bind(that, ...args), time);
}
}
深度拷贝兼容写法(不包括原型属性)
function deepCopy(obj) {
if (typeof obj !== 'object') return obj;
if (typeof window !== 'undefined' && window.JSON) { // 浏览器环境下 并支持window.JSON 则使用 JSON
return JSON.parse(JSON.stringify(obj));
} else {
let newObj = obj.constructor === Array ? [] : {};
for(let key in obj) {
newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key];
}
return newObj;
}
}
let obj = {a: 1, b: [12]};
let newObj = deepCopy(obj);
newObj.b[1] = 100;
console.log(obj);
console.log(newObj);
深度克隆加强版
function cloneDeep(obj) {
let type = isType(obj)
if (type === 'Array' || type === 'Object') {
return cloneObj(obj)
} else if (type === 'Date') {
return obj.constructor(obj)
} else {
return obj
}
}
function cloneObj(obj) {
let newObj = obj instanceof Array ? [] : {};
for (let key in obj) {
newObj[key] = typeof obj[key] === 'object' ? cloneObj(obj[key]) : obj[key]
}
return newObj;
}
function isType(o) {
return /\[object\s(.*?)\]/.exec(Object.prototype.toString.call(o))[1]
}
let fn = function () {
return 123
}
var a = [[1, 2, 3], [4, 5, 6, 7, fn]];
// let c = new Date();
// var b = cloneDeep(c);
var b = cloneDeep(a);
console.log(b[0], a[0]);
console.log(b[0] === a[0]);
原生数据类型检测简易封装
Object.prototype.isType = function (type) {
return function (params) {
return Object.prototype.toString.call(params) === `[object ${type}]`
}
}
let isString = Object.isType('String')
let isArray = Object.isType('Array')
console.log(isString(1)) // false
console.log(isString('hello')) // true
console.log(isArray(2)) // false
console.log(isArray(['hello'])) // true
Array的reduce实现
Array.prototype._reduce = function (callback, initVal) {
let i = 0
let result = initVal
if (typeof initVal === 'undefined') {
result = this[0]
i++
}
for (i; i < this.length; i++) {
result = callback(result, this[i])
}
return result
}
const arr = [1, 2, 3]
let result = arr._reduce((a, b) => {
return a + b
}, 0)
console.log(result) // 6
Function的bind实现
1.es5
Function.prototype._bind = function(context) {
let func = this;
let params = [].slice.call(arguments, 1);
let Fnop = function() {};
let fbound = function() {
params = params.concat([].slice.call(arguments, 0));
return func.apply(this instanceof Fnop ?
this : context, params);
}
Fnop.prototype = this.prototype;
fbound.prototype = new Fnop();
return fbound;
}
function foo() {
this.b = 100;
return this.a;
}
let fe = foo._bind({ a: 1 });
console.log(fe()); // 1
console.log(new fe()); // 实例 {b: 100}
2.es6
Function.prototype.mybind = function(context, ...rest) {
return (...params) => this.call(context, ...rest, ...params);
}
函数组合串联compose(reduce中间件)
// 组合串联
let fn1 = (a) => a + 1;
let fn2 = (b) => b + 2;
let fn3 = (c) => c + 3;
let funs = [fn1, fn2, fn3];
let compose = (func) => {
return arg => func.reduceRight((composed, fn) => fn(composed), arg);
}
console.log(compose(funs)(100)); // 相当于fn1(fn2(fn3(100)))
redux 源码中compose实现(https://github.com/reduxjs/redux/blob/master/src/compose.js)
export default function compose(...funcs) {
if (funcs.length === 0) {
return arg => arg
}
if (funcs.length === 1) {
return funcs[0]
}
return funcs.reduce((a, b) => (...args) => a(b(...args)))
}
koa-compose 实现
function compose(middleware) {
return function(ctx, next) {
let index = -1;
return dispatch(0)
function dispatch(i) {
if(i <= index) return Promise.reject(new Error('next() called multiple times'));
index = i;
let fn = middleware[i]
if (i === middleware.length) fn = next
if (!fn) return Promise.resolve()
try {
return Promise.resolve(fn(ctx, dispatch.bind(null, i + 1)))
} catch(e) {
return Promise.reject(e)
}
}
}
}
let mid1 = (ctx, next) => {
console.log('ctx1', ctx);
next()
}
let mid2 = (ctx, next) => {
console.log('ctx2', ctx);
next()
}
let mid3 = (ctx, next) => {
console.log('ctx3', ctx);
}
let co = compose([mid1, mid2, mid3])
co('ctx')
co函数
function* fn(a = 0) {
a = yield a;
let b = yield 2;
let c = yield 3;
return a + b + c;
}
function co(fn, ...args) {
let g = fn(...args);
return new Promise((resolve, reject) => {
function next(lastValue) {
let { value, done } = g.next(lastValue);
if (done) {
resolve(value);
} else {
if (value instanceof Promise) {
value.then(next, (val) => reject(val));
} else {
next(value)
}
}
}
next();
});
}
co(fn, 100).then(value => {
console.log(value); // 105
});
es6简版 promise
class Promise {
constructor(fn) {
this.status = ‘Pending’
setTimeout(() => {
fn((data) => {
this.resolve(data)
}, (error) => {
this.reject(error)
})
})
}
resolve(data) {
if (this.status === ‘Pending’) {
this.success(data)
this.status = ‘Fulfilled’
}
}
reject(error) {
if (this.status === ‘Pending’) {
this.error(error)
this.status = ‘Rejected’
}
}
then(success, error) {
this.success = success
this.error = error
}
}
let p1 = new Promise((resolve, reject) => {
// reject(‘hello error’);
setTimeout(() => {
resolve(‘hello promise’)
}, 1000)
})
p1.then((data) => console.log(data), (err) => console.log(err))
如何主动中止Promise调用链
const p1 = new Promise((resolve, reject) => {
setTimeout(() => { // 异步操作
resolve(‘start’)
}, 1000);
});
p1.then((result) => {
console.log(‘a’, result);
return Promise.reject(‘中断后续调用’); // 此时rejected的状态将直接跳到catch里,剩下的调用不会再继续
}).then(result => {
console.log(‘b’, result);
}).then(result => {
console.log(‘c’, result);
}).catch(err => {
console.log(err);
});
// a start
// 中断后续调用
bluebird.promisify实现(将异步回调函数api 转换为promise形式)
// promisify.js
module.exports = {
promisify(fn){
return function () {
var args = Array.from(arguments);
return new Promise(function (resolve, reject) {
fn.apply(null, args.concat(function (err) {
if (err) {
reject(err);
} else {
resolve(arguments[1])
}
}));
})
}
}
}
// main.js
const fs = require(‘fs’);
const {promisify} = require(‘./promisify.js’);
const readFile = promisify(‘fs.readFile’); // 转换异步读取
// 异步文件 由回调函数形式变成promise形式
readFile(‘./1.txt’, ‘utf8’).then(data => {
console.log(data);
}).catch(err => {
console.log(err);
});
window.requestAnimationFrame兼容性处理
window._requestAnimationFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
function(callback){
window.setTimeout(callback, 1000 / 60);
};
})();
字符串是否符合回文规则
方法一
function palindrome(params) {
params = params.replace(/[\W\s_]/ig, ”);
return params.toLowerCase() === params.split(”).reverse().join(”).toLowerCase();
}
console.log(palindrome(str));
方法二
function palindrome(params) {
params = params.replace(/[\W\s_]/ig, ”).toLowerCase();
for (var i = 0, j = params.length-1; i<j; i++, j--) {
if (params[i] !== params[j]) {
return false;
}
}
return true;
}
解构
// 将 destructuringArray([1, [2, 3], 4], “[a, [b], c]”) => {a: 1, b: 2, c: 4}
const targetArray = [1, [2, 3], 4];
const formater = “[a, [b], c]”;
const destructuringArray = (values, keys) => {
try {
const obj = {};
if (typeof keys === ‘string’) {
keys = JSON.parse(keys.replace(/\w+/g, ‘”$&”’));
}
const iterate = (values, keys) =>
keys.forEach((key, i) => {
if(Array.isArray(key)) iterate(values[i], key)
else obj[key] = values[i]
})
iterate(values, keys)
return obj;
} catch (e) {
console.error(e.message);
}
}
数组展平
将[[1, 2], 3, [[[4], 5]]] 展平为 [1, 2, 3, 4, 5]
let arr = [[1, 2], 3, [[[4], 5]]]; // 数组展平
function flatten(arr) {
return [].concat(
…arr.map(x => Array.isArray(x) ? flatten(x) : x)
)
}
二分查找
const arr = [1, 2, 3, 4, 5, 6, 7, 8];
// 二分查找 递归 由中间开始往两边查找 前提是有序的数组 返回对应的索引位置
function binarySearch1(arr, dest, start = 0, end = data.length) {
if (start > end) {
return -1
}
let midIndex = Math.floor((start + end) / 2); // 中间位置索引
let mid = arr[midIndex]; // 中间值
if (mid == dest) {
return midIndex;
}
if (dest < mid) { // 要找的比中间值小 就从中间往开头找 一直到0
return binarySearch1(arr, dest, 0, midIndex - 1);
}
if (dest > mid) { // 要找的比中间值大 就从中间往后找 一直到end结束
return binarySearch1(arr, dest, midIndex + 1, end);
}
return -1; // 找不到返回-1
}
console.log(binarySearch1(arr, 7, 3, 6)); // 6
// 非递归 arr前提有序数组 (从小到大)返回对应的索引位置
function binarySearch2(arr, dest) {
let max = arr.length - 1;
let min = 0;
while (min <= max) {
let mid = Math.floor((max + min) / 2); // mid中间位置索引
if (dest < arr[mid]) { // 如果要找的这项比中间项还要小 说明应该在mid中间位置前面 修改最大边界值max=mid-1
max = mid - 1;
} else if (dest > arr[mid]) { // 如果要找的这项比中间项还要大 说明应该在mid中间位置的后面 修改最小边界值min=mid+1
min = mid + 1;
} else {
return mid;
}
}
return -1; // 找不到返回-1
}
console.log(binarySearch2(arr, 3)); // 2
二分查找题
在一个二维数组中,每一行都按照从左到右递增,每一列都从上到下递增的顺序排序,完成一个函数,输入这个二维数组和一个整数,判断数组中是否含有该整数
思路是一样的,只不过从一维变成了二维,我们遍历思路可以这样子:
选取第一行的最后一个进行判断(这个是第一行中最大的)
如果目标大于该值,行数加1,遍历第二行(因为每列都是递增的)
如果目标小于该值,则在这一行中进行查找
循环以上步骤
function findTarget(arr,target) {
let i = 0; // i代表行
let j = arr[i].length -1; // j每行中每项索引位置 从最后项开始比较
while(i < arr.length && j>=0) {
if(target < arr[i][j]) {
j–;
} else if (target > arr[i][j]) {
i++;
} else {
return 找到了,位置在${i},${j}
}
}
return (${i},${j})
}
let arr = [
[1,2,3,4],
[5,9,10,11],
[13,20,21,23]
] //测试
找出数组中重复出现过的元素
// 例如:[1,2,4,4,3,3,1,5,3]
// 输出:[1,3,4]
let arr = [1, 2, 4, 4, 3, 3, 1, 5, 3];
方法一:
function repeat1(arr){
var result = [], map = {};
arr.map(function(num){
if(map[num] === 1) result.push(num); // 等于1说明之前出现过一次 这次重复出现了
map[num] = (map[num] || 0) + 1; // 微妙之处 开始第一次出现无值 记为 0 + 1 = 1 下一次从1开始累加
});
return result;
}
console.log(repeat1(arr));
方法二:
function repeat(arr) {
let result = arr.filter((x, i, self) => {
return self.indexOf(x) = i && self.lastIndexOf(x) ! i
}); //
return result;
}
console.log(repeat(arr));
数组中按照数字重复出现的次数进行排序
// 如果次数相同 则按照值排序 比如 2, 2, 2和 1, 1, 1 应排序为 [1, 1, 1, 2, 2, 2]
// 比如 [1,2,1,2,1,3,4,5,4,5,5,2,2] => [3, 4, 4, 1, 1, 1, 5, 5, 5, 2, 2, 2, 2]
let arr = [9, 7, 7, 1, 2, 1, 2, 1, 3, 4, 5, 4, 5, 5, 2, 2];
function sortArray(arr) {
let obj = {};
let newArr = [];
for(let i = 0; i < arr.length; i++) {
let cur = arr[i];
if(obj[cur]){
obj[cur].push(cur);
continue;
}
obj[cur] = [cur];
}
for(let k in obj) {
if(obj.hasOwnProperty(k)) {
newArr.push(obj[k])
}
}
newArr.sort((a, b) => {
if(a.length === b.length){
return a[0] - b[0];
}
return a.length - b.length;
});
newArr = newArr.reduce((prev, cur) => prev.concat(cur));
return newArr;
}
console.log(sortArray(arr)); // [ 3, 9, 4, 4, 7, 7, 1, 1, 1, 5, 5, 5, 2, 2, 2, 2 ]
不用循环,创建一个长度为 100 的数组,并且每个元素的值等于它的下标。
方法一 递归写法
function createArray(len, arr = []) {
if (len > 0) {
arr[--len] = len;
createArray(len, arr);
}
return arr;
}
console.log(createArray(100));
方法二
// 下面评论中@MaDivH 提供的实现方法 长度为 100 的数组
Array(100).fill().map((_,i)=>i+1);
方法三
[…Array(100).keys()]
根据关键词找出 所在对象id
var docs = [
{
id: 1,
words: [‘hello’, “world”]
}, {
id: 2,
words: [‘hello’, “hihi”]
}, {
id: 3,
words: [‘haha’, “hello”]
}, {
id: 4,
words: [‘world’, “nihao”]
}
];
findDocList(docs, [‘hello’]) // 文档id1,文档id2,文档id3
findDocList(docs, [‘hello’, ‘world’]) // 文档id1
function findDocList(docs, word = []) {
if (word.constructor !== Array) return;
let ids = [];
for (let i = 0; i < docs.length; i++) {
let {id, words} = docs[i];
let flag = word.every((item) => {
return words.indexOf(item) > -1;
});
flag && ids.push(id);
}
return ids;
}
findDocList(docs, [‘hello’, ‘world’]);
getElementsByClassName 兼容写法
function getByClass(cName) {
if (‘getElementsByClassName’ in this) {
return this.getElementsByClassName(cName);
}
cName = cName.replace(/(^\s+|\s+$)/g, ”).split(/\s+/g);
let eles = this.getElementsByTagName(‘*’);
for (let i = 0; i < cName.length; i++) {
let reg = new RegExp((^| )${cName[i]}( |$));
let temp = [];
for (let j = 0; j < eles.length; j++) {
let cur = eles[j];
let {className} = cur;
if (reg.test(className)) {
temp.push(cur);
}
}
eles = temp;
}
return eles;
}
console.log(content.getByClass(‘c1 c2 ‘));
插入顺序
插入排序 从后往前比较 直到碰到比当前项 还要小的前一项时 将这一项插入到前一项的后面
function insertSort(arr) {
let len = arr.length;
let preIndex, current;
for (let i = 1; i < len; i++) {
preIndex = i - 1;
current = arr[i]; // 当前项
while (preIndex >= 0 && arr[preIndex] > current) {
arr[preIndex + 1] = arr[preIndex]; // 如果前一项大于当前项 则把前一项往后挪一位
preIndex– // 用当前项继续和前面值进行比较
}
arr[preIndex + 1] = current; // 如果前一项小于当前项则 循环结束 则将当前项放到 前一项的后面
}
return arr;
}
function insert(arr, i, x) {
let prev = i - 1;
while(prev >= 0 && arr[prev] > x) {
arr[prev + 1] = arr[prev];
prev–;
}
arr[prev + 1] = x;
}
function insert_sort(arr) {
for (let i = 1; i < arr.length; i++) {
insert(arr, i, arr[i]);
}
return arr;
}
console.log(insert_sort([1, 10, 3, 0]));
选择排序
选择排序 每次拿当前项与后面其他项进行比较 得到最小值的索引位置 然后把最小值和当前项交换位置
function selectSort(arr) {
let len = arr.length;
let temp = null;
let minIndex = null;
for (let i = 0; i < len - 1; i++) { // 把当前值的索引作为最小值的索引一次去比较
minIndex = i; // 假设当前项索引 为最小值索引
for (let j = i + 1; j < len; j++) { // 当前项后面向一次比小
if (arr[j] < arr[minIndex]) { // 比假设的值还要小 则保留最小值索引
minIndex = j; // 找到最小值的索引位置
}
}
// 将当前值和比较出的最小值交换位置
if (i !== minIndex) {
temp = arr[i]
arr[i] = arr[minIndex];
arr[minIndex] = temp;
}
}
return arr;
}
冒泡排序
冒泡排序 相邻两项进行比较 如果当前值大于后一项 则交换位置
冒泡1
function swap(arr, i, j) {
[arr[i], arr[j]] = [arr[j], arr[i]]
}
function bubleSort(arr) {
let length = arr.length;
let temp = null;
for (let i = 0; i < length - 1; i++) { // 控制轮数
let flag = false; // 当前这轮是否交换过标识
for (let l = 0; l < length - i - 1; l++) { // 控制每轮比较次数
if (arr[l] > arr[l + 1]) {
// temp = arr[l];
// arr[l] = arr[l + 1];
// arr[l + 1] = temp;
swap(arr, l, l + 1);
flag = true; // 如果发生过交换flag则为true
}
}
if (!flag) { // 优化 如果从头到尾比较一轮后 flag依然为false说明 已经排好序了 没必要在继续下去
temp = null;
return arr;
}
}
return arr;
}
冒泡2
function swap(arr, i, j) {
[arr[i], arr[j]] = [arr[j], arr[i]]
}
function bubble_sort(arr) {
for (let i = arr.length - 1; i >= 1; i–) {
for (let j = 1; j <= i; j++) {
arr[j - 1] > arr[j] && swap(arr, j - 1, j)
}
}
return arr;
}
console.log(bubble_sort([1, 10, 3, 0]));
