先收集一些资料,占个坑。下周末有空写
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 }; let n = { x, y, ...z, x:42 }; let [a,b, ...c] = [x, y, z, n]; let d = [a, b, ...[5, 6]]; function f (a,b, ...c) { console.log(a,b,c) } function f1 ({a,b, ...c}, d) { //destructing the first argument console.log(a,b,c) }
"use strict"; var _extends = Object.assign || function(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; } var _x$y$a$b = { x: 1, y: 2, a: 3, b: 4 }, x = _x$y$a$b.x, y = _x$y$a$b.y, z = _objectWithoutProperties(_x$y$a$b, ["x", "y"]); var n = _extends({ x: x, y: y }, z, _defineProperty({}, "x", 42)); var a = x, b = y, c = [z, n]; var d = [a, b].concat([5, 6]); function f(a, b) { for (var _len = arguments.length, c = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) { c[_key - 2] = arguments[_key]; } console.log(a, b, c); } function f1(_ref, d) { var a = _ref.a, b = _ref.b, c = _objectWithoutProperties(_ref, ["a", "b"]); //destructing the first argument console.log(a, b, c); }
Array Destructing
Array Literal
Object Destructing
Object Literal
The Spec
https://tc39.github.io/proposal-object-rest-spread/
V8 Implementation
Github/node/deps/v8/src/interpreter/bytecode-generator.cc
void BytecodeGenerator::BuildArrayLiteralSpread(Spread* spread, Register array, Register index, FeedbackSlot index_slot, FeedbackSlot element_slot) { RegisterAllocationScope register_scope(this); Register value = register_allocator()->NewRegister(); builder()->SetExpressionAsStatementPosition(spread->expression()); IteratorRecord iterator = BuildGetIteratorRecord(spread->expression(), IteratorType::kNormal); LoopBuilder loop_builder(builder(), nullptr, nullptr); loop_builder.LoopHeader(); // Call the iterator's .next() method. Break from the loop if the `done` // property is truthy, otherwise load the value from the iterator result and // append the argument. BuildIteratorNext(iterator, value); builder()->LoadNamedProperty( value, ast_string_constants()->done_string(), feedback_index(feedback_spec()->AddLoadICSlot())); loop_builder.BreakIfTrue(ToBooleanMode::kConvertToBoolean); loop_builder.LoopBody(); builder() // value = value.value ->LoadNamedProperty(value, ast_string_constants()->value_string(), feedback_index(feedback_spec()->AddLoadICSlot())) .StoreAccumulatorInRegister(value) // array[index] = value .StoreInArrayLiteral(array, index, feedback_index(element_slot)) // index++ .LoadAccumulatorWithRegister(index) .UnaryOperation(Token::INC, feedback_index(index_slot)) .StoreAccumulatorInRegister(index); loop_builder.BindContinueTarget(); loop_builder.JumpToHeader(loop_depth_); }
void BytecodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { expr->InitDepthAndFlags(); // Fast path for the empty object literal which doesn't need an // AllocationSite. if (expr->IsEmptyObjectLiteral()) { DCHECK(expr->IsFastCloningSupported()); builder()->CreateEmptyObjectLiteral(); return; } // Deep-copy the literal boilerplate. uint8_t flags = CreateObjectLiteralFlags::Encode( expr->ComputeFlags(), expr->IsFastCloningSupported()); Register literal = register_allocator()->NewRegister(); // Create literal object. int property_index = 0; bool clone_object_spread = expr->properties()->first()->kind() == ObjectLiteral::Property::SPREAD; if (clone_object_spread) { // Avoid the slow path for spreads in the following common cases: // 1) `let obj = { ...source }` // 2) `let obj = { ...source, override: 1 }` // 3) `let obj = { ...source, ...overrides }` RegisterAllocationScope register_scope(this); Expression* property = expr->properties()->first()->value(); Register from_value = VisitForRegisterValue(property); BytecodeLabels clone_object(zone()); builder()->JumpIfUndefined(clone_object.New()); builder()->JumpIfNull(clone_object.New()); builder()->ToObject(from_value); clone_object.Bind(builder()); int clone_index = feedback_index(feedback_spec()->AddCloneObjectSlot()); builder()->CloneObject(from_value, flags, clone_index); builder()->StoreAccumulatorInRegister(literal); property_index++; } else { size_t entry; // If constant properties is an empty fixed array, use a cached empty fixed // array to ensure it's only added to the constant pool once. if (expr->properties_count() == 0) { entry = builder()->EmptyObjectBoilerplateDescriptionConstantPoolEntry(); } else { entry = builder()->AllocateDeferredConstantPoolEntry(); object_literals_.push_back(std::make_pair(expr, entry)); } BuildCreateObjectLiteral(literal, flags, entry); } // Store computed values into the literal. AccessorTable accessor_table(zone()); for (; property_index < expr->properties()->length(); property_index++) { ObjectLiteral::Property* property = expr->properties()->at(property_index); if (property->is_computed_name()) break; if (!clone_object_spread && property->IsCompileTimeValue()) continue; RegisterAllocationScope inner_register_scope(this); Literal* key = property->key()->AsLiteral(); switch (property->kind()) { case ObjectLiteral::Property::SPREAD: UNREACHABLE(); case ObjectLiteral::Property::CONSTANT: case ObjectLiteral::Property::MATERIALIZED_LITERAL: DCHECK(clone_object_spread || !property->value()->IsCompileTimeValue()); V8_FALLTHROUGH; case ObjectLiteral::Property::COMPUTED: { // It is safe to use [[Put]] here because the boilerplate already // contains computed properties with an uninitialized value. if (key->IsStringLiteral()) { DCHECK(key->IsPropertyName()); if (property->emit_store()) { builder()->SetExpressionPosition(property->value()); VisitForAccumulatorValue(property->value()); FeedbackSlot slot = feedback_spec()->AddStoreOwnICSlot(); if (FunctionLiteral::NeedsHomeObject(property->value())) { RegisterAllocationScope register_scope(this); Register value = register_allocator()->NewRegister(); builder()->StoreAccumulatorInRegister(value); builder()->StoreNamedOwnProperty( literal, key->AsRawPropertyName(), feedback_index(slot)); VisitSetHomeObject(value, literal, property); } else { builder()->StoreNamedOwnProperty( literal, key->AsRawPropertyName(), feedback_index(slot)); } } else { builder()->SetExpressionPosition(property->value()); VisitForEffect(property->value()); } } else { RegisterList args = register_allocator()->NewRegisterList(4); builder()->MoveRegister(literal, args[0]); builder()->SetExpressionPosition(property->key()); VisitForRegisterValue(property->key(), args[1]); builder()->SetExpressionPosition(property->value()); VisitForRegisterValue(property->value(), args[2]); if (property->emit_store()) { builder() ->LoadLiteral(Smi::FromEnum(LanguageMode::kSloppy)) .StoreAccumulatorInRegister(args[3]) .CallRuntime(Runtime::kSetProperty, args); Register value = args[2]; VisitSetHomeObject(value, literal, property); } } break; } case ObjectLiteral::Property::PROTOTYPE: { // __proto__:null is handled by CreateObjectLiteral. if (property->IsNullPrototype()) break; DCHECK(property->emit_store()); DCHECK(!property->NeedsSetFunctionName()); RegisterList args = register_allocator()->NewRegisterList(2); builder()->MoveRegister(literal, args[0]); builder()->SetExpressionPosition(property->value()); VisitForRegisterValue(property->value(), args[1]); builder()->CallRuntime(Runtime::kInternalSetPrototype, args); break; } case ObjectLiteral::Property::GETTER: if (property->emit_store()) { accessor_table.lookup(key)->second->getter = property; } break; case ObjectLiteral::Property::SETTER: if (property->emit_store()) { accessor_table.lookup(key)->second->setter = property; } break; } } // Define accessors, using only a single call to the runtime for each pair of // corresponding getters and setters. for (AccessorTable::Iterator it = accessor_table.begin(); it != accessor_table.end(); ++it) { RegisterAllocationScope inner_register_scope(this); RegisterList args = register_allocator()->NewRegisterList(5); builder()->MoveRegister(literal, args[0]); VisitForRegisterValue(it->first, args[1]); VisitObjectLiteralAccessor(literal, it->second->getter, args[2]); VisitObjectLiteralAccessor(literal, it->second->setter, args[3]); builder() ->LoadLiteral(Smi::FromInt(NONE)) .StoreAccumulatorInRegister(args[4]) .CallRuntime(Runtime::kDefineAccessorPropertyUnchecked, args); } // Object literals have two parts. The "static" part on the left contains no // computed property names, and so we can compute its map ahead of time; see // Runtime_CreateObjectLiteralBoilerplate. The second "dynamic" part starts // with the first computed property name and continues with all properties to // its right. All the code from above initializes the static component of the // object literal, and arranges for the map of the result to reflect the // static order in which the keys appear. For the dynamic properties, we // compile them into a series of "SetOwnProperty" runtime calls. This will // preserve insertion order. for (; property_index < expr->properties()->length(); property_index++) { ObjectLiteral::Property* property = expr->properties()->at(property_index); RegisterAllocationScope inner_register_scope(this); if (property->IsPrototype()) { // __proto__:null is handled by CreateObjectLiteral. if (property->IsNullPrototype()) continue; DCHECK(property->emit_store()); DCHECK(!property->NeedsSetFunctionName()); RegisterList args = register_allocator()->NewRegisterList(2); builder()->MoveRegister(literal, args[0]); builder()->SetExpressionPosition(property->value()); VisitForRegisterValue(property->value(), args[1]); builder()->CallRuntime(Runtime::kInternalSetPrototype, args); continue; } switch (property->kind()) { case ObjectLiteral::Property::CONSTANT: case ObjectLiteral::Property::COMPUTED: case ObjectLiteral::Property::MATERIALIZED_LITERAL: { Register key = register_allocator()->NewRegister(); BuildLoadPropertyKey(property, key); builder()->SetExpressionPosition(property->value()); Register value = VisitForRegisterValue(property->value()); VisitSetHomeObject(value, literal, property); DataPropertyInLiteralFlags data_property_flags = DataPropertyInLiteralFlag::kNoFlags; if (property->NeedsSetFunctionName()) { data_property_flags |= DataPropertyInLiteralFlag::kSetFunctionName; } FeedbackSlot slot = feedback_spec()->AddStoreDataPropertyInLiteralICSlot(); builder() ->LoadAccumulatorWithRegister(value) .StoreDataPropertyInLiteral(literal, key, data_property_flags, feedback_index(slot)); break; } case ObjectLiteral::Property::GETTER: case ObjectLiteral::Property::SETTER: { RegisterList args = register_allocator()->NewRegisterList(4); builder()->MoveRegister(literal, args[0]); BuildLoadPropertyKey(property, args[1]); builder()->SetExpressionPosition(property->value()); VisitForRegisterValue(property->value(), args[2]); VisitSetHomeObject(args[2], literal, property); builder() ->LoadLiteral(Smi::FromInt(NONE)) .StoreAccumulatorInRegister(args[3]); Runtime::FunctionId function_id = property->kind() == ObjectLiteral::Property::GETTER ? Runtime::kDefineGetterPropertyUnchecked : Runtime::kDefineSetterPropertyUnchecked; builder()->CallRuntime(function_id, args); break; } case ObjectLiteral::Property::SPREAD: { RegisterList args = register_allocator()->NewRegisterList(2); builder()->MoveRegister(literal, args[0]); builder()->SetExpressionPosition(property->value()); VisitForRegisterValue(property->value(), args[1]); builder()->CallRuntime(Runtime::kCopyDataProperties, args); break; } case ObjectLiteral::Property::PROTOTYPE: UNREACHABLE(); // Handled specially above. break; } } builder()->LoadAccumulatorWithRegister(literal); }
void BytecodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { // 针对以下三种常用的spread模式进行优化 // 1) `let obj = { ...source }` // 2) `let obj = { ...source, override: 1 }` // 3) `let obj = { ...source, ...overrides }` // 静态部分:属性名是字符串 // 动态部分:属性名需要计算 // ObjectLiteral::Property::SPREAD 调用运行时 CopyDataProperties }
References: