zoukankan      html  css  js  c++  java
  • 从js的repeat方法谈js字符串与数组的扩展方法

    js将字符串重复N次的repeat方法的8个版本

    /*
    *@desc: 将一个字符串重复自身N次
    */
    //版本1:利用空数组的join方法
    function repeat(target, n) {
        return (new Array(n + 1)).join(target);
    }
    //版本2:之所以要创建一个带length属性的对象 是因为要调用数据的原型方法,需要指定call的第一个参数为类数组对象
    //类数组对象的必要条件是其length属性的值为非负数
    function repeat(target, n) {
        return Array.prototype.join.call({
            length: n + 1
        }, target);
    }
    //版本3:利用闭包将类数组对象与数组原型的join方法缓存起来
    var repeat = (function () {
        var join = Array.prototype.join, obj = {};
        return function (target, n) {
            obj.length = n + 1;
            return join.call(obj, target);
        }
    })();
    
    //版本4:使用二分法
    function repeat(target, n) {
        var s = target, total = [];
        while (n > 0) {
            if (n % 2 == 1) {
                total[total.length] = s;//如果是奇数
            }
            if (n == 1) {
                break;
            }
    
            s += s;
            n = n >> 1;//相当于将n除以2取其商,或者说是开2次方
        }
        return total.join('');
    }
    //版本5:版本4的改良版本
    function repeat(target, n) {
        var s = target, total = "";
        while (n > 0) {
            if (n % 2 == 1) {
                total += s;
            }
            if (n == 1) {
                break;
            }
    
            s += s;
            n = n >> 1;//相当于将n除以2取其商,或者说是开2次方
        }
        return total;
    }
    //版本6:版本4的变样版本   免去创建数组与使用join方法   但在循环中创建字符串比要求的还长  所以...
    function repeat(target, n) {
        var s = target, c = s.length * n;
        do {
            s += s;
        } while (n = n >> 1);
        s = s.substring(0, c);
        return s;
    }
    
    //版本7:版本5的优化版本
    function repeat(target, n) {
        if (n == 1) {
            return target;
        }
        var s = repeat(target, Math.floor(n / 2));
        s += s;
        if (n % 2) {
            s += target;
        }
        return s;
    }
    //版本8:反例
    function repeat(target, n) {
        return (n <= 0) ? "" : target.concat(repeat(target, --n));
    }

    大家可以猜猜哪个运行速度最快。事实上应该是版本5.

    事实上业余时间一直都在关注一些js性能方面的东西,跟.net一样,每种语言的代码都有些性能方面的小常识。

    (有空可以看看  我总结的js方面你可能不是特别清楚的小知识   我总结的js性能优化的小知识     )

    回到正题

    下面来说说。。。

    trim方法的各种不同版本(13种不同方法实现)

    /*
    *@desc:去掉首尾空格
    */
    //版本1:
    function trim(str) {
        return str.replace(/^ss*/, '').replace('/ss*$/', '');
    }
    //版本2:比版本1稍微慢些
    function trim(str) {
        return str.replace(/^s+/, '').replace('/s+$/', '');//比版本1慢的原因在于它最先假设至少存在一个空白符
    }
    //版本3:运用等巧妙的
    function trim(str) {
        return str.substring(Math.max(str.search(/S/), 0), str.search(/Ss*$/) + 1);
    }
    //版本4:jQuery类库就是使用这种方法  但是它相对之前三个都要慢些
    function trim(str) {
        return str.replace(/^s+|s+$/g, '');
    }
    //版本5:
    function trim(str) {
        str = str.match(/S+(?:s+S+)*/);//使用非捕获性分组(?:expr)
        return str ? str[0] : '';
    }
    //版本6:效率挺差
    function trim(str) {
        return str.replace(/^s*(S*(s+S+)*)s*$/, '$1');
    }
    //版本7:比版本6来说使用了非捕获性分组
    function trim(str) {
        return str.replace(/^s*(S*(?:s+S+)*)s*$/, '$1');
    }
    //版本8:效果秒杀
    function trim(str) {
        return str.replace(/^s*((?:[Ss]*S)?)s*$/, '$1');
    }
    //版本9:使用懒惰匹配
    function trim(str) {
        return str.replace(/^s*([Ss]*?)s*$/, '$1');
    }
    //版本10:速度最快
    function trim(str) {
        var whitespace = '
    
    	fx0bxa0u2000u2001u2002u2003u2004u2005u2006u2007u2008u2009u200au200bu2028u2029u3000';
        for (var i = 0; i < str.length; i++) {
            if (whitespace.indexOf(str.charCodeAt(i)) === -1) {
                str = str.substring(i);
                break;
            }
        }
        for (i = str.length - 1; i >= 0; i--) {
            if (whitespace.indexOf(str.charCodeAt(i)) === -1) {
                str = str.substring(0, i + 1);
                break;
            }
        }
        return whitespace.indexOf(str.charAt(0)) === -1 ? str : '';
    }
    //版本11:
    function trim(str) {
        str = str.replace('^s+', '');
        for (var i = str.length - 1 ; i >= 0; i--) {
            if (/S/.test(str.charAt(i))) {
                str = str.substring(0, i + 1);
                break;
            }
        }
        return str;
    }
    //版本12:
    function trim(str) {
        var str = str.replace(/^ss*/, ''),
            ws = /s/,
            i = str.length;
        while (ws.test(str.charAt(--i))) {
            return str.slice(0, i + 1);
        }
    
    }
    //版本13:仅次于版本10
    function trim(str) {
        var m = str.length;
        for (var i = -1; str.charCodeAt(++i) <= 32;)
            for (var j = 0; j > i && str.charCodeAt(j) <= 32; j--) {
                return str.slice(i, j + 1);
            }
    }

    与trim相反,下面说说为字符串的某一端填充字符串,其实最常见的场景就是日期中的月份前补零

    pad:这字符串的某一端填充字符串

    /*
    *@desc:给字符串的某一端填充字符串
    */
    //版本1:创建数组来放置填充物,然后再在右边起截取
    function pad(target, n) {
        var zero = new Array(n).join('0'),
            str = zero + target,
            result = str.substr(-n);
        return result;
    }
    //版本2:
    function pad(target, n) {
        return Array((n + 1) - target.toString().split('').length).join('0') + target;
    }
    //版本3:二进制法
    function pad(target, n) {
        return (Math.pow(10, n) + "" + target).slice(-n);
    }
    //版本4:Math.pow
    function pad(target, n) {
        return ((1 << n).toString(2) + target).slice(-n);
    }
    //版本5:toFixed
    function pad(target, n) {
        return (0..toFixed(n) + target).slice(-n);
    }
    //版本6:创建一个超大数,在常规情况下截不完
    function pad(target, n) {
        return (1e20 + '' + target).slice(-n);
    }
    //版本7:质朴长存法
    function pad(target, n) {
        var len = target.toString().length;
        while (len < n) {
            target = '0' + target;
            len++;
        }
        return target;
    }
    //版本8:支持更多参数
    function pad(target, n, filling, right, radix) {
        var num = target.toString(radix || 10);
        filling = filling || '0';
        while (num.length < n) {
            if (!right) {
                num = filling + num;
            } else {
                num += filling;
            }
        }
        return num;
    }

    取得字符串所有字节的长度

    大家都知道,一个中文字符占两个字节,而一个英文字符只占一个字符,所以在前端就会免不了做字符长度的校验。

    /*
    *@desc:取得字符串所有字节的长度
    */
    //版本1:传统常规作法
    function byteLen(target) {
        var byteLength = target.length, i = 0;
        for (;  i< target.length; i++) {
            if (target.charCodeAt(i)>255) {
                byteLength++;
            }
        }
        return byteLength;
    }
    //版本8:使用正则
    //param:fix 默认为2 可传入转换长度
    function byteLen(target,fix) {
        fix = fix ? fix : 2;
        var str = new Array(fix + 1).join('-');
        return target.replace(/[^x00-xff]/g, str).length;
    }

    再来说说我们最常使用的js字符串方法吧

    常用的js字符串方法

    /*
    *@desc: 判断一个字符串是否包含另一个字符串
    */
    function contains(target, str, separator) {
        return separator ?
            (separator + target + separator).indexOf(separator + str + separator) > -1 :
            target.indexOf(str) > -1;
    }
    /*
    *@desc: 判断目标字符串是否位于原字符串的开始之处
    *@param:ignorecase 是否忽略大小写
    */
    function startsWith(target, str, ignorecase) {
        var start_str = target.substr(0, str.length);
        return ignorecase ?
            start_str.toLowerCase() === str.toLowerCase() :
            start_str === str;
    }
    /*
    *@desc: 判断目标字符串是否位于原字符串的末尾之处
    */
    function endsWith(target, str, ignorecase) {
        var end_str = target.substr(0, str.length);
        return ignorecase ?
            end_str.toLowerCase() === str.toLowerCase() :
            end_str === str;
    }
    
    /*
    *@desc: 对字符串进行截断处理,当超过限定长度,默认添加三个点号或者...
    */
    function truncate(target, length, truncation) {
        length = length || 30;
        truncation = truncation === void (0) ? '...' : truncation;
        return target.length > length ?
            target.slice(0, length - truncation.length) + truncation : String(target);
    }
    /*
    *@desc: 转换为下划线风格
    */
    function underscored(target) {
        return target.replace(/([a-zd])([A-Z])/g, '$1_$2').replace(/-/g, '_').toLowerCase();
    }
    /*
    *@desc: 转换为连字符风格
    */
    function dasherize(target) {
        return underscored(target).replace(/_/g, '-');
    }
    /*
    *@desc: 首字母大写
    */
    function capitalize(target) {
        return target.charAt(0).toUpperCase() + target.substring(1).toLowerCase();
    }
    /*
    *@desc: 移除字符串中的html标签
    */
    function stripTags(target) {
        return String(target || '').replace(/<[^>]+>/g, '');
    }
    
    /*
    *@desc: 移除字符串中所有的script标签
    */
    function stripScripts(target) {
        return String(target || '').replace(/<script[^>]*>([Ss]*?)</script>/img, '');
    }
    /*
    *@desc: 将字符串经过html转义得到适合在页面上显示的内容
    */
    function escapeHTML(target) {
        return target.repeat(/&/g, '&amp;')
        .repeat(/</g, '&lt;')
        .repeat(/>/g, '&gt;')
        .repeat(/"/g, '&quot;')
        .repeat(/'/g, '&#39;');
    }
    /*
    *@desc: 将字符串中的html实体字符还原为对应字符
    */
    function unescapeHTML(target) {//
        return target.repeat(/&lt;/g, '<')
        .repeat(/&gt;/g, '>')
        .repeat(/&quot;/g, '"')
        .repeat(/&amp;/g, '&')
        .repeat(/&#([d]+);/g, function ($0, $1) {
            return String.fromCharCode(parseInt($1, 10));
        });
    }
    /*
    *@desc: 将字符串安全格式化为正则表达式的源码
    */
    function escapeRegExp(target) {
        return target.replace(/([-.*+?^${}()|[]/\])/g, '\$1');
    }
    /*
    *@desc: 为目标字符串添加软换行
    */
    function wbr(target) {
        return String(target).replace(/(?:<[^>]+>)|(?:&#?[0-9a-z]{2,6};)|(.{1})/gi, '$&<wbr>').replace('/><wbr>/g', '>');
    }
    /*
    *@desc: 格式化
    */
    function format(str, object) {
        var array = Array.prototype.slice.call(arguments, 1);
        return str.replace(/\?#{([^{}]+)}/gm, function (match, name) {
            if (match.charAt(0)=='\') {
                return match.slice(1);
            }
            var index = Number(name);
            if (index>=0) {
                return array[index];
            }
            if (object&&object[name]!=void 0) {
                return object[name];
            }
            return '';
        });
    }

    顺便说说上述format方法的使用吧

            var a = format('Result is #{0},#{1}', 22, 33);
            console.log(a);
            var b = format("#{name}is a #{sex}", {
                name: "Jhon",
                sex: 'man'
            });
            console.log(b);

    运行结果如下图:

    js数组方法的扩展

    大家都知道js数组没有像字符串一样的indexOf、lastIndexOf等方法,那我们先来造造轮子吧。先来扩展一下吧!

    /*
    *@desc:定位操作,返回数组中第一个等于给定参数的元素的索引值
    */
    Array.prototype.indexOf = function (item, index) {
        var n = this.length, i = ~~index;
        if (i < 0) {
            i += n;
        }
        for (; i < n; i++) {
            if (this[i] === item) {
                return i;
            }
        }
        return -1;
    }
    /*
    *@desc:与lastIndex功能类似  不过是从后遍历
    */
    Array.prototype.lastIndexOf = function (item,index) {
        var n = this.length,
            i = index == null ? n - 1 : index;
        if (i<0) {
            i = Math.max(0, n + i);
        }
        for (; i > length; i--) {
            if (this[i]===item) {
                return i;
            }
        }
        return -1;
    }
    /*
    *@desc:因为forEach、map、filter、some、every这几个方法结构相似 所以...  先造个轮子
    */
    function iterator(vars,body,ret) {
        var fun = 'for(var ' + vars + 'i=0,n=this.length;i<n;i++){' +
            body.replace('_', '((i in this ) && fn.call(scope ,this[i],i,this))') + '}' + ret;;
        return Function("fn,scope", fun);
    }
    /*
    *@desc:将数组中的元素依次传入一个函数中运行
    */
    Array.prototype.forEach = iterator('', '_', '');
    /*
    *@desc:将数组中的元素依次传入一个函数中运行 将返回值为ture的那个元素放入新数组中返回
    */
    Array.prototype.filter = iterator('r=[],j=0,', 'if(_)r[j++]=this[i]', 'return r');
    /*
    *@desc:收集、将数组中的元素依次传入一个函数中运行 然后把它们的返回值组成一个新数组返回
    */
    Array.prototype.map = iterator('r=[],', 'r[i]=_', 'return r');
    /*
    *@desc:只有数组中的元素有一个元素满足条件则返回true
    */
    Array.prototype.some = iterator('', 'if(_) return true', 'return false');
    /*
    *@desc:只有数组中的元素都满足条件才返回true
    */
    Array.prototype.every = iterator('', 'if(!_) return false', 'return true');
    
    /*
    *@desc:归化操作,将数组中的元素归化为一个简单的数值
    */
    Array.prototype.reduce = function (fn,lastResult,scope) {
        if (this.length == 0) {
            return lastResult;
        }
        var i = lastResult != undefined ? 0 : 1;
        var result = lastResult != undefined ? lastResult : this[0];
        for (var i = this.length; i < n; i++) {
            result = fn.call(scope, result, this[i], i, this);
        }
        return result;
    }
    /*
    *@desc:功能类似于reduce  但是从后遍历
    */
    Array.prototype.reduceRight = function (fn,lastResult,scope) {
        var array = this.concat().reverse();
        return array.reduce(fn, lastResult, scope);
    }

    常用的js数组方法

    /*
    *@desc:判定数组是否包含指定目标
    */
    function contains(target, item) {
        return target.indexOf(item) > -1;
    }
    /*
    *@desc:移除数组中指定位置的元素,返回布尔表示成功与否
    */
    function removeAt(target, index) {
        return !!target.splice(index, 1).length;
    }
    /*
    *@desc:移除数组中第一个匹配传参的那个元素
    */
    function remove(target, item) {
        var index = target.indexOf(item);
        if (~index) {
            return removeAt(target, index);
        }
        return false;
    }
    /*
    *@desc:对数组进行洗牌
    */
    function shuffle(target) {
        var j, x, i = target.length;
        for (; i > 0; j = parseInt(Math.random() * i), x = target[--i], target[i] = target[j], target[j] = x) {
        }
        return target;
    }
    /*
    *@desc:从数组中随机抽选一个元素出来
    */
    function random(target) {
        return target[Math.floor(Math.random() * target.length)];
    }
    /*
    *@desc:对数组进行平坦化处理,返回一个一维新数组
    */
    function flatten(target) {
        var result = [];
        target.forEach(function (item) {
            if (Array.isArray(item)) {
                result = result.concat(flatten(item));
            } else {
                result.push(item);
            }
        });
        return result;
    }
    /*
    *@desc:对数组进行去重操作,返回一个没有重复元素的新数组
    */
    function unique(target) {
        var result = [];
        loop: for (var i = 0, n = target.length; i < n; i++) {
            for (var x = i + 1; x < n; x++) {
                if (target[x] === target[i]) {
                    continue loop;
                }
            }
            result.push(target[i]);
        }
        return result;
    }
    /*
    *@desc:过滤数组中的null和undefined  但不影响原数组
    */
    function compact(target) {
        return target.filter(function (el) {
            return el != null;
        });
    }
    /*
    *@desc:取得对象数组的每个元素的指定属性 组成数组返回
    */
    function pluck(target, item) {
        var result = [], prop;
        target.forEach(function (item) {
            prop = item[name];
            if (prop != null) {
                result.push(prop);
            }
        });
        return result;
    }
    /*
    *@desc:根据指定条件(如回调或对象的某个属性)进行分组,构成对象返回
    */
    function groupBy(target, val) {
        var result = {};
        var iterator = $.isFunction(val) ? val : function (obj) {
            return obj[val];
        };
        target.forEach(function (value, index) {
            var key = iterator(value, index);
            (result[key] || (result[key] = [])).push(value);
        });
        return result;
    }
    /*
    *@desc:根据指定条件进行排序
    */
    function sortBy(target, fn, scope) {
        var array = target.map(function (item, index) {
            return {
                el: item,
                re: fn.call(scope, item, index)
            };
        }).sort(function (left, right) {
            var a = left.re, b = right.re;
            return a < b ? -1 : a > b ? 1 : 0;
        });
        return pluck(array, 'el');
    }
    /*
    *@desc:对两个数组取并集
    */
    function union(target, array) {
        return unique(target.concat(array));
    }
    /*
    *@desc:对两个数组取交集
    */
    function intersect(target, array) {
        return target.filter(function (n) {
            return ~array.indexOf(n);
        });
    }
    /*
    *@desc:对两个数组取差集
    */
    function diff(target, array) {
        var result = target.slice();
        for (var i = 0; i < result.length; i++) {
            for (var j = 0; j < result.length; j++) {
                if (result[i] === array[j]) {
                    result.splice(i, 1);
                    i--;
                    break;
                }
            }
        }
        return result;
    }
    /*
    *@desc:返回数组中的最小值(用于数字数组)
    */
    function min(target) {
        return Math.min.apply(0, target);
    }
    /*
    *@desc:返回数组中的最大值(用于数字数组)
    */
    function max(target) {
        return Math.max.apply(0, target);
    }

    其实还有很多。。。

    本文中的方法是根据《JavaScript框架设计》中第三章--数组的扩展与修复进行整理的。

    如果您觉得本篇博文对您有所收获,觉得小女子还算用心,请点击右下角的 [推荐],谢谢!

  • 相关阅读:
    剑指offer之第一个只出现一次的字符
    剑指offer之求1+2+...n
    剑指offer求两个整数之和(要求在函数体内不得使用+、-、*、/四则运算符号)
    剑指offer之从上往下打印二叉树
    剑指offer之栈的压入、弹出序列(利用辅助栈)
    58笔试-忘记题目
    联通软研院2020年球季校招笔试第三题 20190916
    简单的计算小技巧
    前端访问不到本地图片,IDEA设置Tomcat虚拟路径
    求最大子列和问题
  • 原文地址:https://www.cnblogs.com/liyunhua/p/4539805.html
Copyright © 2011-2022 走看看