1.call/apply
Function.prototype.myCall = function (context, ...args) {
    if (typeof this !== 'function') return;
    var fun = this;
    context._fn = fun; // TODO:确保_fn 键不存在
    var res = context._fn(args);
    return res;
} // test
function say (...args) {
    return args + this.name
} var obj = { name: 'rencoo' } say.myCall(obj, 'hello, ') // "hello, rencoo"
2.bind
  • 绑定环境
  • 如果以new调用的话,
// var fn = func.bind(obj, ...args);
// fn(...args);
// new fn(...args); // 对 this 来说, new 的优先级高于 bind; 会忽视传入的 context
Function.prototype.myBind = function (context, ...args) {
    if (typeof this !== 'function') return;
    var fun = this;
    return function (...innerArgs) {
        args = args.concat(innerArgs);
        var res = fun.apply(context, args);
        return res;
    }
}

考虑到 new 调用,在返回函数里做一层判断,如果函数是使用使用 new 进行调用的,那么绑定的环境就会失效

var obj = { age: 25 };
function Person(name) {
    this.name = name;
    console.log(this); // (*)
}
Person.prototype.sayHi = function () {
    console.log('hi');
} var fn = Person.bind(obj, 'Bob'); // 调用后, (*)的打印内容
// 普通调用
fn();
console: { age: 25, name: "Bob" } // 通过new调用
var p = new fn();
console.log(p); // Person { name: 'Bob' }
console.log(p.sayHi); // f () { console.log('hi') }

改进我们的 myBind

Function.prototype.myBind = function (context, ...args) {
    var fn = this;
    if (typeof fn !== 'function') return;     var boundFn;
    var binder = function (...innerArgs) {
        args = args.concat(innerArgs);         // console.log(this instanceof boundFn); // 使用 new 调用时, this 即为构造函数的实例, 判断结果为 true (优先级高于 bind 传入的 context)
        if(this instanceof boundFn) { // new 调用; 不使用bind传入的 context
            var res = fn.apply(this, args);
            if (typeof res === 'object') {
                return res;
            }
            return this;
        } else { // 普通调用
            return fn.apply(context, args);
        }
    }     boundFn = Function('binder', 'return function (){ return binder.apply(this,arguments); }')(binder);     if (fn.prototype) {
        var Empty = function Empty() {};
        Empty.prototype = fn.prototype;
        boundFn.prototype = new Empty();
        Empty.prototype = null; 
    }     return boundFn;
} // 测试
var obj = { age: 25 };
function Person(name) {
    this.name = name;
    console.log(this); // (*)
} var fn = Person.myBind(obj, 'Bob'); // 调用后, (*)的打印内容
// 普通调用
fn();
// console: { age: 25, name: "Bob" } // 通过new调用
new fn();
// console: Person { name: 'Bob' }

常用的装饰器函数 once/throttle/debounce

3.once

Ensure a function is called only once,函数调用一次即失效

function once (fn) {
    var flag = false;
    return function (...args) {
        !flag && fn.apply(this, args);
        flag = true;
    }
} // more elegant way to write a once function
function once (fn) {
    // closure here; var fn = fn;
    return function (...args) {
        var res = fn && fn.apply(this, args);
        fn = null;
        return res;
    }
}
4.throttle

节流:连续的高频操作,只有最后一次操作后的一段时间后才调用传入的函数(最终的状态最重要),后一次操作会覆盖前一次操作

应用:实时搜索里的搜索函数、鼠标移动函数中的更新函数、窗口resize里的更新函数

(原因是这些传入的函数可能都是一些耗时耗性能的操作,无法也没必要在每次微小动作上执行)

function throttle (fn, delay) {
    var timer = null;
    return function (...args) {
        // 如果上一次操作的定时器尚未执行,那么取消它并将其替换为一个新的定时器
        clearTimeout(timer);
        timer = setTimeout(() => fn.apply(this, args), delay);
    }
}
5.debounce

防抖:第一次生效,之后一定时间内无论怎么触发都无效

function debounce (fn, wait) {
    var flag = true, timer = null;
    return function (...args) {
        if (flag) {
            flag = false;             timer = setTimeout(function () {
                flag = true;
                clearTimeout(timer);
            }, wait);             return fn.apply(this, args);
        }
    }
} // 另一种写法
function debounce (fn, wait) {
    var timer = null;
    return function (...args) {
        if (!timer) {
            timer = setTimeout(() => { 
                timer = null;
            }, wait);             return fn.apply(this, args);
        }
    }
}
6.throttle_optimize

