手写系列

224 阅读2分钟

这是我参与11月更文挑战的第6天,活动详情查看:2021最后一次更文挑战

本篇文章是一些手写题目相关的学习笔记,持续更新。

call

主要思想:将函数作为当前绑定对象的属性方法进行调用,之后再销毁。

Function.prototype.call = function(ctx) {
    ctx.fn = this;
    ctx.fn();
    delete ctx.fn;
}

需要额外考虑的点:

  1. this 参数可以传 null,当为 null 的时候,视为指向 window
  2. this 参数需要是对象
  3. 函数有返回值
  4. 除了 this,还可以有其它参数
Function.prototype.call = function(ctx) {
    var ctx = (ctx == null ? window : Object(ctx));
    ctx.fn = this;
    
    var args = [];
    for (let i = 1; i < arguments.length; i++) {
        args.push('arguments[' + i + ']');
    }
    var result = eval('ctx.fn(' + args +')');
    
    // 或者用 es6 相关语法实现
    // var args = Array.from(arguments).slice(1);
    // var result = ctx.fn(...args);
   
    delete ctx.fn;
    return result;
}

apply

apply 的实现与 call 类似,只是参数传递方式不同。

Function.prototype.apply = function (ctx, arr) {
    var ctx = ctx == null ? window : Object(ctx);
    ctx.fn = this;

    var result;
    if (!arr) {
        result = ctx.fn();
    } else {
        var args = [];
        for (var i = 0, len = arr.length; i < len; i++) {
            args.push('arr[' + i + ']');
        }
        result = eval('ctx.fn(' + args + ')')
        
        // 或者 es6 语法实现
        // result = ctx.fn(...arr);
    }

    delete ctx.fn
    return result;
}

bind

主要思想:以闭包的方式实现,将 this 存起来。

Function.prototype.bind = function (ctx) {
    var self = this;
    return function () {
        return self.apply(ctx);
    }
}

bind 的特点:

  1. 返回一个函数;
  2. 可以传入参数;
  3. 被 new 调用的时候,指定的 this 会失效,传入参数依然生效。
Function.prototype.bind = function(ctx) {
    if (typeof this !== "function") {
      throw new Error("只有 function 才能调用 bind");
    }
    
    var self = this;
    var args = [].slice.call(arguments, 1); // 把 绑定对象 和 参数 存在闭包变量中
    
    var fBound = function() {
        var bindArgs = [].slice.call(arguments);
        // 需要判断是否被 new 调用
        return self.apply(this instanceof fBound ? this : ctx, args.concat(bindArgs));
    };
    
    var fNOP = function() {};
    fNOP.prototype = this.prototype; // 避免修改 fBound.prototype 的时候修改了 this.prototype
    fBound.prototype = new fNOP();
    return fBound;
}

new 实现

由于 new 是操作符,这里我们通过函数的形式来实现。

new 的特点:

  1. 创建一个对象,并经过构造函数处理;
  2. 如果返回值是一个对象,则返回该对象,否则返回所创建的对象。

function objectFactory() {

    var obj = new Object(),

    Constructor = [].shift.call(arguments);

    obj.__proto__ = Constructor.prototype;

    var ret = Constructor.apply(obj, arguments);
    
    // 判断构造函数返回值是否是对象
    return typeof ret === 'object' ? ret : obj;

};

debounce(防抖)

防抖目的:让函数在多次调用时,停止调用一段时候后才真正执行。降低函数指定的频次。

简单实现:利用闭包和定时器实现。

function debounce(func, wait) {
    var timeout;
    return function () {
        clearTimeout(timeout)
        timeout = setTimeout(func, wait);
    }
}

需要考虑 this 指向和参数问题。

function debounce(func, wait) {
    var timeout;

    return function () {
        var context = this;
        var args = arguments;

        clearTimeout(timeout)
        timeout = setTimeout(function(){
            func.apply(context, args)
        }, wait);
    }
}

重要参考

冴羽的博客