JavaScript 自己写一个 replaceAll() 函数

更新日期: 2019-09-07 阅读: 2.7k 标签: 函数

JavaScript 的  replace()  方法可以在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串。

但是,只输入字符串的话,仅替换第一个字符,当然也可以用正则表达式来进行全局替换:

// 查找所有 word 替换成 words
string.replace(/word/g,"words");

那么,问题来了,如果我用的是变量呢?百度到可以这么来:

// 随便来一条字符串
let str = "How old are you? Yes, I'm very old!"
let search = "old";
// 使用 new RegExp(pattern,modifiers) 创建正则表达式
let pattern = new RegExp(search, "g");
let str = text.value.replace(pattern, "young");
// 结果:How young are you? Yes, I'm very young!

但是,不用 new RegExp 自己写一个函数,要怎么实现呢(我果然是太闲了)?

首先,摒弃掉 replace() 函数,自己来替换。

替换的话,不就是从前往后找想要替换的文并且一个个换掉嘛。

思路是这样的,用 indexOf() 方法返回指定的字符串在字符串中首次出现的位置,并用 slice(start, end)  方法提取找过但没有匹配项的,匹配的文字和还没找的三个部分,将第一部分和替换文字放入数组中,还没找的部分中再次使用这种办法,直至字符串末尾,将数组连起来成为一条字符串就是我想要的结果啦。

这是仅一次替换,咦,这不就是 replace() 嘛:

// 用于存放文字的数组
let array = [];
let data;
// 查询的文字第一次出现的位置
let start = oldText.indexOf(searchValue);
// 没有找到匹配的字符串则返回 -1 
if (start !== -1) {
    // 查找的字符串结束的位置
    let end = start + searchValue.length;
    // 添加没有匹配项的字符,即从头到查询的文字第一次出现的位置
    array.push(oldText.slice(0, start));
    // 添加替换的文字来代替查询的文字
    array.push(replaceValue);
    // 剩下没有查询的文字
    let remaining = oldText.slice(end, oldText.length);
    // 这是结果
    data = array[0] + array[1] + remaining;
} else {
    // 没找到呀
    data = "No Found" + searchValue + "!";
}
let textNode = document.createTextNode(data);
span.appendChild(textNode);

接下来进行全局替换,使用 while 循环,判定条件就是 indexOf(searchValue) 是否能找到文字,返回 -1 就停止循环。

let array = [];
// 用于存放未查找的文字
let remaining = oldText;
let data;
let start = oldText.indexOf(searchValue);
while (start !== -1) {
    let end = start + searchValue.length;
    array.push(remaining.slice(0, start));
    array.push(replaceValue);
    remaining = remaining.slice(end, remaining.length);
    start = remaining.indexOf(searchValue);
}
14 // 这是结果
data = array.join("") + remaining;

接着,再进一步,实现使用正则表达式来进行全局替换,大致思路是先找到正则匹配项,放入一个数组,在循环遍历每一项,使用上面的方法进行全局替换。

要注意的是,替换的数组不能有重复项,否则有可能出现问题,比如我想把 old 替换成 older,如果有两个 old 在数组中,最后的结果就会变成 olderer 。

/**
 * 字符串的全局替换
 * @param oldText {string} 原始字符串
 * @param searchValue {string} 需要替换的字符串
 * @param replaceValue {string} 替换后的字符串
 * @returns {string} 返回结果
 */