升级版的 throttle_optimize 结合了 throttle 与 debounce 的特点

与 throttle 的区别:throttle_optimize 对于用户的第一次操作总是调用传入的函数

与 debounce 的区别:如果被忽略的调用是冷却期间的最后一次,那么它会在延迟结束时执行传入的函数

function throttle_optimize (fn, delay) {
    var flag = true, timer = null;
    return function (...args) {
        if (flag) {
            flag = false;
            fn.apply(this, args);
            args = null;
        }         clearTimeout(timer);
        timer = setTimeout(() => {
            flag = true;
            if (args) {
                fn.apply(this, args);
            }
        }, delay);
    }
}
7.throttle_optimize

用于动画渲染的 throttle_optimize

对于第一次操作,总是 update;之后的 delay 时间内的最后一次操作总是在 delay 时刻渲染一次;

也就是说装饰器函数 throttle_optimize 会经常调用,但是传入的更新函数,最多每 delay 时间调用一次(也可能不调用,没有任何操作就不会调用)

比如,使用鼠标移动和屏幕坐标原点绘制矩形,鼠标持续移动过程中,每隔delay时间渲染一次矩形,而不是每个移动都绘制,或者说只有最后一次移动结束的一段时间后才绘制(throttle的原理)

function throttle_optimize(func, ms) {

    let isThrottled = false,
        savedArgs,
        savedThis;     function wrapper() {
        if (isThrottled) {
            savedArgs = arguments;
            savedThis = this;
            return;
        }         func.apply(this, arguments);
        isThrottled = true;         setTimeout(function () {
            isThrottled = false;
            if (savedArgs) {
                wrapper.apply(savedThis, savedArgs);
                savedArgs = savedThis = null;
            }
        }, ms);
    }     return wrapper;
}

8.new

function myNew (Fn, ...args) {
    // 1.生成一个空对象作为this
    // 2.将对象链接到原型上
    let obj = Object.create(Fn.prototype)
    // 3.执行构造函数, 并将obj作为context传入(为实例对象添加属性)
    let res = Fn.apply(obj, args) // 构造函数内的 this 动态改变为 obj
    // 4.return this或者构造函数的执行结果(引用类型)
    return typeof res === 'object' ? res : obj
    // return result instanceof Object ? res : obj
} // test
function Person (name, age) {
    this.name = name;
    this.age = age;
} var a = myNew(Person, 'rencoo', 25)
console.log(a) // Person {name: "rencoo", age: 25}
var b = new Person('ge can', 26)
console.log(b) // Person {name: "gecan", age: 26} Person.prototype.sayHi = function () {
    console.log(this.name)
} a.sayHi() // rencoo
b.sayHi() // gecan

9.promise

class Promise {
    constructor (executor) {
        // 设置属性 status value resolveCbs rejectCbs
    }
    then (onResolved, onRejected) {     }
    catch (cb) {
        return this.then(null, cb)
    }
}

promise链式,实现必须上一个异步完成后再去跑下一个任务

// 1.
const template = Promise.resolve();
promises.forEach((p) => {
    template = template.then(p)
})
// 2. 使用 await 

10.deep copy(deepclone)

11.extends

12.singleton

13.pub-sub

14.打印出html里所有标签

15.lazyman

16.快排

17.数组乱序

18.LRU

19.两数之和

20.找出一个集合的所有子集

21.Object.defineProperty

  • 实现的关键在那个闭包

22.requestAnimation(京东讲座,react间隙渲染)

/**
 * Provides requestAnimationFrame in a cross browser way.
 * http://paulirish.com/2011/requestanimationframe-for-smart-animating/
 */ if ( !window.requestAnimationFrame ) {     window.requestAnimationFrame = ( function() {         return window.webkitRequestAnimationFrame ||
        window.mozRequestAnimationFrame ||
        window.oRequestAnimationFrame ||
        window.msRequestAnimationFrame ||
        function( 
        /* function FrameRequestCallback */ callback, 
        /* DOMElement Element */ element ) {             window.setTimeout( callback, 1000 / 60 );         };     } )(); }

23.手写 Proxy(使用Object.defineProperty将一个对象的属性代理到另一个对象上)

24.webpack

  • loader plugin的区别
  • tree-shaking 的工作原理(一个模块导出十个方法,但只用到了一个,那么只打包那一个方法)
  • code splitting用的是什么插件
  • 如何提高 webpack 的构建速度
  • 利用 DIIPlugin 预编译资源模块
  • 利用 Happypack 加速代码构建

