ES6解构对象赋值函数参数默认值

时间:2016-06-27 23:14:07

标签: javascript object ecmascript-6 destructuring

您好我在这里传递函数参数Object Destructuring Demo

时会经历对象解构的使用示例
function drawES6Chart({size = 'big', cords = { x: 0, y: 0 }, radius = 25} = **{}**) {
  console.log(size, cords, radius);
 // do some chart drawing
}

 // In Firefox, default values for destructuring assignments are not yet  
 implemented (as described below). 
 // The workaround is to write the parameters in the following way:
   // ({size: size = 'big', cords: cords = { x: 0, y: 0 }, radius: radius =  
      25} = **{}**)

 drawES6Chart({
    cords: { x: 18, y: 30 },
    radius: 30
});

有人能告诉我在函数参数末尾使用空对象赋值的原因是什么,我在上面用粗体标记(嵌入双星)?

5 个答案:

答案 0 :(得分:9)

如果你使用它,并调用没有参数的函数,它的工作原理:

function drawES6Chart({size = 'big', cords = { x: 0, y: 0 }, radius = 25} = {}) {
  console.log(size, cords, radius);
 // do some chart drawing
}

drawES6Chart();

如果没有,则抛出错误:

  

TypeError:无法将undefined转换为object

function drawES6Chart({size = 'big', cords = { x: 0, y: 0 }, radius = 25}) {
  console.log(size, cords, radius);
 // do some chart drawing
}

drawES6Chart();

答案 1 :(得分:5)

使用默认值进行的解构只会在传递没有相应属性的对象时执行。整个参数的= {}默认值允许不传递(空)对象。

它使drawES6Chart()等同于drawES6Chart({})

答案 2 :(得分:4)

您有一个具有默认值的对象,但该对象也是一个参数,因此它需要一个空对象作为第一个参数的默认值,这是填充的对象值。

function drawES6Chart({size = 'big', cords = { x: 0, y: 0 }, radius = 25} = {}) {
}

在伪代码中,它将是:

function drawES6Chart({**first argument**} = {**default value for first argument**}) {
}

答案 3 :(得分:2)

这是函数参数的默认值。不使用= {} JavaScript解释器在没有对象传递给函数时抛出错误,因为它无法解构undefined值。

答案 4 :(得分:2)

这里是对该现象的描述(比我原先打算的要长得多) 您是从更严格的角度观察。 为什么更严格? 想调查这个问题,因为我不确定是否有 有关函数默认参数的特殊规则,或者是否存在某些特殊规则 我不了解的关于销毁的基础知识。原来,那是 后者。

我将使用伪语法描述我的发现,该伪语法在某种程度上反映了您的意愿 参见ECMA-262。那是我唯一的参考。

要点:

有解构分配和解构绑定 模式。两者的目的都是引入名称并分配 值。

解构分配:

ObjectAssignmentPattern:'{'AssignmentPropertyList'}'= AssignmentExpression AssignmentPropertyList:AssignmentProperty [','AssignmentProperty]

这两个只是说明了“解构分配”的一般形式。

AssignmentProperty:IdentifierReference [Initializer]

这是LHS中名称的“默认值”。

AssignmentProperty:PropertyName':'AssignmentElement AssignmentElement:LeftHandSideExpression [Initializer]

这使得解构可以递归嵌套,但是语义必须是 定义。

语义学

如果你看 DestructuringAssignmentEvaluation, 您可以看到谁被分配了什么。 ObjectAssignmentPattern 不太好 有趣的是,它给出了LHS的基本'{' assignments '}'结构, 更有趣的是12.15.5.3, PropertyDestructuringAssignmentEvaluation 。这显示了当您 实际分配默认值,以及绑定更深层嵌套的名称时。

AssignmentProperty:IdentifierReference [Initializer]

步骤3在此算法中很重要,其中调用GetV。在这次通话中 正在尝试获取当前分配给的名称的值 (LHS)来自 value (RHS)。这会抛出错误,这就是以下代码段的原因 抛出:

 y = Object.defineProperty({},'foo',{get: () => {throw new Error("get foo");}})
 {foo} = y;

下一步(第4步)仅评估初始化程序是否存在,并且 从RHS获得的未定义。例如:

 y = Object.defineProperty({},'foo',{get: () => undefined})
 {foo = 3} = y; // foo === 3

请注意,此步骤以及将值实际“放入”所需位置的步骤 去,都可以扔。下一个项目比较棘手,混乱之处在于 肯定会出现:

AssignmentProperty:PropertyName':'AssignmentElement

这里的语义是将罐子打到 KeyedDestructuringAssignmentEvaluation, 传递 PropertyName 和当前值(RHS)。这是它的标题 运行时语义:

AssignmentElement:DestructuringAssignmentTarget [Initializer]

