swiper.js中的坑,你不知道的js


现在随着javascript日趋流行,编写可维护的js也变得重要,根据工作中遇到的问题,今天写下这遍文章供大家参考(会持续更新),大家如有问题可以沟通!

1,正确的检测数据类型

(1)大家都知道typeof可以返回一个用于识别其运算数类型的字符串;对于任何变量来说,使用typeof可以返回以下6中类型之一:

number,string,boolean;object;function;undefined;

不过,对于null来说,typeof返回 的却是object;因此我们进行简单封装下:

 function type(o){
        return (o===null) ? 'null' : typeof o;
    }
但是,一定要注意,typeof不能检测复杂的数据类型,以及各种特殊的用途的对象;如正则表达式对象;date对象;数学对象等;

如果,对于对象或数组,我们可以用constructor属性,该属性值引用的是原来构造该对象的函数,如果结合typeof和constructor,基本上可以检测数据类型;

(2)当然,使用constructor可以判断大部分的数据类型,但是对于null和undefined特殊值,就不能使用constrictor,因为使用javascript解释器会跑出异常,此时可以,先把值转为布尔值,如果为true,则说明不是null和undefined;然后再调用constructor

 var val = undefined;
    console.log(typeof val);//"undefined"
    console.log(val && val.constructor);//undefined
    var vals = null;
    console.log(typeof vals);//object
    console.log(vals && vals.constructor);//null

对于数值直接量,也不能直接使用constrctor,不过可以用小括号括起来,因为小括号运算符可以把树枝转换为对象;

 console.log((10).constructor);//function Number() { [native code] }

(3)使用toString()方法检测对象类型是最安全/最准确的;调用其方法,把对象转换为字符串,然后通过检测字符串中是否包含数组所特有的标志可以确定对象的数据类型;

其方法返回的字符串如下:

【object class】

其中objce是对象的通用类型。class表示对象内部类型,内部类型的名称与该对象的构造函数名对应;例如Array对象的class是“Array”;

但是,要获取对象的class值的唯一方法是调用Obect对象定义的toString()方法,因为不同对象都会预定一自己的toString(),所以不能直接调用,例如:

  var data = new Date();
    console.log(data.toString());//返回utc时间
要调用Object的toString()方法,可以先调用Object.prototype.toString对象的默认的toString()方法,再调用该函数的apply()方法在想要检测的对象上执行;

var data = new Date();
    console.log(Object.prototype.toString.apply(data));//[object Date]
 下面是一个完整的检测数据类型的方法:

  function typeOf(o){
      var _toString = Object.prototype.toString;
      var _type = {
          'undefiend':'undefiend',
          'number':'number',
          'boolean':'boolean',
          'string':'string',
          '[object Function]':'function',
          '[object RegExp]':'regexp',
          '[object Array]':'array',
          '[object Date]':'date',
          '[object Error]':'error'
      };
      return _type[typeof o] || _type[_toString.call(o)] || (o ? 'object':'null');
  }

  var a = Math.abs();
    console.log(typeOf(a));//number

上面的方法对于自定义的对象是无效的;这是因为自定义对象转换字符串后,返回的值时没有规律的;

2,正确处理javascript特殊值

(1)正确使用NaN和Infinity

为了方便检测NaN值,js提供了静态方法isNaN,以辨数字和NaN的区别;

其实判断一个值是否可以用数字的最佳方法是使用isFinite函数,因为它会先刷掉NaN和Infinity(无穷大);使用inFinite函数能够检测NaN,正负无穷大;不幸的是,它会试图把它的运算数转换为一个数字,因此,如果值不是一个数字,使用isFinite不是一个有效的检测方法,不过,我们可以封装下isNumber函数

    var isNumber = function(val){
        return typeof val === 'number' && isFinite(val);
    };
    console.log(isNumber('11'));//false

(2)谨慎使用伪数组

