美文网首页
js背诵计划

js背诵计划

作者: 小柠有点萌 | 来源:发表于2020-08-12 10:03 被阅读0次
    谈谈你对闭包的理解,以及在项目中的应用!

    回答技巧

    1.阐述闭包是什么?(引申:堆栈、EC、AO、VO、SCOPE…)
    2.闭包的作用及在真实项目中的应用场景,以及所带来的问题!
    3.由闭包引发的高阶编程技巧
    4.突出自己在分析研究框架的源码(逼格更高的是自己写类库、插件的时候)是怎么应用这些东西的!
    回答如下:

    // 建议:浏览器加载页面会把代码放到栈内存中执行(也就是ECStack),函数进栈执行会产生一个私有的上下文(也就是EC),此上下文能保护里面的私有变量(也就是AO)不受外界的干扰,并且如果当前上下文中的某些内容,被上下文以外的内容所占用,当前上下文是不会出栈释放的,这样可以保存里面的变量和变量值,所以我认为闭包是一种保存和保护内部私有变量的机制…在真实的项目中,其实我应用闭包的场景还是很多的,例如:

    // 1. 我会基于闭包把自己编写的模块内容包起来,这样自己编写的代码都是私有的,防止和全局变量或者别人的代码冲突,这一点利用的是闭包的保护机制

    // 2. 在没有用LET之前,我们循环处理事件绑定,在事件触发需要用到索引值的时候,我们基于闭包,把每一轮循环的索引值保存起来,这样来实现我们的需求,只不过现在都是基于LET来完成,因为LET会产生块级作用域来保存需要的内容(机制和闭包类似)

    // 但是不建议过多使用闭包,因为形成不被释放的上下文,是占用栈内存空间的,过多使用会导致页面渲染变慢,所以要合理应用闭包

    // 除了这些传统的业务开发中会应用闭包,我之前在研究别人源码和自己写一写插件的时候,往往会利用一些JS高阶编程技巧来实现代码的管理和功能的开发,他们的底层机制其实就是闭包,例如:
    // 1. 惰性函数
    // 2. 柯理化函数
    // 3. compose函数
    // …

    call 和 apply的区别,哪一个性能更好

    1、call和applay都是Function原型上的公共方法,可以供给其实例调用,
    2、函数作为Function的实列都可以调用call和apply,都是把函数执行,并改变this的指向,他们唯一的区别在于call的第二参数开始是一个个传的,而apply是以数组的形式传的。
    3、和他们类似的方法还有bind,只不过bind不会立即执行函数,只是预先处理了this的指向,bind不兼容ie6-8。
    4、性能方面,经过网上大神的测试,call的性能要比apply好一些。尤其是传递的参数多余3个的时候。所以我们以后在真实项目中使用call要好一些。

    手写call方法(成员访问)

    // 如果CONTEXT是基本类型值,默认是不能设置属性的,此时我们需要把这个基本类型值修改为它对应的引用类型值(也就是构造函数的结果)
    Function.prototype.call = function call(context, ...params) {
    //【非严格模式下】不传或者传递NULL/UNDEFINED都让THIS最后改变为WINDOW
    context == undefined ? context = window : null;//context=context==undefined?window:context;

    // CONTEXT不能是基本数据类型值,如果传递是值类型,我们需要把其变为对应类的对象类型
        if (!/^(object|function)$/.test(typeof context)) {
            if (/^(symbol|bigint)$/.test(typeof context)) {
                context = Object(context);
            } else {
                context = new context.constructor(context);
            }
        }
        let key = Symbol('KEY'),
            result;
        context[key] = this;
        result = context[key](...params);
        delete context[key];
        return result;
    };
    
    

    4、手写bind方法(预处理=》柯理化函数)

    Function.prototype.bind = function bind(context = window, ...params) {
        //this->func
        let _this = this;
        return function anonymous(...inners) {
            //_this.call(context,...params,...inners);
            //_this.call(context,...params.concat(inners));
            //_this.apply(context, [...params,...inners]);        
            _this.apply(context, params.concat(inners));
        };
    };
    var obj = {name:'zhouyuxiang'};
    function func(){
        console.log(this,arguments);
        //=>当点击BODY的时候,执行func方法,输出:obj [100,200,MouseEvent事件对象]
    }
    document.body.onclick = func.bind(obj,100,200);
    

    COMPOSE函数

    在函数式编程当中有一个很重要的概念就是函数组合,
    实际上就是把处理数据的函数像管道一样连接起来, 然后让数据穿过管道得到最终的结果。 例如:

    const add1 = (x) => x + 1;
        const mul3 = (x) => x * 3;
        const div2 = (x) => x / 2;
        div2(mul3(add1(add1(0)))); //=>3
    

    而这样的写法可读性明显太差了,我们可以构建一个compose函数,它接受任意多个函数作为参数(这些函数都只接受一个参数),然后compose返回的也是一个函数,达到以下的效果:

    const operate = compose(div2, mul3, add1, add1)
        operate(0) //=>相当于div2(mul3(add1(add1(0)))) 
        operate(2) //=>相当于div2(mul3(add1(add1(2))))
    

    简而言之:compose可以把类似于f(g(h(x)))这种写法简化成compose(f, g, h)(x),请你完成 compose函数的编写

    const add1 = (x) => x + 1;
    const mul3 = (x) => x * 3;
    const div2 = (x) => x / 2;
    
    
    function compose(...funcs) {
        // funcs接收的就是所有传递进来的函数
        return function anonymous(val) {
            // val第一个函数执行时候需要的实参  0
            if (funcs.length === 0) return val;
            if (funcs.length === 1) return funcs[0](val);
            // return funcs.reverse().reduce((N, item) => {
            //  return typeof N === "function" ? item(N(val)) : item(N);
            // });
            return funcs.reverse().reduce((N, item) => {
                return item(N);
            }, val);
            //return funcs.reverse().reduce((N,item)=>item(N),val);//写成一行
        }
    }
    let result = compose(div2, mul3, add1)(5);
    console.log(result);
    

    reduce

    reduce只传递一个回调函数

    let arr = [10, 20, 30, 40]; 
    result = arr.reduce((N, item) => {
        // 第一次:10 20
        // 第二次:30 30
        // 第三次: 60 40
        // ...
        // reduce只传递一个回调函数,那么N第一次默认是第一项,后续的N是上一次函数执行的处理结果
        console.log(N, item);
        return N + item;
    });
    console.log(result);
    

    REDUCE的第二个参数就是给N赋值的初始值

    result = arr.reduce((N, item) => {
        console.log(N, item);
        return N + item;
    }, 0); //=>REDUCE的第二个参数就是给N赋值的初始值 ITEM从数组第一项开始遍历
    console.log(result);
    

    相关文章

      网友评论

          本文标题:js背诵计划

          本文链接:https://www.haomeiwen.com/subject/wlcofktx.html