随后的算法步骤有些熟悉,有些意外 和间接。该算法中几乎任何步骤都可以抛出,因此不会 明确指出。步骤1是另一个“递归基础”,说如果 目标不是对象或数组文字(例如,只是标识符),则让 lref 是(请注意,它不必是标识符,而不必是标识符 可以分配给例如

w = {}
{x:w.u = 7} = {x:3} // w == {u:3}

然后,尝试检索“目标值” value.propertyName, 使用 GetV 。如果未定义此值,则尝试获取 初始化值,该值在第6步中放入 lref 中。步骤5是递归的 调用,剥去尽可能多的层以实现基础 变形的分配。这是我认为可以说明的更多示例 重点。

更多澄清的例子:

{x={y:1}} = {} // x == {y:1}

{x:{y=1}} = {} // error, {}.x is undefined, tried to find {}.x.y

x = 'foo'
{x:{y=1}} = {x} // x == 'foo', y == 1.
                // x doesn't get assigned in this destructuring assignment,
                // RHS becomes {x:x} === {x:'foo'} and since 'foo'.y is
                // undefined, y gets the default 1

{x:{y=1}} = {x:{y}} // error, tried to give object value {y} === {y:y} to x
                    // in RHS, but y is undefined at that point

y = 'foo'
{x:{y=1}} = {x:{y}} // y == 'foo', gave {y} === {y:y} === {y:'foo'} to x in RHS

{x:{y=1}} = {x:{y:2}} // y == 2, maybe what you wanted?

// exercises:
{x=1} = undefined         // error
{x=1} = null              // error
{x=1} = null || undefined // error
{x=1} = null | undefined  // can you guess? x == 1

功能声明

在看到以下代码后,我实际上开始研究销毁 react-redux的来源:

export function createConnect({
  connectHOC = connectAdvanced,
  mapStateToPropsFactories = defaultMapStateToPropsFactories,
  mapDispatchToPropsFactories = defaultMapDispatchToPropsFactories,
  mergePropsFactories = defaultMergePropsFactories,
  selectorFactory = defaultSelectorFactory
} = {}) {

所以,我开始挖掘的第一个地方是:

14.1函数定义

这里有一些“堆栈跟踪”试图跟踪相关产品以 让我了解具有约束力的东西。

FunctionDeclaration

FormalParameters

FormalParameterList

FormalParameter

-> 13.3.3破坏绑定模式

BindingElement

+ SingleNameBinding

++ BindingIdentifier 初始化器

+ BindingPattern

+ ObjectBindingPattern

+ BindingPropertyList

+ BindingProperty

+ SingleNameBinding

+ PropertyName ':' BindingElement

语义:解构绑定与解构分配

据我所知,解构绑定和 解构分配是可以使用它们的地方以及词汇的不同方式 处理环境。在形式参数之外解构绑定 列表(等等)需要初始化程序,并且传递了Destructuring Binding 明确地说明一个环境,而根据其定义暗示分配 初始化程序”,从“环境”中获取它们的值。我很高兴听到 为什么会出错,但这是一个快速演示:

var {x};    // syntax error
function noInit({x}) { return x; }
            // ok
noInit()    // runtime error
noInit({})  // undefined
noInit({x:4})  // 4

function binding({x:y} = {x:y}){ return y; }
function assigning(){({x:y} = {x:y}); return y}

binding()   // error, cannot access y before initialization
assigning() // error, y is not defined
y = 0
binding()   // still error
assigning() // 0 - now y is defined

结论:

我得出以下结论。破坏绑定和分配的目的 是将名称引入当前的词法环境中,可以选择分配 他们的价值观。嵌套解构是为了剖析您的数据形状 想要,而且您不会免费获得自己的名字。你可以有初始化器 作为默认值,但是一旦使用它们,您将无法再进行深入研究。如果 您雕刻出特定形状(实际上是一棵树),然后尝试将其绑定到 可能具有未定义的叶子,但分支节点必须与您所拥有的匹配 描述(名称和形状)。

附录

当我开始时,发现给定不支持解构的目标,看看tsc(打字稿编译器)会将这些内容转换为什么很有帮助和有趣。

以下代码:

function f({A,B:{BB1=7,BB2:{BBB=0}}}) {}

var z = 0;
var {x:{y=8},z} = {x:{},z};

将(tsc --target es5 --noImplicitAny false)转换为:

function f(_a) {
  var A = _a.A,
    _b = _a.B,
    _c = _b.BB1,
    BB1 = _c === void 0 ? 7 : _c,
    _d = _b.BB2.BBB,
    BBB = _d === void 0 ? 0 : _d;
}
var z = 0;
var _a = { x: {}, z: z },
  _b = _a.x.y,
  y = _b === void 0 ? 8 : _b,
  z = _a.z;