为什么/何时使用.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?
答案 0 :(得分:2)
有时最好查阅文档:Array.concat和String.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有一些不太理想的部分。每种语言都至少有一些不好的部分。不要认为你必须使用任何语言的每一部分。