JavaScript的高级函数和DOM回流

目录

高级函数

回调函数

栈区

回调函数理解

自执行函数

函数闭包

 闭包模拟随机数

闭包模拟点赞

 闭包模拟私有属性

递归函数

 递归求 斐波那契数列

 快速排序

防抖和节流

防抖

节流

防抖和节流的区别 

call和apply

 应用

DOM回流

分类

DOM回流的数据绑定方式

  字符串拼接

  动态添加

 利用文档碎片


高级函数

回调函数

概念:将函数作为参数传递给另一个函数调用的函数(等某个动作完成后调用的函数),是解决异步操作的有效途径

字面上的理解,回调函数就是传递一个参数化的函数,就是将这个函数作为一个参数传到另一个主函数里面,当那一个主函数执行完之后,再执行传进去的作为参数的函数。走这个过程的参数化的函数 就叫做回调函数。换个说法也就是被作为参数传递到另一个函数(主函数)的那个函数就叫做 回调函数

栈区

主栈任务队列 和 等待任务队列

  • 同步:javaScript中先执行主栈任务队列中的代码 (除了以下几个异步的代码都放到主栈任务队列执行)

  • 异步:等主栈任务队列中的代码执行完毕,再去执行等待任务队列中的代码

异步:事件,定时器, (回调函数),Ajax请求

回调函数理解

	function title(value){//这是回调函数!!!!
			alert(value);
		}
		function main(title, value){//这个主函数:在参数列表中,title作为一个参数传递进来,也就是上文说的 参数化函数;然后value这个值正是title()函数中所需要的。
			alert("我是主函数");
			title(value);//结果为:'我是回调函数'。——————然后在这行这个title(),它就是回调函数咯。
		}
		main(title,"我是回调函数");//title参数加上()后,就会变回一个函数,并会被执行一次。
		//PS:看清楚,调用的是main()函数,意味着先执行main(),这时已经执行了主函数,title()被main()在函数体中执行了一次,因此title()是回调函数。
 <button onClick=test()>click me</button> 
  function a(callback) {
            alert("我是parent函数a!");
            alert("调用回调函数");
            callback();
        }

        function b() {
            alert("我是回调函数b");

        }

        function c() {
            alert("我是回调函数c");

        }

        function test() {
            a(b);
            a(c);
        }

自执行函数

  • 自执行函数 自己调用自己 立即执行

    • 标准语法:;(function(){做的事情})();

    • ;->可以有可以没有,防止前边有未执行完毕的代码

;(function(){
            console.log("哈哈");
        })();

        ~function(){
            console.log("111");
        }();

        !function(){
            console.log("222");
        }();

        +function(){
            console.log("333");
        }();

        -function(){
            console.log("444");
        }();

函数闭包

  • 闭包的概念:一个函数嵌套另一个函数,子函数可以访问当前这个函数的局部变量,在本质上,闭包是将函数内部和函数外部连接起来的桥梁

  • 闭包的优点和缺点:优点缓存数据,延长作用域链,优点即缺点,使用闭包需谨慎,可能导致内存泄漏

  • 闭包的本质是形成了一个不销毁的私有作用域

       全局作用域下的变量或函数只有浏览器关闭才会销毁

        函数执行形成私有作用域,执行完毕自动销毁

// 闭包
function test() {
    var n = 1;
    function test1() {
        n++;
        console.log(n);
    }
    return test1;
}

var resFn = test();

resFn();//2
resFn();//3
resFn();//4
resFn();//5

 

 闭包模拟随机数

 function test() {
            var random = Math.random();

            return function () {
                console.log(random);
            };

        }


        var resFn = test();

        resFn();
        resFn();
        resFn();

闭包模拟点赞

  var spans = document.querySelectorAll("span");


        // 利用自定义属性
        // for(var i = 0;i<spans.length;i++){
        //     spans[i].index = 0;
        //     spans[i].onclick = function(){
        //         this.index = this.index + 1;
        //         this.innerHTML = "赞:"+this.index;
        //     }
        // }


        // 闭包
        // zan这个函数形成了三个不销毁的私有作用域
        for (var i = 0; i < spans.length; i++) {
            // 赋值的是zan这个函数执行完成的返回值
            spans[i].onclick = zan();
        }


        function zan() {
            var n = 0;
            return function () {
                n++;
                this.innerHTML = "赞:" + n;
            }
        }

 

 闭包模拟私有属性

     // 闭包模拟私有属性


        function myUtils() {
            var a = 100;
            function test1() {
                console.log(a);
            }

            function test2() {
                console.log("haha");
            }

            function test3() {
                console.log("hehe");
            }

            return {
                fn1:test1,
                fn2:test2,
                fn3:test3
            }
        }


        var utils = myUtils();
        console.log(utils);//{fn1: ƒ, fn2: ƒ, fn3: ƒ}

        utils.fn1();//100
        utils.fn2();//haha
        utils.fn3();//hehe

递归函数

递归就是自己调用自己

 

  • 递去:自己调用自己的过程在递去的过程中形成的私有作用域不销毁

  • 归来:在归来过程中将值返回,然后这个私有作用域才销毁

