你什么时候在javascript中使用.concat()

时间:2014-09-03 02:24:00

标签: javascript string

为什么/何时使用.concat()代替赋值运算符?

即。如果我想要结合以下内容:

var p1 = "My name is ";
var p2 = "Joe";
var sen = p1+p2;
//Or you could use concat to do the same
var sen2 = p1.concat(p2);
//My question is, why would you ever use the latter?

2 个答案:

答案 0 :(得分:2)

有时最好查阅文档:Array.concatString.concat

简单地说,Array.concat()用于创建一个新数组,相当于所有传入对象(数组或其他)的平面合并。 String.concat()用于创建一个新字符串,相当于合并所有传入的字符串。

但是,正如MDN提示的那样,String.concat()不应该用作赋值+, +=运算符要快得多。为什么你会使用String.concat()?你不会。那为什么呢?这是规范的一部分:See Page 111 - 112 (Section: 15.5.4.6)

关于为什么String.Concat这么慢?的问题。我做了一些挖掘Chrome的V8引擎。首先,在幕后,这是对String.prototype.concat的调用:

// ECMA-262, section 15.5.4.6
// https://github.com/v8/v8/blob/master/src/string.js#L64
function StringConcat(other /* and more */) {  // length == 1
  CHECK_OBJECT_COERCIBLE(this, "String.prototype.concat");

  var len = %_ArgumentsLength();
  var this_as_string = TO_STRING_INLINE(this);
  if (len === 1) {
    return this_as_string + other;
  }
  var parts = new InternalArray(len + 1);
  parts[0] = this_as_string;
  for (var i = 0; i < len; i++) {
    var part = %_Arguments(i);
    parts[i + 1] = TO_STRING_INLINE(part);
  }
  return %StringBuilderConcat(parts, len + 1, "");
}

正如您所看到的,StringBuilderConcat中发生了所有实际工作,然后调用StringBuilderConcatHelper,最后调用String::WriteToFlat来构建字符串。这些都是非常长的功能,为简洁起见,我将其中的大部分功能都削减了。但如果你想在github中寻找你的自己:

<强> StringBuilderConcat

// https://github.com/v8/v8/blob/master/src/runtime.cc#L7163
RUNTIME_FUNCTION(Runtime_StringBuilderConcat) {
  // ...
  StringBuilderConcatHelper(*special,
                              answer->GetChars(),
                              FixedArray::cast(array->elements()),
                              array_length);
  // ...
}

<强> StringBuilderConcatHelper

template <typename sinkchar>
static inline void StringBuilderConcatHelper(String* special,
                                             sinkchar* sink,
                                             FixedArray* fixed_array,
                                             int array_length) {

  // ...
  String::WriteToFlat(string, sink + position, 0, element_length);
  // ...
}

<强>字符串:: WriteToFlat

// https://github.com/v8/v8/blob/master/src/objects.cc#L8373
template <typename sinkchar>
void String::WriteToFlat(String* src,
                         sinkchar* sink,
                         int f,
                         int t) {
  String* source = src;
  int from = f;
  int to = t;
  while (true) {
      // ...
      // Do a whole bunch of work to flatten the string
      // ...
    }
  }
}

现在分配路径有什么不同?让我们从JavaScript添加功能开始:

// ECMA-262, section 11.6.1, page 50.
// https://github.com/v8/v8/blob/master/src/runtime.js#L146
function ADD(x) {
  // Fast case: Check for number operands and do the addition.
  if (IS_NUMBER(this) && IS_NUMBER(x)) return %NumberAdd(this, x);
  if (IS_STRING(this) && IS_STRING(x)) return %_StringAdd(this, x);

  // Default implementation.
  var a = %ToPrimitive(this, NO_HINT);
  var b = %ToPrimitive(x, NO_HINT);

  if (IS_STRING(a)) {
    return %_StringAdd(a, %ToString(b));
  } else if (IS_STRING(b)) {
    return %_StringAdd(%NonStringToString(a), b);
  } else {
    return %NumberAdd(%ToNumber(a), %ToNumber(b));
  }
}

首先要注意的是,与上面的StringConcat相比,它没有循环,而且相当短。但是我们感兴趣的大部分工作都发生在%_StringAdd函数中:

// https://github.com/v8/v8/blob/master/src/runtime.cc#L7056
RUNTIME_FUNCTION(Runtime_StringAdd) {
  HandleScope scope(isolate);
  DCHECK(args.length() == 2);
  CONVERT_ARG_HANDLE_CHECKED(String, str1, 0);
  CONVERT_ARG_HANDLE_CHECKED(String, str2, 1);
  isolate->counters()->string_add_runtime()->Increment();
  Handle<String> result;
  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
      isolate, result, isolate->factory()->NewConsString(str1, str2));
  return *result;
}

实际上这很简单,一些计数器和一个名为NewConsString的东西用左右操作数调用。 NewConsString也很简单:

// https://github.com/v8/v8/blob/master/src/ast-value-factory.cc#L260
const AstConsString* AstValueFactory::NewConsString(
    const AstString* left, const AstString* right) {
  // This Vector will be valid as long as the Collector is alive (meaning that
  // the AstRawString will not be moved).
  AstConsString* new_string = new (zone_) AstConsString(left, right);
  strings_.Add(new_string);
  if (isolate_) {
    new_string->Internalize(isolate_);
  }
  return new_string;
}

所以这只会返回一个新的AstConsString,那是什么:

// https://github.com/v8/v8/blob/master/src/ast-value-factory.h#L117
class AstConsString : public AstString {
 public:
  AstConsString(const AstString* left, const AstString* right)
      : left_(left),
        right_(right) {}

  virtual int length() const OVERRIDE {
    return left_->length() + right_->length();
  }

  virtual void Internalize(Isolate* isolate) OVERRIDE;

 private:
  friend class AstValueFactory;

  const AstString* left_;
  const AstString* right_;
};

这根本不像是一个字符串。它实际上是一个'抽象语法树',这个结构形成一个'Rope',它可以有效地修改字符串。事实证明,在进行字符串添加时,大多数其他浏览器现在都使用此类型或绳索结构。

除此之外,附加路径使用更高效的数据结构,其中StringConcat使用不同的数据结构可以显着更多地工作。

答案 1 :(得分:-2)

根据Javascript:道格拉斯克罗克福德的好零件:

  

concat方法通过连接其他字符串来创建一个新字符串   一起。它很少使用,因为+运算符更方便

Concat不仅不太方便,而且速度也较慢:Benchmark

documentation page from MDN上:

  

强烈建议使用赋值运算符(+,+ =)   而不是concat方法。

Javascript有一些不太理想的部分。每种语言都至少有一些不好的部分。不要认为你必须使用任何语言的每一部分。