25.jsonp

//通过JQuery Ajax 发起jsonp请求
(注:不是必须通过jq发起请求 , 
     例如:Vue Resource中提供 $.http.jsonp(url, [options]))
$.ajax({
    // 请求方式
    type: "get", 
    // 请求地址
    url: "http://169.254.200.238:8080/jsonp.do", 
    // 标志跨域请求
    dataType: "jsonp",                
    // 跨域函数名的键值,即服务端提取函数名的钥匙(默认为callback)
    jsonp: "callbackparam",   
    // 客户端与服务端约定的函数名称
    jsonpCallback: "jsonpCallback",
    // 请求成功的回调函数,json既为我们想要获得的数据
    success: function(json) {
        console.log(json);
    },
    // 请求失败的回调函数
    error: function(e) {
    alert("error");
    }
}); @RequestMapping({"/jsonp.do"})
public String jsonp(@RequestParam("callbackparam") String callback){
    // callback === "jsonpCallback"
    // return callback + "({\"result\":\"success\"})";
    return  (request.from === jsonp) ? callback(data) : data ;
}

26.实现一个Queue类,要求包含两个函数

task函数:新增一个任务。包含两个参数,等待时间和回调函数

start函数:执行任务队列。将所有任务按队列顺序执行, 执行完一个任务才能执行下一个任务

new Queue()
    .task(1000, () => {
        console.log(1)
    })
    .task(2000, () => {
        console.log(2)
    })
    .task(1000, () => {
        console.log(3)
    })
    .start()
// 链接:https://www.zhihu.com/question/358075914/answer/915814865
const queue = {
  a() {
    setTimeout(() => {
      console.log("a is done");
      this.next();
    }, 1000);
  },
  b() {
    setTimeout(() => {
      console.log("b is done");
      this.next();
    }, 1000);
  },
  c() {
    setTimeout(() => {
      console.log("c is done");
    }, 1000);
  }
}; Object.defineProperty(queue, Symbol.iterator, {
  enumerable: false,
  writable: false,
  configurable: true,
  value() {
    const o = this;
    const ks = Object.keys(o);
    let idx = 0;
    return {
      next() {
        const key = ks[idx++];
        const func = o[key];
        if (typeof func === "function") {
          func.call(this);
        }
        return {
          value: key,
          done: idx > ks.length
        };
      }
    };
  }
}); const qt = queue[Symbol.iterator]();
qt.next(); // a is done
// b is done
// c is done
let queue = new Queue();

queue.push((next) => {
    // do something async
    next();
}) queue.push((next) => {
    // do something async
    Ajax.post('xxx').then(() => next());
}); queue.run()

27.实现一个wait

function wait (time) {
    return new Promise((resolve, rejec) => {
        setTimeout(resolve, time);
    });
} wait(2000).then(() => {
    console.log('hello1');
    return wait(2000);
}).then(() => {
   console.log('hello2'); 
}); // 使用 await
(async function () {
    var res = await wait(2000);
    // console.log(res); // undefined
    console.log('hello1');
    var res1 = await wait(2000);
    // console.log(res1); // undefined
    console.log('hello2');
})();

28.实现一个jquery事件代理

function on(parent, currentClassName, eventName, cb) {

  parent.addEventListener(eventName, function (evt) {
    var target = evt.target;
    var currentTarget;
    while (target) {
      if (target.tagName === 'BODY') break; // 找到body还没找到(说明点击的位置已经是目标元素的父级了; 不可能找到的)
      if (target.getAttribute('class') === currentClassName) {
        currentTarget = target;
        break;
      } else {
        target = target.parentNode; // 往上找; 从目标元素的子元素触发事件
      }
    }     if (currentTarget) {
      // 获取 currentTarget 上的数据; 并执行事件回调
      cb.apply(currentTarget);
    }
  }); }

