为什么String表现得像Array? -JS

 收藏

We all know Strings somewhat behaves like an Array.You can even apply some of the array methods to it and take benefits, like the below example.

[].filter.call('abcdef',function(val){
    return val<'e';
});

也,

var a='xyz';

I can access first element using a[0] or I can call a.length like an Array

My question is why String behaves like an Array. and if it does, why do I get false below when I check if it is an instance of Array. Is String Array-like?

'a' instanceof Array
回复
  • 爱旳偏执 回复

    To use instanceof you need to create an instance of an Object and a is not an instance of one. It is a primitive or also known as string literal:

    String literals (denoted by double or single quotes) and strings returned from String calls in a non-constructor context (i.e., without using the new keyword) are primitive strings. JavaScript automatically converts primitives to String objects, so that it's possible to use String object methods for primitive strings. In contexts where a method is to be invoked on a primitive string or a property lookup occurs, JavaScript will automatically wrap the string primitive and call the method or perform the property lookup.

    例如:

    let foo = 'abc'         // primitive
    let boo = new String()  // object
    
    console.log(foo instanceof String)  // false
    console.log(boo instanceof String)  // true
    
    console.log(typeof foo)  // 'string' <-- notice not String
    console.log(typeof boo)  // object

    这仅仅是由于:

    instanceof运算符用于测试是否存在Constructor.prototype   对象的原型链

    But as we explained above we are dealing with string literals which are created without a constructor call (new keyword) and only boxed for our convenience when operating on them. They are not actual instances of String and thus instanceof returns false.

    The reason you can use Array.filter on the string primitive is simply due to the fact that it was boxed for you to a String from where it got the length property at the time of execution.

    For example in the case of V8 engine string primitive is parsed/boxed to String and a String to a StringObject. Notice they are actually different instances.

    Array.filter only cares about that length property and numeric indicies as pointed nicely by CertainPerformance which are provided by the boxing to String. Example:

    console.log(Object.getOwnPropertyNames('a'))  // ["0", "length"]

    However String is not StringObject and thus instanceof would return false.

  • x小学生 回复

    All that Array.prototype.filter really requires is that the variable being iterated over has a length property, and that the variable has numeric-indexed values. See (part of) the polyfill:

    var len = this.length >>> 0,
        res = new Array(len), // preallocate array
        t = this, c = 0, i = -1;
    if (thisArg === undefined){
      while (++i !== len){
        // checks to see if the key was set
        if (i in this){
          if (func(t[i], i, t)){
            res[c++] = t[i];
          }
        }
      }
    }
    

    Strings fulfill this condition - strings have a length property, and numeric indicies accessed on the string resolve to individual characters.

    但是您可以对任意对象执行相同的操作:

    const obj = {
      0: 'foo',
      1: 'bar',
      length: 2
    };
    
    const result = [].filter.call(obj, val => val.startsWith('f'));
    console.log(result);

    You could say that obj is array-like as well, since it has a length property and numeric indicies. Most array methods like .filter, .reduce, etc can be .called on array-like objects, even if those objects aren't actual arrays.

    (从技术上讲,您也可以在非类似数组的对象上调用数组方法,这只会做任何有用的事情-不会执行任何迭代)