function sum(n) {
if (n == 1) {  //递归结束的条件  没有结束条件报错 RangeError: Maximum call stack size exceeded
    return 1;
}
return n + sum(n - 1);
//3 + sum(2)
//3 + 2 + sum(1);
//3 + 2 +  1 =>6    

}
var res = sum(3);
console.log(res);

 递归求 斐波那契数列

  斐波那契数列(Fibonacci sequence),又称黄金分割数列,因数学家莱昂纳多·斐波那契(Leonardo Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列 1、1、2、3、5、8、13、21、34、 在数学上,斐波那契数列以如下被以递推的方法定义: F(n)=F(n - 1)+F(n - 2)

 // 求第n项的值
        function fn(n){
            if(n == 1 || n == 2){
                return 1;
            }
            return  fn(n-1)  + fn(n-2);
        }

        var res = fn(6);

        /*
            fn(6);
            fn(5) + fn(4)
            fn(4) + fn(3) + fn(3) + fn(2)
            fn(3) + fn(2) + fn(2) + fn(1)  + fn(2) + fn(1) + fn(2)
            fn(2) + fn(1) + fn(2) + fn(2) + fn(1)  + fn(2) + fn(1) + fn(2)
        =>    1   +   1   +  1    +    1     + 1   +   1     + 1    + 1  => 8
     
        */ 

        console.log(res);

 快速排序

 

var arr1 = [12, 13, 23, 14, 20, 26, 34, 13, 16];
// 快速排序
// 思想:取出当前数组中的中间项和其它的每一项进行比较,比中间项小的放到左边,大的放到右边(左边和右边是指一个数组)

function quickSort(arr) {
    if(arr.length <=1){
        return arr;
    }
    // 获取中间索引
    var pointIndex = Math.floor(arr.length / 2);
    // 获取中间项
    var pointVal = arr.splice(pointIndex, 1)[0];

    // 和其它的每一项进行比较,比中间项小的放到左边,大的放到右边(左边和右边是指一个数组)
    var left = [],right = [];
    for(var i = 0;i<arr.length;i++){
        var curVal = arr[i];
        curVal  < pointVal ? left.push(curVal):right.push(curVal);
    }
    return quickSort(left).concat(pointVal,quickSort(right));
}

var res =  quickSort(arr1);
console.log(res);

//快速排序原数组会少一项
console.log(arr1);

防抖和节流

在开发过程中我们经常绑定高频发事件,例如resize,scroll,mousemove,..我们可能不想让对应的事件处理函数实时触发,这个我们就要用到防抖和节流,防抖和节流都是用来做性能优化的。

防抖

在一个事件发生一定时间之后,才执行特定动作(这个动作完成,一定时间后才执行一次)。

  • 搜索框搜索输入。只需用户最后一次输入完,再发送请求;

  • 浏览器窗口大小改变后,只需窗口调整完后,再执行 resize 事件中的代码,防止重复渲染。

  • ...


function debounce(fun, awit) {
    var timer;
    return function () {
        if (timer) {  //看当前是否有定时器
            // 清楚定时器
            clearTimeout(timer);
        }
        timer = setTimeout(fun, awit);
    }
}
 window.onresize = debounce(getWin, 3000);
  // 获取窗口大小的函数
        function getWin() {
            var winW = document.documentElement.clientWidth;
            var winH = document.documentElement.clientHeight;
            console.log(winW, winH);
        }

节流

在一定时间之内,限制一个动作只执行一次 。 (过安检,30s,检查一个人)

  • 监听滚动条位置;

  • 防止高频点击提交,防止表单重复提交;

  • ...

  window.onscroll = throttle(getScroll, 3000);
        // 闭包
        function throttle(fun, awit) {
            var timer;
            return function () {
                // 判断
                if (!timer) { //判断没有定时器就设置一个
                    timer = setTimeout(function () {
                        getScroll();
                        timer = null;
                    }, 3000);
                }
            }
        }


        // 获取滚动距离
        function getScroll() {
            var scrollT = document.documentElement.scrollTop || document.body.scrollTop;

            console.log(scrollT);
        }

防抖和节流的区别 

  • 共同点:防抖和节流都要使用延迟定时器去完成,有利于性能优化

  • 不同点:防抖是这个动作(事件触发)完成,一定时间后才执行一次,节流,是多长事件就去做一次这个事件

call和apply

call和apply都是用来改变this指向的

  • 函数.call(_this,实参1,实参2,...);

    • 作用:首先将这个函数执行,然后把里边的thi变为第一个参数

    • _this:将这个函数中this变为谁第一个参数就传递谁

    • 实参:就是传递函数的实参

  • 函数.apply(_this,[实参1,实参2,...]);

    • 作用:首先将这个函数执行,然后把里边的thi变为第一个参数

    • _this:将这个函数中this变为谁第一个参数就传递谁

    • [实参1,实参2,...]  就是传递函数的实参 

   function test(a, b) {
            console.log(this);
            console.log(a, b);
        }



        test.call([10, 20, 30], 10000, 20000);
        test.call({ name: "哈哈" });
        test.call(1);
        test.call("呵呵", "hello", "wolrld");



        test.apply(true);
        test.apply(100, [100, 200]);

 应用

  /*
            求数组中最大值
        */ 

        var arr = [20,158,100,21,200,88,66];

        // var maxVal = arr[0];
        // for(var i = 0;i<arr.length;i++){
        //     if(maxVal < arr[i]){
        //         maxVal = arr[i];
        //     }
        // }
        // console.log(maxVal);

       var res =  Math.max.apply(Math,arr);
       console.log(res);

DOM回流

分类

  DOM回流可以分为重排 和 重绘

        重排 和 重绘:页面重新渲染 ,重排->页面整体重新渲染 重绘->局部渲染

        重排:HTML结构的width,height,位置,display:none,结构的操作(添加,删除,替换,...)都会引起重排

        重绘:背景颜色,文本颜色样式发生改变是局部渲染

        DOM回流只能尽量减少,不能避免

DOM回流的数据绑定方式

  字符串拼接

   优点:引发一次DOM回流

    缺点:innerHTML在进行字符串解析的时候,如果说字符串过长,解释时间导致过长,可能   出现页面空白或卡顿

 // 获取元素
        var ul = document.querySelector("ul");

        var str = '';
        // 字符串拼接
        for(var i = 0;i<arr.length;i++){
            str += '<li>'+arr[i].name+'</li>';
        }
        console.log(str);
        ul.innerHTML = str;

  动态添加

        优点:不会出现页面空白或卡顿由于字符串解析造成的这个问题

        缺点:发生多次DOM回流

  // 获取元素
        var ul = document.querySelector("ul");
        for(var i = 0;i<arr.length;i++){
            // 先创建li
            var li = document.createElement("li");
            li.innerHTML = arr[i].name;
            // 添加到ul中
            ul.appendChild(li);
        }

 利用文档碎片

      // 综合的方案
        var ul = document.querySelector("ul");
        // 文档碎片  相当于一个临时的容器
        var frg = document.createDocumentFragment();
        console.log(frg);
        for (var i = 0; i < arr.length; i++) {
            // 先创建li
            var li = document.createElement("li");
            li.innerHTML = arr[i].name;
            // 添加到ul中
            frg.appendChild(li);
        }
        ul.appendChild(frg);

        // 手动释放
        frg = null;



        console.log("getComputedStyle" in window);
        // IE8及以下  结果是false
        // var width = window.getComputedStyle(document.body).width;
        // console.log(width);

猜你喜欢

转载自blog.csdn.net/weixin_58139900/article/details/121458033