要判断一个值是否为数组,必须使用constructor属性

    var value = [];
    if(value && typeof value === 'object' && value.constructor === Array){
        console.log('true');//true
    }else{
        console.log('false')
    }

3,推荐提高条件性能的策略

由于不同的浏览器对流控制进行了不同的优化,因此两者在性能上并没有特别大的差异,主要还是根据需求形式进行分析和选择:条件数量越大,建议选择switch结构,而不是if语句,这样可以使代码更容易读;如果条件少的话,建议if语句结构;
(1)小心if嵌套的思维陷阱
if(a){
        if(b){
            if(c){
                if(d){
                    alert('所有条件都成立!');
                }else{
                    alert('条件d不成立!')
                }
            }else{
                alert('条件c不成立!');
            }
        }else{
            alert('条件b不成立!');
        }
    }else {
        alert('条件a不成立!');
    }
从思维方向上来说,这种结构嵌套并没有错误;但是,如果这种多重结构嵌套,就会出现另外一种可能:a条件不成立,直接退出,而不管b,c,d条件是否成立,狠武断,给测试带来伤害;未来避免上述情况,我们可以采用排除法,即对每个条件进行判断,条件成立再执行特定的操作;
    var flag = true;
    if(!a){
        alert('条件a不成立');
        flag = false;
    }
    if(!b){
        alert('条件b不成立');
        flag = false;
    }
    if(!c){
        alert('条件c不成立');
        flag = false;
    }
    if(!d){
        alert('条件d不成立');
        flag = false;
    }
    if(flag){
        alert('所有条件都成立!');
    }
(2)获取字节长度问题
String对象的length属性可以返回子节的长度,无论是单子节,还是双子节,都可以计算;
    String.prototype.sumLength = function(){
        var _b = 0,_l = this.length;
        if(_l){
            for(var i= 0;i<_l;i++){
                if(this.charCodeAt(i) > 255){
                    _b +=2;
                }else{
                    _b +=1;
                }
            }
            return _b;
        }else{
            return 0;
        }
    }
    var s = '我是andy';
    console.log(s.sumLength());//8
(3)正确的检测数组类型
前面我讲过,可以简单通过判断来确定数组,不过,它不能正确的判断从不同窗口或帧中构造的数组,要想正确的判断,可食用以下的方法:
    var isArray = function(val){
        return Object.prototype.toString.apply(val) === '[object Array]';
    }

4,使用arguments模拟重载
在js中,每一个函数内部都有arguments对象,该对象包涵了函数被调用时的实际参数值,arguments只是类数组,其原因是arguments对象的prototype指向的是Object.prototype,而不是Array.prototype;
Java和C++语言都支持方法重载,即允许出现名称相同而参数不同的方法,但是js不支持这种方式的重载;这是因为js中的function对象也是以属性的形式出现的,在一个对象总增加一个与已有的function同名的新的function是,旧的fucntion会被覆盖;不过,我们可以通过argumetns来模拟重载,其实现机制是通过argumetns中实际参数的个数和类型来执行不同的逻辑;
    function sayHello(){
        switch (arguments.length){
            case 0:
                return 'Hello';
            case 1:
                return 'Hello,'+arguments[0];
            case 2:
                return (arguments[1] == 'cn' ? '你好,':'Hello, ') + arguments[0];
        }
    }
    console.log(sayHello());
    console.log(sayHello('andy'));
    console.log(sayHello('andy','cn'));

4,比较函数调用模式

在js中,有四种调用模式:方法调用模式,函数调用模式,构造器调用模式和apply调用模式;这些模式在如何初始化关键参数this上存在差异;
(1)方法调用模式
当一个函数被保存对象的一个属性时,将成为一个方法;当一个方法被调用时,this被绑定到该对象,如果一个调用表达式包涵一个属性存取表达式(即一个点表达式或下标表达式),那么它被当做一个方法来调用;
    var obj = {
        value:0,
        increment:function(inc){
            this.value += typeof inc === 'number' ? inc :1;
        }
    };
    obj.increment();
    console.log(obj.value);//1
    obj.increment(2);
    console.log(obj.value);//3

