JS杂项备忘录

快速排序

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
    }

事件

  1. DOM事件流:捕获阶段 → 目标阶段 → 冒泡阶段
  2. 事件捕获的具体流程: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类型。

原型链

创建对象的几种方法

  1. 直接书写字面量或new Object({ })
  2. function的new调用
  3. 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;

运行机制类

  1. 如何理解js的单线程

JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。那么,为什么JavaScript不能有多个线程呢?这样能提高效率啊。

JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?

所以,为了避免复杂性,从一诞生,JavaScript就是单线程,这已经成了这门语言的核心特征,将来也不会改变。

为了利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。所以,这个新标准并没有改变JavaScript单线程的本质。

什么是任务队列
什么是event loop: 「面试必考」从多线程到Event Loop全面梳理
将语句放入异步任务队列的时机

具体参考饿了么前端的这两篇文章
Event Loop 这个循环你晓得么?(附GIF详解)

设计模式

  1. 观察者模式 订阅发布
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');

一些题

  1. 完成函数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('');
}
  1. css3设计一个立起的圆形, 并围绕自身中轴线做360度持续旋转.
  2. 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; // 好像不需要这个
}
  1. 什么是CSRF, 怎么造成的, 有什么防御方法?
  2. Cookie和Session有什么区别
  3. 如何html中开启关闭DNS预读取?
    <meta http-equiv="x-dns-prefetch-control" content="on">
  4. <script>标签defer或async属性的取用及二者的区别?
  1. defer: HTML解析完后才执行, 如果有多个, 按照加载的顺序一次执行
  2. async: 加载完之后立即执行, 如果有多个, 执行顺序和加载顺序无关
  1. 设计移到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;
}
  1. [{
    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)(\?.*)?$/;

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,013评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,205评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,370评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,168评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,153评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,954评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,271评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,916评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,382评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,877评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,989评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,624评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,209评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,199评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,418评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,401评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,700评论 2 345

推荐阅读更多精彩内容

  • 概要 64学时 3.5学分 章节安排 电子商务网站概况 HTML5+CSS3 JavaScript Node 电子...
    阿啊阿吖丁阅读 9,095评论 0 3
  • 前端面试题的简单整理,都只是大概回答,具体某些问题的具体理解后续会补上。 前端页面有哪三层构成,分别是什么?作用是...
    李欢li阅读 475评论 0 2
  •   JavaScript 与 HTML 之间的交互是通过事件实现的。   事件,就是文档或浏览器窗口中发生的一些特...
    霜天晓阅读 3,473评论 1 11
  • Lua 5.1 参考手册 by Roberto Ierusalimschy, Luiz Henrique de F...
    苏黎九歌阅读 13,738评论 0 38
  • 所有知识点已整理成app app下载地址 J2EE 部分: 1.Switch能否用string做参数? 在 Jav...
    侯蛋蛋_阅读 2,407评论 1 4