zoukankan      html  css  js  c++  java
  • JS的进阶技巧

    前言

    你真的了解JS吗,看完全篇,你可能对人生产生疑问。

    typeof

    typeof运算符,把类型信息当做字符串返回。

    //正则表达式 是个什么 ?
    
    typeof /s/     // object
    
    //null
    
    typeof null   // object

    正则表达式并不是一个‘function’,而是一个object。在大多数语言中,null 代表的是一个空指针(0x00),但是在js中,null为一个object。

    instanceof

    instanceof运算符,用来测试一个对象在其原型链中是否存在一个构造函数:prototype

    //语法 object instanceof constructor 
    
    function Person(){};
    var p =new Person();
    p instanceof Person;  //true

    [] instanceof window.frames[0].Array  // false

    因为 Array.prototype !== window.frames[0].Array.prototype ,因此,我们必须使用Array.isArray(obj)或者Object.prototype.toString.call(obj) === "[object Array]" 来判断obj是否为数组。

    Object.prototype.toString

    根据上面提到的,可以使用该方法获取对象类型的字符串。

    //call的使用可以看博主前面的文章。(使用apply亦可)
    
    var toString = Object.prototype.toString;
    
    toString.call(new Date); // [object Date]
    toString.call(new String); // [object String]
    toString.call(Math); // [object Math]
    toString.call(/s/); // [object RegExp]
    toString.call([]); // [object Array]
    toString.call(undefined); // [object Undefined]
    toString.call(null); // [object Null]

    作用域安全与构造函数

    构造函数:使用new调用的函数,当使用new调用时,构造函数内的this会指向新创建的对象实例。

    function Dog(name, age){
        this.name = name;
        this.age = age;
    }
    
    let dog = new Dog("柴犬", 5);
    
    dog.name // 柴犬

    如果我们没有使用new又会如何?

    let dog = Dog("柴犬", 5);
    
    dog.name // undefined
    window.name // 柴犬

    这是因为在没有使用new关键字时,this在当前情况被解析成window,所以属性就被分配到window上了。

    function Dog(name, age){
        if(this instanceof Dog){
            this.name = name;
            this.age = age;     
        }else{
            return new Dog(name, age);
        }
    }
    
    let dog1 = new Person("柴犬", 5);
    dog1.name // 柴犬
    
    let dog2 = Dog("柯基犬", 20);
    dog2 .name // 柯基犬

    使用上面的方法,就可以再不使用new的情况下,正常构造函数。

    惰性载入函数

    一个函数如下:

    function foo(){
        if(a != b){
            console.log('111') //返回结果1
        }else{
            console.log('222') //返回结果2
        }
    }

    ab是不变的,那么无论执行多少次,结果都是不变的,但是每一次都要执行if判断语句,这样就造成了资源浪费

    而惰性载入函数,便可以解决这个问题。

    function foo(){
        if(a != b){
            foo = function(){
                console.log('111')
            }
        }else{
            foo = function(){
                console.log('222')
            }
        }
        return foo();
    }
    var foo = (function foo(){
        if(a != b){
            return function(){
                console.log('111')
            }
        }else{
            return function(){
                console.log('222')
            }
        }
    })();

    如上函数所示:第一次执行后便会对foo进行赋值,覆盖之前的函数,所以再次执行,便不会在执行if判断语句。

    fun.bind(thisarg[,arg1[,arg2[,....]]])绑定函数

    thisarg:当绑定函数被调用时,该参数会作为原函数运行时的this指向。当使用new时,该参数无效。

    arg:当绑定时,这些参数将传递给被绑定的方法。

    例子:

    let person = {
       name: 'addone',
       click: function(e){
           console.log(this.name)
       }
    }
    
    let btn = document.getElementById('btn');
    EventUtil.addHandle(btn, 'click', person.click);

    这里创建了一个person对象,然后将person.click方法分配给DOM,但是当你按按钮时,会打印undefied,原因是this指向了DOM而不是person

    解决方法,当时是使用绑定函数了:

     EventUtil.addHandle(btn, 'click', person.click.bind(person));

    函数柯里化

    柯里化是把接受多个参数的函数转变成接受单一参数的函数。

    //简单例子,方便你明白柯里化
    function add(num1, num2){
        return num1 + num2;
    }
    function curryAdd(num2){
        return add(1, num2);
    }
    add(2, 3) // 5
    curryAdd(2) // 3

    下面是柯里化函数的通用方法:

    function curry(fn){
        var args = Array.prototype.slice.call(arguments, 1);
        return function(){
            let innerArgs = Array.prototype.slice.call(arguments);
            let finalArgs = args.concat(innerArgs);
            return fn.apply(null, finalArgs);
        }
    }

    Array.prototype.slice.call(arguments, 1)来获取第一个参数后的所有参数。在函数中,同样调用 Array.prototype.slice.call(arguments)innerArgs存放所有的参数, 然后用contact将内部外部参数组合起来,用apply传递函数。

    function add(num1, num2){
        return num1 + num2;
    }
    let curryAdd1 = curry(add, 1);
    curryAdd1(2); // 3
    
    let curryAdd2 = curry(add, 1, 2);
    curryAdd2(); // 3

    不可扩展对象

    默认情况下对象都是可扩展的,无论是扩展属性或是方法。

    let dog = { name: '柴犬' };
    dog.age = 5;

    如第二行,我们为dog扩展了age属性。

    使用Object.preventExtensions()可以阻止扩展行为。

    let dog = { name: '柴犬' };
    Object.preventExtensions(dog);
    dog.age = 20;
    
    dog.age // undefined

    还可以使用 Object.isExtensible()来判断对象是否支持扩展。

    let dog = { name: 'addone' };
    Object.isExtensible(dog); // true
    
    Object.preventExtensions(dog);
    Object.isExtensible(dog); // false。

    密封的对象

    密封后的对象不可扩展,且不能删除属性和方法。

    使用Object.seal()来进行密封。

    let dog = { name: '柴犬' };
    Object.seal(dog);
    
    dog.age = 20;
    delete dog.name;
    
    dog.age // undefined
    dog.name // 柴犬

    当然也有Object.isSealed()来判断是否密封

    let dog = { name: '柴犬' };
    Object.isExtensible(dog); // true
    Object.isSealed(dog); // false
    
    Object.seal(dog);
    Object.isExtensible(dog); // false
    Object.isSealed(dog); // true

    冻结对象

    冻结对象为防篡改级别最高的,密封,且不能修改。

    使用Object.freeze()来进行冻结。

    let dog= { name: '柴犬' };
    Object.freeze(dog);
    
    dog.age = 20;
    delete dog.name;
    dog.name = '吉娃娃'
    
    dog.age // undefined
    dog.name // 柴犬

    当然也有Object.isFrozen()来判断是否冻结

    let dog = { name: '柴犬' };
    Object.isExtensible(dog); // true
    Object.isSealed(dog); // false
    Object.isFrozen(dog); // false
    
    Object.freeze(dog);
    Object.isExtensible(dog); // false
    Object.isSealed(dog); // true
    Object.isFrozen(dog); // true

    数组分块

    浏览器对长时间运行的脚本进行了制约,如果运行时间超过特定时间或者特定长度,便不会继续执行。

    如果发现某个循环占用了大量的时间,那么就要面对下面两个问题:

    1.该循环是否必须同步完成?

    2.数据是否必须按顺序完成?

    如果是否,那么我们可以使用一种叫做数组分块的技术。基本思路是为要处理的项目创建一个列队,然后使用定时取出一个要处理的项目进行处理,以此类推。

    function chunk(array, process, context){
        setTimeout(function(){
            // 取出下一个项目进行处理
            let item = array.shift();
            process.call(item);
            
            if(array.length > 0){
                setTimeout(arguments.callee, 100);
            }
        }, 100)
    }

    这里设置三个参数,要处理的数组,处理的函数,运行该函数的环境。

    节流函数

    节流函数目的是为了防止某些操作执行的太快,比如onresize,touch等事件。这种高频率的操作可能会使浏览器崩溃,为了避免这种情况,可以采用节流的方式。

    function throttle(method, context){
        clearTimeout(method.tId);
        method.tId = setTimeout(function(){
            method.call(context);
        }, 100)
    }

    这里接收两个参数,要执行的函数,和执行的环境。执行时先clear之前的定时器,然后将当前定时器赋值给方法的tId,之后调用call来确定函数的执行环境。

    function resizeDiv(){
        let div = document.getElementById('div');
        div.style.height = div.offsetWidth + "px";
    }
    
    window.onresize = function(){
        throttle(resizeDiv);
    }
  • 相关阅读:
    Qt学习笔记12:基本会话框4——总结
    Python游戏-实现键盘控制功能
    Unity c# 状态机的简单入门
    Unity3D windows平台视频录制录屏插件 UnityRecorder
    kubeadm安装Kubernetes13.1集群-三
    windows server安装zabbix-agent
    teamviewer被识别为商业用途
    Linux入侵检测工具
    k8s-StatefulSet控制器-十四
    k8s-存储卷2-configMap-Secret-十三
  • 原文地址:https://www.cnblogs.com/peiyu1988/p/9042313.html
Copyright © 2011-2022 走看看