各种常用js函数实现的更多相关文章

  1. 原生JS研究:学习jquery源码,收集整理常用JS函数

    原生JS研究:学习jquery源码,收集整理常用JS函数: 1. JS获取原生class(getElementsByClass) 转自:http://blog.csdn.net/kongjiea/ar ...

  2. api日常总结:前端常用js函数和CSS常用技巧

    我的移动端media html{font-size:10px} @media screen and (min-width:321px) and (max-width:375px){html{font- ...

  3. web前端关于html转义符的常用js函数

    web前端关于html转义符的常用js函数 //去掉html标签 function removeHtmlTab(tab) { return tab.replace(/<[^<>]+? ...

  4. 基础常用JS函数和语法

    100多个基础常用JS函数和语法集合大全  来源:http://www.cnblogs.com/hnyei/p/4605103.html 网站特效离不开脚本,javascript是最常用的脚本语言,我 ...

  5. 常用 JS 函数

    各种业务开发都离不开对数据的处理,然而遇到的很多数据都是不好处理的.这个时候就需要寻求搜索引擎的帮助.这种方法效率是非常低下的,而且根据作者的个性不能保证其对自己的口味.因此这篇文字包含了一份 JS ...

  6. 100多个基础常用JS函数和语法集合大全

    网站特效离不开脚本,javascript是最常用的脚本语言,我们归纳一下常用的基础函数和语法: 1.输出语句:document.write(""); 2.JS中的注释为//3.传统 ...

  7. 一些常用JS函数和技巧总结

    1.JS原生函数parseInt(),返回字符串的第一个数字,默认是十进制. 2.!!data.success  //强制转换成布尔类型

  8. 常用js函数整理--common.js

    var h = {}; h.get = function (url, data, ok, error) { $.ajax({ url: url, data: data, dataType: 'json ...

  9. 一些常用JS 函数总结

    搜索url参数 /** * 搜索url参数 * @param {String} name 参数键名 * @return {String} 对应值 */ function getQueryVariabl ...

随机推荐

  1. Kotlin Coroutines不复杂, 我来帮你理一理

    Coroutines 协程 最近在总结Kotlin的一些东西, 发现协程这块确实不容易说清楚. 之前的那篇就写得不好, 所以决定重写. 反复研究了官网文档和各种教程博客, 本篇内容是最基础也最主要的内 ...

  2. 从surfaceflinger历史变更谈截屏

    众所周知,有一个程序screencap可以截屏,这个程序十分简单,只是使用了surfaceflinger服务的截屏功能. 所以要了解截屏,看surfaceflinger服务的代码是不二首选.但是sur ...

  3. three.js使用卷积法实现物体描边效果

    法线延展法 网上使用法线延展法实现物体描边效果的文章比较多,这里不再描述. 但是这种方法有个缺点:当两个面的法线夹角差别较大时,两个面的描边无法完美连接.如下图所示: 卷积法 这里使用另一种方法卷积法 ...

  4. C#控制打印机通过不同纸盒/进纸口进纸打印

    通常我们是通过程序操作打印机打印我们设置好的内容,但基本都是打印机默认进纸口打印:最近有一个通过C#程序控制两个进纸口分别进一张纸进行打印的需求,通过偿失找到了解决方案如下: 关于C#调用打印机打印的 ...

  5. web前端面试题总结(html、css)

    1.对 WEB 标准以及 W3C 的理解与认识? 参考: 标签闭合.标签小写.不乱嵌套.提高搜索机器人搜索几率.使用外 链 css 和 js 脚本. 结构行为表现的分离.文件下载与页面速度更快.内容能 ...

  6. 20191121-7 Scrum立会报告+燃尽图 03

    此作业的要求参见https://edu.cnblogs.com/campus/nenu/2019fall/homework/10067一.小组情况 队名:扛把子 组长:孙晓宇 组员:宋晓丽 梁梦瑶 韩 ...

  7. pyinstaller打包python文件成exe(原理.安装.问题)

    py文件打包成exe文件的方式一共有三种:py2exe.PyInstaller和cx_Freeze 本文分四个步骤来详讲如何用PyInstaller将py文件打包成exe文件 1. PyInstall ...

  8. Altium Designer 20.0.9

    Altium Designer 20.0.9 Download: http://dl3.downloadly.ir/Files/Software/Altium_Designer_20.0.9_Buil ...

  9. Vue组件通信之非父子组件传值

    前言: 如果想要了解非父子关系的组件传值,最好是在了解父传子和子传父的基础上在来了解非父子传值可能会有更透彻的思路. 因为非父子传值是通过定义事件总线来代理实现父传子+子传父从而实现的传值方式. 这是 ...

  10. 面试阿里被“吊打”,一问Spring三不知,半年后二战终拿下offer

    Spring框架是一个为Java应用程序的开发提供了综合.广泛的基础性支持的Java平台.Spring帮助开发者解决了开发中基础性的问题,使得开发人员可以专注于应用程序的开发. 近两年来,许多大厂在面 ...