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

ES6 destructuring object assignment function parameter default value

您好,我在这里查看了在传递函数参数时使用对象解构的示例 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
});

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

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

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

在伪代码中,将是:

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

如果你使用它,并且调用不带参数的函数,它会起作用:

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

drawES6Chart();

如果不是,则抛出错误:

TypeError: can't convert undefined to object

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

drawES6Chart();

只有当你传递一个没有相应属性的对象时,默认的解构才会起作用。 = {} 整个参数的默认值允许根本不传递(空)对象。

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

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

这是对该现象的描述(比我原先预期的要长得多) 您正在从更严格的角度观察。 为什么更严谨?我 想调查这个问题,因为我不确定是否有一些 关于函数默认参数的特殊规则,或者如果有什么 我不明白的关于解构的基础知识。原来,这是 后者。

我将使用 pseudo-grammar 来描述我的发现,这在某种程度上反映了您的想法 请参见 ECMA-262。这是我唯一的参考。

要点:

有解构赋值和解构绑定 图案。两者的目的都是引入名字和赋值 值。

解构赋值:

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

这两个只是说明了解构赋值的一般形式。

AssignmentProperty : IdentifierReference [Initializer]

这是 "default value" LHS 中的名称。

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

这让解构递归嵌套,但语义需要 定义。

语义

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

AssignmentProperty : IdentifierReference [Initializer]

这个算法中的第3步很重要,这里调用了GetV。在这个调用中,它 正在尝试获取当前分配给的名称的值 (左轴)来自 (右轴)。这可以抛出,这就是为什么下面的代码片段 抛出:

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

下一步,即第 4 步,仅评估初始化器是否存在以及 value 从 RHS 获得未定义。例如:

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

注意这一步,而这一步其实"putting"需要值的地方 去,都可以扔。下一项更棘手,是混淆的地方 最肯定会出现:

AssignmentProperty : PropertyName ':' AssignmentElement

这里的语义是把罐头踢到 KeyedDestructuringAssignmentEvaluation, 传递 PropertyName 和当前值 (RHS)。这是它的 header 运行时语义:

AssignmentElement : DestructuringAssignmentTarget [Initializer]

后续算法的步骤有点熟悉,有一些惊喜 和间接。这个算法几乎任何一步都可以抛出,所以不会 明确指出。第 1 步是另一个 "base of recursion," 说如果 目标不是 object 或数组文字(例如,只是一个标识符),然后让 lref 是那个(注意它不一定是标识符,只是一些东西 可以分配给,例如

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

然后,尝试检索 "target value" 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 函数定义

这里有点"stack trace"试图追踪相关的作品 带我去绑定东西。

FunctionDeclaration

FormalParameters

FormalParameterList

FormalParameter

-> 13.3.3 解构绑定模式

BindingElement

+SingleNameBinding

++BindingIdentifier, Initializer

+BindingPattern

+ObjectBindingPattern

+BindingPropertyList

+BindingProperty

+SingleNameBinding,

+PropertyName ':' BindingElement

语义:解构绑定与解构赋值

据我所知,解构绑定和解构绑定之间的唯一区别 Destructuring Assignment 是它们可以使用的地方以及词法有何不同 环境得到处理。在形式参数之外解构绑定 列表(和类似的)需要初始化器,并且通过 Destructuring Binding 一个明确的环境,而任务,根据他们的定义意味着 一个初始值设定项”,从 "ambience." 中获取它们的值 我会很高兴听到 为什么这是错误的,但这里有一个快速演示:

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;