JS基础之手写bind方法和手写深拷贝

  • Post author:
  • Post category:其他




手写bind方法

要说到bind方法,自然离不开说到他的两个兄弟call和apply了。他们三兄弟最主要的功能就是改变this的指向了,也就是改变对象的执行上下文,但彼此之间也有一些不同。

下面就细细道来~



call方法

call 的语法:

Function.call (obj,[param1...])
  • 调用 call 的对象,必须是个函数。
  • call 的第一个参数,是一个对象。 Function 的调用者,将会指向这个对象。如果不传,则默认为全局对象 window。
  • 第二个参数开始,可以接收任意个参数。每个参数会映射到相应位置的 Function 的参数上。但是如果将所有的参数作为数组传入,它们会作为一个整体映射到 Function 对应的第一个参数上,之后参数都为空。
  • 执行完 call 会立即执行函数

一个小例子:

function fn1() {
    console.log(this)
}
fn1() // window
fn1.call({ x: 100 }) // {x:100}
// 不但会绑定this还会执行fn1函数



apply方法

apply 的语法:

Function.apply(obj,[,argArray])
  • 调用 apply 的对象,必须是个函数。
  • 第二个参数,必须是数组或者类数组,它们会被转换成类数组,传入 Function 中,并且会被映射到 Function 对应的参数上。

    这也是 call 和 apply 之间,很重要的一个区别。
  • 执行完 apply 方法会立即执行函数



call和apply的用途

1.ES6 之前可以用来继承

2.计算最大最小值

let max = Math.max.apply(null, array);
let min = Math.min.apply(null, array);



bind方法

bind 的语法:

Function.bind(obj,[param1...])

bind方法和其他两兄弟基本相同,

除了

执行完不会立马执行。


一个小例子

function fn1(a, b, c) {
    console.log('this', this); // this {x: 100}
    console.log(a, b, c); //10 20 30
    return 'this is fn1'
}
const fn2 = fn1.bind({ x: 100 }, 10, 20, 30)
const res = fn2()
console.log(res); //this is fn1



手写bind方法

区分完三兄弟之后,开始手写bind方法,也是面试中经常会考到的手写代码啦

// 手写bind方法,即写一个自己的插件
// 由于原生bind方法的参数个数不一定(即除了绑定this的参数外,还有其他可能传入的参数)


Function.prototype.bind1 = function () {
    // 将参数拆解为数组
    const args = Array.prototype.slice.call(arguments, 0);
    // Array.prototype.slice.call(arguments)能将有length属性的对象转换为数组
    // 前提:arguments是一个具有length属性的对象
    // call 方法改变this指向
    // Array.prototype.slice 改变了 slice 只能操作对象的情况,并转化为数组
    // 最后返回一个数组


    //获取this(数组第一项)
    const t = args.shift();
    // shift方法是将数组的第一项去除并且返回剩余的内容作为新的数组
    // const a = [1, 2, 3, 4]
    // a.shift() // 1
    // console.log(a);//[2,3,4]


    //fn1.bind(...)中的fn1
    const self = this;


    //返回一个函数
    return function () {
        return self.apply(t, args);
    }
}

再结合刚才的例子

function fn1(a, b, c) {
    console.log('this', this); // this {x: 100}
    console.log(a, b, c); //10 20 30
    return 'this is fn1'
}
const fn2 = fn1.bind1({ x: 100 }, 10, 20, 30)
const res = fn2()
console.log(res); //this is fn1



手写深拷贝

深拷贝出现的原因就是在普通的拷贝(浅拷贝)下,对引用类型的数据会出现拷贝失败,因为只拷贝了引用数据,未拷贝地址。因此会出现更改数值两个对象都改变的情况,所以深拷贝的重要性不言而喻~

const obj1 = {
    age: 20,
    name: 'xxx',
    address: {
        city: 'beijing'
    },
    arr: ['a', 'b', 'c'],
};



/**
 * 深拷贝
 * @param {Object} obj  要拷贝的对象
 */
function deepClone(obj = {}) {
    if (typeof obj !== 'object' || obj == null) {
        // obj 是null,或者不是对象和数组,直接返回
        return obj;
    }


    // 初始化返回结果
    let result
    if (obj instanceof Array) {
        result = [];
    } else {
        result = {};
    };


    for (let key in obj) {
        // 保证 key 不是原型的属性
        if (obj.hasOwnProperty(key)) {
            // 递归调用
            result[key] = deepClone(obj[key]);
        };
    };


    // 返回结果
    return result
}




// 深拷贝
const obj2 = deepClone(obj1);

// 浅拷贝
// const obj2 = obj1;

obj2.address.city = 'wuhan'

console.log(obj1.address.city);
// 深拷贝结果为beijing,正确, 因为obj2改变不影响obj1的内容
// 浅拷贝结果为wuhan,错误,obj2的改变影响了obj1的内容

除了这种源码形式的深拷贝,还有两种形式:

  • JSON.parse(JSON.stringify(obj))
  • 使用 lodash 工具



版权声明:本文为weixin_44738381原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。