【问题标题】:Who can help to explain this JavaScript algorithm [].filter.call()谁能帮忙解释一下这个 JavaScript 算法 [].filter.call()
【发布时间】:2017-03-24 17:13:44
【问题描述】:

我的任务是从字符串中按顺序接收唯一元素作为参数。我不明白这个函数 uniqueElements 如何返回 ['A','B','C','B']

var word = "AAAABBBBCCBB";
var uniqueElements = function(word) {
    return [].filter.call(word, (function(elem,index) {
        return word[index-1] !== elem
    }));
}    
uniqueElements(word);      
//it should return ['A','B','C','B']

我检查并阅读了以下来源:

MDN MozillaJS Blog 但没有成功。

【问题讨论】:

  • 那是把字符串转换成字符数组。
  • 语法“借用”了一个数组方法,并将其应用于字符串。另见call
  • 出现编译错误
  • 在 ES6 世界中,这会写成[...word].filterArray.from(word).filter 更简洁易读。

标签: javascript arrays filter call


【解决方案1】:

它在数组对象的过滤器方法上执行显式call(),以便它们可以传入字符串。他们只是使用[] 而不是Array,因为它的语法更短,或者有人在编写它时试图变得聪明。

通常,filter() 方法会影响数组自身的内容。但是,通过call()-ing 它,您可以通过传入另一个数组来更改上下文,或者在本例中是一个字符串(被视为字符数组)。然后它为每个元素运行提供的函数,如果函数的返回值为true,则保留该元素。

【讨论】:

  • 这忽略了他们这样做是为了将filter 应用于字符串。
  • 你可以传递一个字符串,因为这个引用和过滤函数将按预期执行 split('') ,这有点不同寻常。这是可靠的行为吗?
  • @ganqqwerty 它没有调用 split,而是遍历字符串中的每个字母。字符串是可迭代的,因为它们实际上只是字符数组。
【解决方案2】:

var word = "AAAABBBBCCBB";
var unique = word.split('').filter(function(item, i, ar) {
  return ar.indexOf(item) === i;
}).join('');

【讨论】:

    【解决方案3】:
    var uniqueElements = function(word) {
        return [].filter.call(word, (function(elem,index) {
            return word[index-1] !== elem
        }));
    }    
    

    等价于

    var uniqueElements = function(word) {
        return word.split("").filter(function(elem,index) {
            return word[index-1] !== elem
        });
    }    
    

    这应该已经更清楚了:它将 word 转换为 char 数组,然后过滤与前一个不同的每个 char。这就解释了为什么“B”在结果中出现了两次。

    要获得独特的元素,您可以这样做

    var uniqueElements = function(word) {
        var res = []
        word.split("").forEach(function(val){
            if (res.indexOf(val)===-1) res.push(val);
        });
        return res
    }  
    

    【讨论】:

      【解决方案4】:

      如果您不了解代码的作用,您可以记录操作!浏览器

      var word = "AAAABBBBCCBB";
      function uniqueElements(word){
          return [].filter.call(word,(
              function(elem,index) { 
              console.log("Index : "+index+" | Elem : " +elem+" | CompareTo "+word[index-1]);
                  return word[index-1] !== elem;
              }
          ));
      }
      uniqueElements(word); 
      

      你会得到以下输入:

      Index : 0 | Elem : A | CompareTo undefined
      Index : 1 | Elem : A | CompareTo A
      Index : 2 | Elem : A | CompareTo A
      Index : 3 | Elem : A | CompareTo A
      Index : 4 | Elem : B | CompareTo A
      Index : 5 | Elem : B | CompareTo B
      Index : 6 | Elem : B | CompareTo B
      Index : 7 | Elem : B | CompareTo B
      Index : 8 | Elem : C | CompareTo B
      Index : 9 | Elem : C | CompareTo C
      Index : 10 | Elem : B | CompareTo C
      Index : 11 | Elem : B | CompareTo B
      

      使用它,您可以检查每个不等于之前的元素是否都发送到您的数组。

      很少有答案/cmets 已经指出 [].filter.call() 是如何工作的。

      如果要获取字符串而不是数组,只需添加.join("") IE

      var word = "AAAABBBBCCBB";
      function uniqueElements(word){
          return [].filter.call(word,(
              function(elem,index) { 
              console.log("Index : "+index+" | Elem : " +elem+" | CompareTo "+word[index-1]);
                  return word[index-1] !== elem;
              }
          )).join("");
      }
      uniqueElements(word); 
      

      【讨论】:

        【解决方案5】:

        为了更好地理解,我已使用内联 cmets 将代码分成更小的部分。

        var word = "AAAABBBBCCBB";
        var uniqueElements = function(word){
            // this is the filter function
            // this function will get each element of the string with its index
            var filterFunction = function(elem, index) {
                // the elements who pass the following condition are kept
                // the condition - 
                // if the character at the previous index (index-1) is not equal to the 
                // current element, then keep the element
                return word[index-1] !== elem;
            }
        
            // the below is equivalent to Array.prototype.filter.call(context, filterFunction)
            return [].filter.call(word, filterFunction);
        }
        console.log(uniqueElements(word));
        

        【讨论】:

          【解决方案6】:

          [].filter.call 的目的是在字符串上应用filter 方法。天真地你会首先尝试这样做:

          return word.filter(function(elem,index) {
              return word[index-1] !== elem;
          });
          

          ... 如果word 是一个数组,它将返回其中满足条件word[index-1] !== elemword[index-1] !== word[index] 的缩写)的字符,即与字符不同的每个字符在它之前。

          但是,filter 方法只存在于继承自 Array 原型的对象上,字符串不存在这种情况。

          但是,filter 本身可以处理类似数组的对象,即具有 length 并可能具有数字属性的对象。要在没有真正的Array 对象的情况下调用filter,您可以在该函数上使用call,然后提供上下文(即类似数组的对象)作为第一个参数。其他参数保持不变。所以你首先需要知道在哪里可以找到filter 方法...它在Array.prototype 上,所以你可以这样引用它:

          Array.prototype.filter
          

          但由于所有数组都可以访问此方法,因此只取一个空数组并引用其filter 方法会更短:

          [].filter
          

          两者中的任何一个都可以。现在您必须对其执行call 方法,并提供您要迭代的类数组对象:

          [].filter.call(words, ...)
          

          其余的就像你对一个真正的数组所做的那样:你提供回调函数:

          return [].filter.call(word, function(elem,index) {
              return word[index-1] !== elem
          });
          

          返回值不再是字符串。它——就像filter 总是返回——一个数组:一个真实的——它是由filter 方法创建的。

          ES6方式:

          在 ES6 中,您可以以更易读的方式编写它,因为现在有 Array.from 方法,它可以将类似数组的对象转换为真正的数组。然后你可以继续使用filter 方法:

          return Array.from(word).filter(function(elem,index) {
              return word[index-1] !== elem
          });
          

          扩展运算符是 ES6 提供的另一种选择:

          return [...word].filter(function(elem,index) {
              return word[index-1] !== elem
          });
          

          【讨论】:

            【解决方案7】:

            我会通过使用Array.prototype.reduce() 来完成这个 O(n) 时间,如下所示;

            var str = "AAAABBBBCCBB",
            uniques = Array.prototype.reduce.call(str, (p,c,i) => i-1 ? p[p.length-1] !== c ? (p.push(c),p)
                                                                                            : p
                                                                      : [c]);
            console.log(uniques);

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2018-01-12
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2021-12-22
              相关资源
              最近更新 更多