function replaceAll(oldText, searchValue, replaceValue) {
    let result = oldText;
    // 检查是否是正则表达式
    // 如果是正则表达式,则获得匹配内容
    let search;
    if (searchValue) {
        // 首先去掉空格
        search = searchValue.match(/\S+/g)[0];
        // 匹配以 / 符号开头 以 /img 形式结尾的内容
        search = search.search(/^\/[\s\S]*?\/[img]$/g);
    } else {
        search = -1;
    }
    // 为了方便直接创建一个数组用来存放需要替换的值
    let searchArray = [];
    if (search !== -1) {
        let pattern = searchValue.slice(searchValue.indexOf("\/") + 1, searchValue.lastIndexOf("\/"));
        let modifiers = searchValue.slice(searchValue.lastIndexOf("\/") + 1, searchValue.length);
        // 防止正则写的有问题,或者只是写的像正则实际不是而导致的 nothing to repeat 报错。
        try {
            search = oldText.match(new RegExp(pattern, modifiers));
        } catch (e) {
            console.log(e);
            // 报错则默认为是需要替换的文本
            search = null;
            searchArray.push(searchValue);
        }
        if (search !== null) {
            // 匹配成功后去掉重复项
            search.forEach(function (item1) {
                // if(searchArray.includes(item1)){}
                // IE 不支持 array.includes() 所以自己写一个循环吧
                // 数组中有相同元素则为 true
                let alreadyIn = false;
                searchArray.forEach(function (item2) {
                    if (item1 === item2) {
                        alreadyIn = true;
                    }
                });
                if (!alreadyIn) {
                    searchArray.push(item1);
                }
            });
        } else {
            // 匹配失败也默认为是需要替换的文本
            searchArray.push(searchValue);
        }
    } else {
        // 不是正则表达式也需要添加进数组
        searchArray.push(searchValue);
    }
    // 来循环吧,把 search 里的每个元素换一遍,当然首先里面要有元素
    if (searchValue) {
        let remaining = result;
        searchArray.forEach(function (item) {
            // 将上一次替换结束的字符串赋值给未扫描的字符串变量
            remaining = result;
            let array = [];
            let start = remaining.indexOf(item);
            console.log(start);
            // 没有匹配项则返回源字符串
            if (start === -1) {
                result = remaining;
            }
            while (start !== -1) {
                let end = start + item.length;
                array.push(remaining.slice(0, start));
                array.push(replaceValue);
                remaining = remaining.slice(end, remaining.length);
                start = remaining.indexOf(item);
                result = array.join("") + remaining;
            }
        });
    }
    return result;
}


本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!

链接: https://fly63.com/article/detial/6311

相关推荐

JavaScript push() 方法详解

push() 方法主要用于向数组的末尾添加一个或多个元素,其返回值为添加后新的长度,即push后的数组长度,该值为number类型。介绍:一个数组中添加新元素、把一个数组的值赋值到另一个数组上、在对象使用push

什么是纯函数_以及为什么要用纯函数?

当我第一次听到 “纯函数 (Pure Function)” 这个术语的时候我很疑惑。常规的函数做错了什么?为什么要变纯? 为什么我需要纯的函数?除非你已经知道什么是纯函数,否则你可能会问同样的疑惑

让我们来创建一个JavaScript Wait函数

Async/await以及它底层promises的应用正在猛烈地冲击着JS的世界。在大多数客户端和JS服务端平台的支持下,回调编程已经成为过去的事情。当然,基于回调的编程很丑陋的。

什么是函数的副作用——理解js编程中函数的副作用

函数副作用是指当调用函数时,除了返回函数值之外,还对主调用函数产生附加的影响。副作用的函数不仅仅只是返回了一个值,而且还做了其他的事情

js中sort函数用法总结_sort排序算法原理

js中sort方法用于对数组的元素进行排序,并返回数组。默认排序顺序是根据字符串Unicode码点。如果要得到自己想要的结果,不管是升序还是降序,就需要提供比较函数了。该函数比较两个值的大小,然后返回一个用于说明这两个值的相对顺序的数字

javascript封装函数

使用函数有两步:1、定义函数,又叫声明函数, 封装函数。2、调用函数var 变量 = 函数名(实参);对函数的参数和返回值的理解

js中reduce()方法

reduce() 方法接收一个函数作为累加器,reduce 为数组中的每一个元素依次执行回调函数,不包括数组中被删除或从未被赋值的元素,接受四个参数:初始值(上一次回调的返回值),当前元素值,当前索引,原数组。

javascript回调函数的理解和使用方法(callback)

在js开发中,程序代码是从上而下一条线执行的,但有时候我们需要等待一个操作结束后,再进行下一步操作,这个时候就需要用到回调函数。 在js中,函数也是对象,确切地说:函数是用Function()构造函数创建的Function对象。

js调用函数的几种方法_ES5/ES6的函数调用方式

这篇文章主要介绍ES5中函数的4种调用,在ES5中函数内容的this指向和调用方法有关。以及ES6中函数的调用,使用箭头函数,其中箭头函数的this是和定义时有关和调用无关。

js构造函数

JS中的函数即可以是构造函数又可以当作普通函数来调用,当使用new来创建对象时,对应的函数就是构造函数,通过对象来调用时就是普通函数。在我们平时工作中,经常会需要我们创建一个对象,而我们更多的是使用对像直接量,直接创建

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!