快速排序
const utils = {
swap(array, a, b) {
[array[a], array[b]] = [array[b], array[a]]
},
randomNum() {
return Math.floor(Math.random() * 100)
},
randomArray() {
return Array.from(Array(this.randomNum()), _ => this.randomNum())
}
}
function partition(array, start, end) {
let j = start
let index = Math.floor(Math.random()*(end -start + 1) + start)
utils.swap(array, index, end)
let pivot = array[end]
for (let i = start; i <= end; i++) {
if (array[i] <= pivot) {
utils.swap(array, i, j++)
}
}
return j - 1
}
function quickSort(array, start = 0, end = array.length -1) {
if (end - start < 1) return array
let pivotIndex = partition(array, start, end)
quickSort(array, start, pivotIndex - 1)
quickSort(array, pivotIndex + 1, end)
return array
}
let array = utils.randomArray()
console.log(quickSort(array))
函数组合
function pipe(...funcs){
return function(x){
return funcs.reduce(function(accu, func){
return func(accu);
}, x);
}
}
let str = 'jspool'
function stringToUpper(str) {
return str.toUpperCase()
}
function stringReverse(str) {
return str.split('').reverse().join('')
}
function getThreeCharacters(str){
return str.substring(0,3)
}
function stringToArray(str) {
return str.split('')
}
let toUpperAndGetThreeAndArray = compose(stringToArray, getThreeCharacters,stringToUpper)
let result = toUpperAndGetThreeAndArray(str) // => ["J","S","P"]
call, bind, apply
Function.prototype.myCall = function (context, ...arg) {
const fn = Symbol('临时属性')
context[fn] = this
context[fn](...arg)
delete context[fn]
}
Function.prototype.myBind = function (context, ...firstarg) {
const that = this
const bindFn = function (...secoundarg) {
return that.myCall(context, ...firstarg, ...secoundarg)
}
bindFn.prototype = Object.create(that.prototype)
return bindFn
}
事件
- DOM事件流:捕获阶段 → 目标阶段 → 冒泡阶段
- 事件捕获的具体流程:window document html body → 元素
注意,第一个接收到事件的是window。
获取HTML根元素,document.documentElement
dom操作
node.parentNode
node.removeChild()
function findParent(node) {
let parentNode = node.parentNode;
let root = this.el.find(item => item === parentNode);
if (root) {
root.removeChild();
} else {
findParent(parentNode);
}
window.addEventListener('DOMContentLoaded', function () {}, false);
}
Event对象的常见应用
event.preventDefault()
阻止默认事件。
event.stopPropagation()
阻止冒泡(防止父级元素响应)。
event.stopImmediatePropagation()
给同一个元素注册两个同类型事件a,b;在a事件注册函数中添加这段调用,可阻止b的注册事件触发。
event.currentTarget
触发时获取当前绑定的事件元素(常用于事件委托获取父级元素)。
event.target
获取当前触发的目标对象(子级元素)。
自定义事件
Event无法传参,其余与CustomEvent一致.
CustomEvent传递参数,需在new CustomEvent
的第二个参数给定一个拥有detail
属性的对象。
ele.addEventListener('custome', function(e){
console.log(e.deatil.data); // Event时为null, CustomEvent时为‘somedata’
});
var eve = new Event('custome');
var eve = new CustomEvent('custome', {
detail: {
data: 'somedata'
}
});
ele.dispatchEvent(eve);
类型转换
Number函数
转换时优先调用valueOf,后toString,这两步操作如果返回基本数据类型则将这个值重新调用Number函数,如果这两步都未返回基础数据类型则报错。
字符串:如果可以解析为数值,转换为相应值,不同于parseInt,包含非数字字符会返回NaN,为小数则返回小数,空字符串返回0.
undefined:返回NaN。
null:返回0.
[] + []
答案为空字符串。 加号运算符两侧未出现字符串,优先转换为数据,左侧valueOf
返回其本身,故继续调用toString
,数组toString
方法相当于调用join
方法,返回空字符串,右侧同理。
{} + []
答案为0。左侧大括号被认为为空代码块,运算表达式实为+[]
,对空数组调用Number方法,空数组toString后为空字符串,Number('')
返回0。
[] + {}
答案为'[object Object]'
。空数组被转为空字符串,加号左侧为string故右侧值转为string类型。
原型链
创建对象的几种方法
- 直接书写字面量或
new Object({ })
- function的new调用
Object.create(obj)
模拟new运算符
var myNew = function (func, ...arg){
var obj = Object.create(func.prototype);
var res = func.apply(obj, arg);
if (typeof res === 'object') return res;
else return obj;
}
function继承
function Parent() {
this.name = 'parent';
this.arr = [1, 2, 3];
}
function Child() {
Parent.call(this);
this.type = 'child';
}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
运行机制类
- 如何理解js的单线程
JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。那么,为什么JavaScript不能有多个线程呢?这样能提高效率啊。
JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?
所以,为了避免复杂性,从一诞生,JavaScript就是单线程,这已经成了这门语言的核心特征,将来也不会改变。
为了利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。所以,这个新标准并没有改变JavaScript单线程的本质。
什么是任务队列
什么是event loop: 「面试必考」从多线程到Event Loop全面梳理
将语句放入异步任务队列的时机
具体参考饿了么前端的这两篇文章
Event Loop 这个循环你晓得么?(附GIF详解)
设计模式
- 观察者模式 订阅发布
class Publisher {
constructor() {
this.eventEmitter = new EventEmitter();
}
publish(type, ...args) {
this.eventEmitter.emit(type, ...args);
}
}
class Subscriber {
constructor() {
this.eventEmitter = new EventEmitter();
}
subscribe(publisher, type) {
publisher.eventEmitter.on(type, subscriber, cb);
}
}
class EventEmitter {
constructor() {
// 定义存放事件订阅的存储变量
this.listeners = {};
}
on(type, subscriber, cb) {
let cbs = this.listeners[type];
if (!cbs) { // 判断是否已经有订阅
cbs = [];
}
// 订阅一则事件
cbs.push(cb);
this.listeners[type] = cbs;
return this;
}
emit(type, ...args) {
const cbs = this.listeners[type];
if (Array.isArray(cbs)) {
for (let i = 0; i < cbs.length; i++) {
const cb = cbs[i];
if (typeof cb === 'function') {
// 触发一则事件
cb(...args);
}
}
}
return this;
}
off(type, cb) {
if (cb) { // 如果有回调,则取消订阅该回调
let cbs = this.listeners[type];
cbs = cbs.filter(func => func !== cb);
this.listeners[type] = cbs;
} else { // 否则取消订阅整个事件
this.listeners[type] = null;
delete this.listeners[type];
}
return this;
}
}
export default new EventEmitter();
以下这种则是给每个对象上方添加listener的实现方式, 使用WeakMap
var listeners = new WeakMap();
// 监听事件
function on(object, event, fn){
var thisListeners = listeners.get(object);
if(!thisListeners) thisListeners = {};
if(!thisListeners[event]) thisListeners[event] = [];
thisListeners[event].push(fn);
listeners.set(object, thisListeners);
}
// 触发事件
function emit(object, event){
var thisListeners = listeners.get(object);
if(!thisListeners) thisListeners = {};
if(!thisListeners[event]) thisListeners[event] = [];
thisListeners[event].forEach(function(fn){
fn.call(object, event);
});
}
// 使用
var obj = {};
on(obj, 'hello', function(){
console.log('hello');
});
emit(obj, 'hello');
一些题
- 完成函数changeDateFormate, 将字符串中的'MM/DD/YYYY'为替换为'YYYY/MM/DD'.
输入:'John was born in 02/13/1920 and died in 01/20/1970.'
输出:'John was born in 1920/02/13 and died in 1970/01/20.'
function changeDateFormate(str) {
const dateReg = /\d{2}\/\d{2}\/\d{4}/g;
var times = str.match(dateReg);
times = times.map(item => {
var times = item.split('/');
times.unshift(times.pop());
return times.join('/');
});
var splitDateStrs = str.split(dateReg);
var res = [];
splitDateStrs.forEach((item, idx) => {
res.push(item);
times[idx] && res.push(times[idx]);
});
return res.join('');
}
- css3设计一个立起的圆形, 并围绕自身中轴线做360度持续旋转.
- css分割单行和多行截断字符串, 最后以...结尾
.单行 {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
width: xxpx;
}
.多行 {
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 3;
text-overflow: ellipsis;
-webkit-box-orient: vertical; // 好像不需要这个
}
- 什么是CSRF, 怎么造成的, 有什么防御方法?
- Cookie和Session有什么区别
- 如何html中开启关闭DNS预读取?
<meta http-equiv="x-dns-prefetch-control" content="on"> - <script>标签defer或async属性的取用及二者的区别?
- defer: HTML解析完后才执行, 如果有多个, 按照加载的顺序一次执行
- async: 加载完之后立即执行, 如果有多个, 执行顺序和加载顺序无关
- 设计移到js的range算法, 如下:
range(1, 10, 3), 返回[1, 4, 7, 10]. range('A', 'F', 2), 返回['A', 'C', 'E'].
使用'A'.charCodeAt(0)
与String.fromCharCode()
function range(start , end , interval) {
const res = [];
if (typeof start == 'string' && typeof end =='string') {
start = start.charCodeAt(0);
end = end.charCodeAt(0);
for (let i = start; i<=end; i+=interval) {
res.push(String.fromCharCode(i));
}
} else if (typeof start == 'number' && typeof end == 'number'){
for (let i = start; i<=end; i+=interval) {
res.push(i);
}
}
return res;
}
- [{
time: number,
content: string
},
{
time: number,
content: string
},
...]. 需要快速定位到某个时间点的弹幕, 请编码实现(不使用sort)
Object深拷贝.
目前虽然已经考虑到很多条件, 但还不太完善.
在codepen上打开
// 简化版本
function clone(target, map = new WeakMap()) {
if (!isObject(target)) {
return target;
} else {
const isArray = Array.isArray(target);
let cloneTarget = new target.constroctor();
if (!map.has(target)) {
map.set(target, cloneTarget);
} else {
return map.get(target);
}
const keys = isArray ? undefined : Reflect.ownKeys(target);
forEach(keys || target, (value, key) => {
if (keys) {
key = value;
}
cloneTarget[key] = clone(target[key], map);
});
return cloneTarget;
}
}
function isObject(target) {
const type = typeof target;
return target !== null && (type === 'object' || type === 'function');
}
function getType(target) {
return Object.prototype.toString.call(target);
}
function forEach(array, iteratee) {
let index = -1;
while (++index < array.length) {
iteratee(array[index], index);
}
}
const target = {
field1: 1,
field2: undefined,
field3: {
child: 'child'
},
field4: [2, 4, 8],
f: { [Symbol()]: { f: { f: { f: { f: { f: { f: { f: { f: { f: { f: {} } } } } } } } } } } },
};
// 以下是更全的版本
const mapTag = 'Map'; const setTag = 'Set'; const arrayTag = 'Array';
const objectTag = 'Object'; const argsTag = 'Arguments'; const boolTag = 'Boolean';
const numberTag = 'Number'; const stringTag = 'String';
const symbolTag = 'Symbol'; const regexpTag = 'RegExp'; const funcTag = 'Function';
const deepTag = [mapTag, setTag, arrayTag, objectTag, argsTag];
function isObject(target) {
const type = typeof target;
return target !== null && (type === 'object' || type === 'function');
}
function getType(target) {
return Object.prototype.toString.call(target).slice(8, -1);
}
function cloneReg(targe) {
const reFlags = /\w*$/;
const result = new RegExp(targe.source, reFlags.exec(targe)[0]);
result.lastIndex = targe.lastIndex;
return result;
}
function cloneFunction(func) {
const funcString = func.toString();
if (!func.prototype) {
return eval(funcString);
} else {
const bodyReg = /(?<={).*(?=})/s;
const paramReg = /(?<=\().*(?=\)\s*{)/s;
const param = paramReg.exec(funcString);
const body = bodyReg.exec(funcString);
if (body) {
if (param) {
return new Function(param[0], body[0]);
const paramArr = param[0].split(',');
// return new Function(...paramArr, body[0]);
} else {
return new Function(body[0]);
}
} else {
return null;
}
}
}
function cloneOtherType(targe, type) {
switch (type) {
case numberTag:
case stringTag:
case boolTag:
case symbolTag:
return Object(targe.valueOf());
case regexpTag:
return cloneReg(targe);
case funcTag:
return cloneFunction(targe);
default:
return new targe.constructor(targe);
}
}
function clone(target, map = new WeakMap()) {
if (!isObject(target)) return target; // 原始类型
// 防止循环引用
if (map.has(target)) return map.get(target);
// 初始化
const type = getType(target);
if (!deepTag.includes(type)) {
return cloneOtherType(target, type);
}
let cloneTarget = new target.constructor();
map.set(target, cloneTarget);
if (type === setTag) { // 克隆set
target.forEach(value => {
cloneTarget.add(clone(value, map));
});
}
if (type === mapTag) { // 克隆map
target.forEach((value, key) => {
cloneTarget.set(key, clone(value, map));
});
}
if (type === arrayTag) { // 数组
target.forEach(v => {
cloneTarget.push(clone(v, map));
});
}
if (type === objectTag) { // Object
for (let key of Reflect.ownKeys(target)) {
cloneTarget[key] = clone(target[key], map);
}
}
return cloneTarget;
}
// 用例demo
const sym = Symbol('testB');
let a = {
a: function (a, b) {
console.log(a,b);
},
b: 'b',
c: [1, 2, {a: 1}],
};
a[sym] = "symbol";
a.d = a;
let b = clone(a);
console.log(a.d === a, a);
console.log(b.d === b, b);
函数柯里化
柯里化是个十分实用的工具函数, 可以用于包裹一些业务逻辑, 以下是个人修改版.
function curry(func, len = func.length) {
return function curried(...args) {
if (args.length >= len) {
return func.apply(this, args);
} else {
return function(...newArgs) {
return curried.apply(this, args.concat(newArgs));
}
}
};
}
Promise
function MyPromise(executor){
let self = this
self.value = undefined
self.reason = undefined
self.status = 'pending'
self.onResolvedCallbacks = []
self.onRejectedCallbacks = []
function resolve(value){
if(self.status === 'pending'){ //保证状态一旦变更,不能再次修改
self.value = value
self.status = 'resolved' // 成功状态
self.onResolvedCallbacks.forEach(fn => {
fn()
})
}
}
function reject(reason){
if(self.status === 'pending'){
self.reason = reason
self.status = 'rejected' //失败状态
self.onRejectedCallbacks.forEach(fn => {
fn()
})
}
}
executor(resolve, reject) // 因为会立即执行这个执行器函数
}
MyPromise.prototype.then = function(onFulfilled, onRejected){
let self = this
return new MyPromise(function(resolve, reject){
if(self.status === 'resolved'){
onFulfilled(self.value)
}
if(self.status === 'rejected'){
onRejected(self.reason)
}
if(self.status === 'pending'){
self.onResolvedCallbacks.push(function(){
onFulfilled(self.value)
})
self.onRejectedCallbacks.push(function(){
onRejected(self.reason)
})
}
}
}
判断url是图片的正则: const imgReg = /\.(png|jpe?g|gif|svg|bmp|raw|webp)(\?.*)?$/;