call

    1. Function.prototype.myCall = function(context){
    2. // 1.判断有没有传入要绑定的对象,没有默认是window,如果是基本类型的话通过Object()方法进行转换
    3. context = Object(context)||window
    4. /**
    5. 在指向的对象obj上新建一个fn属性,值为this,也就是fn()
    6. 相当于obj变成了
    7. {
    8. value: 'hdove',
    9. fn: function fn() {
    10. console.log(this.value);
    11. }
    12. }
    13. */
    14. context.fn = this
    15. // 取出传递的参数,第一个参数是this,截取除第一个参数之外剩余参数
    16. const args = [...arguments].slice(1)
    17. let result = ''
    18. result = context.fn(...args)
    19. delete context.fn
    20. return result
    21. }
    22. const obj = {
    23. value: 'hdove'
    24. }
    25. function fn(name, age) {
    26. return {
    27. value: this.value,
    28. name,
    29. age
    30. }
    31. }
    32. fn.myCall(obj, 'LJ', 25); // {value: "hdove", name: "LJ", age: 25}

    apply
    apply与call基本一致,只不过就是传递的参数不同。

    1. Function.prototype.myApply = function(context, args) {
    2. var context = Object(context) || window;
    3. context.fn = this;
    4. let result = '';
    5. //4. 判断有没有传入args
    6. if(!args) {
    7. result = context.fn();
    8. }else if(args instanceof Array) { //判断是否传递数组参数,如果不是则报错
    9. result = context.fn(...args);
    10. }else{
    11. throw new TypeError()
    12. }
    13. delete context.fn;
    14. return result;
    15. }
    16. const obj = {
    17. value: 'hdove'
    18. }
    19. function fn(name, age) {
    20. return {
    21. value: this.value,
    22. name,
    23. age
    24. }
    25. }
    26. fn.myApply(obj, ['LJ', 25]); // {value: "hdove", name: "LJ", age: 25}

    bind

    1. Function.prototype.myBind = function(context){
    2. let self = this
    3. // 第一个参数是this,截取掉
    4. let args1 = [...arguments].slice(1)
    5. //柯里化解决参数丢失的问题
    6. let bindFn = function(){
    7. // 获取调用时传入的参数
    8. let args2 = [...arguments]
    9. /**
    10. 这里我们通过打印this,我们可以看出来。
    11. 当这个绑定函数被当做普通函数调用的时候,this其实是指向window。
    12. 而当做构造函数使用的时候,却是指向这个实例,所以this instanceof bindFn为true,这个实例可以获取到fn()里面的值。
    13. 我们可以再fn里面添加一个属性test.
    14. 如果按照之前的写法 打印出来的是undefined,正好验证了我们上面所说的this指向的问题。
    15. 所以解决方法就是添加验证,判断当前this
    16. 如果 this instanceof bindFn 说明这是new出来的实例,指向这个实例, 否则指向context
    17. */
    18. console.log(this);
    19. return self.apply(this instanceof bindFn?this:context,args1.concat(args2))
    20. }
    21. // 绑定原型
    22. function proFn(){} //创建新方法
    23. proFn.prototype = self.prototype //继承原型
    24. bindFn.prototype = new proFn() //绑定原型
    25. return bindFn
    26. }
    27. const obj = {
    28. value: 'hdove'
    29. }
    30. function fn(name, age) {
    31. this.test = '我是测试数据';
    32. }
    33. fn.prototype.pro = '原型数据';
    34. var bindFn = fn.myBind(obj, 'LJ', 25);
    35. var newBind = new bindFn();
    36. console.log(bindFn.__proto__ === fn.prototype); // false
    37. console.log(bindFn.pro); // "undefined"
    38. console.log(fn.prototype.pro); // "原型数据"

    防抖

    1. //模拟一段ajax请求
    2. function ajax(content) {
    3. console.log('ajax request ' + content)
    4. }
    5. function debounce(fun,delay){
    6. return function(args){
    7. let that = this
    8. let _args = args
    9. clearTimeout(fun.id)
    10. fun.id = setTimeout(function(){
    11. fun.call(that,_args)
    12. },delay)
    13. }
    14. }
    15. let inputb = document.getElementById('debounce')
    16. let debounceAjax = debounce(ajax, 500)
    17. inputb.addEventListener('keyup', function (e) {
    18. debounceAjax(e.target.value)
    19. })

    运行结果:
    手写call、apply、bind、防抖、节流、深拷贝、new - 图1

    加入了防抖以后,当你在频繁的输入时,并不会发送请求,只有当你在指定间隔内没有输入时,才会执行函数。如果停止输入但是在指定间隔内又输入,会重新触发计时。

    节流

    1. function throttle(fun, delay) {
    2. let last, deferTimer
    3. return function (args) {
    4. let that = this
    5. let _args = arguments
    6. let now = +new Date()
    7. if (last && now < last + delay) {
    8. clearTimeout(deferTimer)
    9. deferTimer = setTimeout(function () {
    10. last = now
    11. fun.apply(that, _args)
    12. }, delay)
    13. }else {
    14. last = now
    15. fun.apply(that,_args)
    16. }
    17. }
    18. }
    19. let throttleAjax = throttle(ajax, 1000)
    20. let inputc = document.getElementById('throttle')
    21. inputc.addEventListener('keyup', function(e) {
    22. throttleAjax(e.target.value)
    23. })

    运行结果:手写call、apply、bind、防抖、节流、深拷贝、new - 图2

    在不断输入时,ajax会按照我们设定的时间,每1s执行一次。

    深拷贝

    1. function deepClone(value) {
    2. if (value == null) return value;
    3. if (typeof value !== 'object') return value;
    4. if (value instanceof RegExp) return new RegExp(value);
    5. if (value instanceof Date) return new Date(value);
    6. // 要判断 value 是对象还是数组 如果是对象 就产生对象 是数组就产生数组
    7. let obj = new value.constructor;
    8. for(let key in value){
    9. obj[key] = deepClone(value[key]); // 看一看当前的值是不是一个对象
    10. }
    11. return obj;
    12. }

    new

    1. function _new(fn, ...arg) {
    2. const obj = Object.create(fn.prototype);
    3. const ret = fn.apply(obj, arg);
    4. // 如果函数返回 非空并是对象 返回 value 否则 返回 newObj
    5. return ret instanceof Object ? ret : obj;
    6. }