(2)函数调用模式
当一个函数并非一个对象的属性的时候,它将被当作一个函数来调用:
当函数以次模式调用时,this被绑定到全局对象.这无非是语言设计上的错误.当若语言设计正确,当内部函数被调用时,
this应该仍绑定到外部函数的this变量.不过,有一个狠容易的解决方法:如果改办法定义一个变量并将它赋值为this,
那么内部函数就可以通过这个变量访问this
    var obj = {
        value:1,
        doub:function(){
            var that  = this;
            var helper = function(){
                that.value = that.value *2;
            };
            helper();
        }
    }
    obj.doub();
    console.log(obj.value);//2
(3)构造器调用模式
js是一门基于原型继承的语言,改语言是无类别的,对象可以直接从其它对象继承属性.
    当今大多数语言都是基于类的语言,虽然原型继承有着强大的表现力,但它偏离来主流用法,不被广泛理解;
    js为了能够兼容基于类语言的编写风格,提供来一套基于类语言的对象构建语法;
    如果在一个函数前面加上new运算符来进行调用,那么将创建一个隐藏链接到该函数的prototype原型对象的新实例对象,
    同时this将会被绑定到这个新实例对象上,注意,new前缀也会改变return语句的行为;
    var F = function(string){
        this.status = string;
    };
    F.prototype.getNum = function(){
        return this.status;
    };
    var f = new F('andy');
    console.log(f.getNum());//andy
(4)apply调用模式
js是函数式的面向对象编程语言,函数可以拥有方法.apply就是函数的一个基本方法,
使用这个方法可以调用函数,并修改函数体内的this值;
apply方法包括两个参数:第一个参数设置绑定给this的值;第二个参数是包涵参数的数组;
 var array = [5,4];
    var add  = function(){
        var i,sum = 0;
        for(i=0;i<arguments.length;i+=1){
            sum += arguments[i];
        }
        return sum;
    };
    var sum = add.apply({},array);
    console.log(sum);//9
上面代码构建一个包涵两个数字的数组,然后使用apply方法调用add()函数,将数组array中的元素值相加.
    var F = function(string){
        this.status = string;
    };
    F.prototype.get = function(){
        return this.status;
    };
    var obj = {
        status :'objh'
    };
    var status = F.prototype.get.apply(obj);
    console.log(status);//objh
上面代码构建了一个构造函数F,为该函数定义了一个原型方法get,该方法能够读取当前对象当status属性的值.
然后定义了一个obj对象,改对象包涵了一个status属性,使用apply方法在obj对象上调用构造函数F的get方法,
返回obj对象的status属性值

5,使用闭包跨域开发

闭包是指词法表示包括不必计算的变量的函数,必包函数能够使用函数外定义的变量;
闭包结构有两个比较鲜明的特征:
(1)封闭性
(2)持久性
对于一般函数来说,调用后会注销掉,而对于闭包来说,在外部函数被调用后,闭包依然保存在系统中,闭包的函数依然存在,
从而实现对数据对持久使用。
    function bar(x){
        var a = x;
        var b = function(){
            return a;
        };
        return b;
    }
    var c1 = bar(1);
    console.log(c1());//1---调用闭包函数
    //在上面实例中,首先在函数bar结构体内定义两个变量,分别存储参数和必报结构,而闭包结构中
    //寄存着参数值.当调用函数bar之后,函数结构被注销,它对局部变量也会跟着注销掉,因此变量a中
    //存储的参数值也会随之丢失.但是由于变量b存储着必报结构,因此闭包结构内部的参数值并没有释放,
    //在调用函数之后,依然能够从闭包中读取到参数值


    function f(x){//外部函数
        var a = x;//外部函数的局部变量,并把参数值传递给它
        var b = function(){//内部函数
            return a;//访问外部函数中的局部变量
        };
        a++;//访问后,动态更新外部函数的变量
        return b;//内部函数
    }
    var c = f(5);//调用外部函数,并赋值
    console.log(c(5));//调用内部函数,返回外部函数更新后的值6

    //如果没有闭包函数的作用,那么这种数据寄存和传递就无法得以实施:
    function f2(x){
        var a = x;
        var b = a;
        a++;
        return b;
    }
    var c2 = f2(5);
    console.log(c2);//5

