可见,parseInt(),parseFloat(),Math.floor() 的效率最低,只有其他运算 2% 左右的效率,而其中又以parseInt()最慢,仅有 1%。
为什么这些方法存在着这些差异?这些运算在引擎层又是如何被解释执行的?接下来将从 V8、JavaScriptCore、QuickJS 等主流 JS 引擎的视角,探究这些方法的具体实现。
`Handle
// Install Number.parseInt and Global.parseInt.
Handle
JSObject::AddProperty(isolate_, global_object, “parseInt”, parse_int_fun, native_context()->set_global_parse_int_fun(*parse_int_fun); `
可以见,Number.parseInt 和全局对象的 parseInt 都是基于 SimpleInstallFunction 注册的,它会将 API 安装到 isolate 中,并将该方法与 Builtin 做绑定。JS 侧调用 pasreInt 即为引擎侧调用 Builtin::kNumberParseInt。
Builtin (Built-in Functions) 是 V8 中在 VM 运行时可执行的代码块,用于表达运行时对 VM 的更改。目前 V8 版本中 Builtin 有下述 5 种实现方式:
// Convenience macro to avoid generating named accessors for all builtins. #define BUILTIN_CODE(isolate, name) \ (isolate)->builtins()->code_handle(i::Builtin::k##name)
因此这个函数注册的原名是 NumberParseInt,实现在 [→ src/builtins/number.tq] 中,是个基于 Torque 的 Builtin 实现。
`// ES6 #sec-number.parseint transitioning javascript builtin NumberParseInt( js-implicit context: NativeContext)(value: JSAny, radix: JSAny): Number { return ParseInt(value, radix); }
transitioning builtin ParseInt(implicit context: Context)( input: JSAny, radix: JSAny): Number { try { // Check if radix should be 10 (i.e. undefined, 0 or 10). if (radix != Undefined && !TaggedEqual(radix, SmiConstant(10)) && !TaggedEqual(radix, SmiConstant(0))) { goto CallRuntime; }
} label Int32(i: int32) { return ChangeInt32ToTagged(i); } label String(s: String) { // Check if the string is a cached array index. const hash: NameHash = s.raw_hash_field; if (IsIntegerIndex(hash) && hash.array_index_length < kMaxCachedArrayIndexLength) { const arrayIndex: uint32 = hash.array_index_value; return SmiFromUint32(arrayIndex); } // Fall back to the runtime. goto CallRuntime; } label CallRuntime { tail runtime::StringParseInt(input, radix); } } `
`// ES6 18.2.5 parseInt(string, radix) slow path RUNTIME_FUNCTION(Runtime_StringParseInt) { HandleScope handle_scope(isolate); DCHECK_EQ(2, args.length()); Handle
可见,parseInt(),parseFloat(),Math.floor() 的效率最低,只有其他运算 2% 左右的效率,而其中又以parseInt()最慢,仅有 1%。
为什么这些方法存在着这些差异?这些运算在引擎层又是如何被解释执行的?接下来将从 V8、JavaScriptCore、QuickJS 等主流 JS 引擎的视角,探究这些方法的具体实现。
`Handle
// Install Number.parseInt and Global.parseInt.
Handle
JSObject::AddProperty(isolate_, global_object, “parseInt”, parse_int_fun, native_context()->set_global_parse_int_fun(*parse_int_fun); `
可以见,Number.parseInt 和全局对象的 parseInt 都是基于 SimpleInstallFunction 注册的,它会将 API 安装到 isolate 中,并将该方法与 Builtin 做绑定。JS 侧调用 pasreInt 即为引擎侧调用 Builtin::kNumberParseInt。
Builtin (Built-in Functions) 是 V8 中在 VM 运行时可执行的代码块,用于表达运行时对 VM 的更改。目前 V8 版本中 Builtin 有下述 5 种实现方式:
// Convenience macro to avoid generating named accessors for all builtins. #define BUILTIN_CODE(isolate, name) \ (isolate)->builtins()->code_handle(i::Builtin::k##name)
因此这个函数注册的原名是 NumberParseInt,实现在 [→ src/builtins/number.tq] 中,是个基于 Torque 的 Builtin 实现。
`// ES6 #sec-number.parseint transitioning javascript builtin NumberParseInt( js-implicit context: NativeContext)(value: JSAny, radix: JSAny): Number { return ParseInt(value, radix); }
transitioning builtin ParseInt(implicit context: Context)( input: JSAny, radix: JSAny): Number { try { // Check if radix should be 10 (i.e. undefined, 0 or 10). if (radix != Undefined && !TaggedEqual(radix, SmiConstant(10)) && !TaggedEqual(radix, SmiConstant(0))) { goto CallRuntime; }
} label Int32(i: int32) { return ChangeInt32ToTagged(i); } label String(s: String) { // Check if the string is a cached array index. const hash: NameHash = s.raw_hash_field; if (IsIntegerIndex(hash) && hash.array_index_length < kMaxCachedArrayIndexLength) { const arrayIndex: uint32 = hash.array_index_value; return SmiFromUint32(arrayIndex); } // Fall back to the runtime. goto CallRuntime; } label CallRuntime { tail runtime::StringParseInt(input, radix); } } `
`// ES6 18.2.5 parseInt(string, radix) slow path RUNTIME_FUNCTION(Runtime_StringParseInt) { HandleScope handle_scope(isolate); DCHECK_EQ(2, args.length()); Handle