6,推荐链式调用方法

在js中,很多方法没有返回值,一些设置或修改对象的某个状态却不返回任何值的方法就是典型的例子;
如果让这些方法返回this,而不是undefiend,那么就要启用级联功能,即所谓的链式语法.
在一个级联中,单独一条语句可以连续调用同一个对象的很多方法
如下面扩展String的3个方法
Function.prototype.method = function(name,func){
        if(!this.prototype[name]){
            this.prototype[name] = func;
            return this;
        }
    };
    String.method('trim',function(){
        return this.replace(/^\s+|\s+$/g,'');
    });
    String.method('writeIn',function(){
        console.log(this);
        return this;
    });
    String.method('alert',function(){
        window.alert(this);
        return this;
    });
    var str = ' abc ';
    str.trim().writeIn().alert();


    //延伸
    Function.prototype.method = function(name,func){
        if(!this.prototype[name]){
            this.prototype[name] = func;
            return this;
        }
    };
    function Person(name){
        this.name = name;
    }
    Person.method('hide',function(){
        console.log(this.name);
        return this;
    });
    Person.method('writeIn',function(){
        console.log(2);
        return this;
    });
    Person.method('alert',function(){
        console.log(3);
        return this;
    });
    var p = new Person('andy');
    p.trim().writeIn().alert();

7,用局部变量访问集合元素

一般来说,访问任何类型的dom,当同一个dom属性或方法被访问一次以上,最好使用一个局部变量
缓存改dom成员.当遍历一个集合时,第一个要优化的是将集合引用存储与局部变量,并在循环之外
缓存length属性.然后,如果在循环体中多次访问同一个集合元素,那么使用局部变量缓存它.
下面例子,循环访问每个元素的3个属性.执行最慢的方法是每次都要访问全局变量document,
优化后的代码缓存了一个指向集合的引用,执行最快的方法是将集合的当前元素存入局部变量.
    //较慢
    function collGlobal(){
        var coll = document.getElementsByTagName('b'),len = coll.length,name = '';
        for(var i = 0;i<len;i++){
            name = document.getElementsByTagName('b')[i].nodeName;
            name = document.getElementsByTagName('b')[i].nodeType;
            name = document.getElementsByTagName('b')[i].tagName;
        }
        return name;
    }
    //较快
    function collLocal(){
        var coll = document.getElementsByTagName('b'),len = coll.length,name = '';
        for(var i = 0;i<len;i++){
            name = coll[i].nodeName;
            name = coll[i].nodeType;
            name = coll[i].tagName;
        }
        return name;
    }
    //最快
    function collNodesLocal(){
        var coll = document.getElementsByTagName('b'),len = coll.length,name = '',el =null;
        for(var i = 0;i<len;i++){
            el = coll[i];
            name = el.nodeName;
            name = el.nodeType;
            name = el.tagName;
        }
        return name;
    }

8,推荐使用css选择器

使用css选择器是一个便捷的确定节点的方法,这是因为大家已经对css狠熟悉了;
许多js库为此提供了API,而且,最新对浏览器提供了一个名为querySelectorAll()原生浏览器DOM函数.
显然这种方法比使用js和dom迭代并缩小元素列表对方法要快;
    var b = document.querySelectorAll('.name b');
    //如果不使用querySelectorAll,达到同样对目的,代码会沉长些
    var b = document.getElementsByClassName('names')[0].getElementsByTagName('b');
    console.log(b);

    //当需要联合查询时,使用querySelectorAll()更加遍历

    var errs = document.querySelectorAll('div.uzais,div.nums');


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值