ES6 / 2015中的零安全财产访问(和条件分配)

时间:2015-08-21 11:23:47

标签: javascript coffeescript ecmascript-6 babeljs

ES6中是否存在null - 安全属性访问(空传播/存在)运算符(ES2015 / JavaScript.next / Harmony),如中的 ?. CoffeeScript 例如?或者它是否计划用于ES7?

var aThing = getSomething()
...
aThing = possiblyNull?.thing

这大致类似于:

if (possiblyNull != null) aThing = possiblyNull.thing

理想情况下,如果undefinedaThing,则解决方案不应(possiblyNull}分配给null

11 个答案:

答案 0 :(得分:77)

目前处于第1阶段的功能:可选链接。

https://github.com/tc39/proposal-optional-chaining

如果你想今天使用它,有一个Babel插件可以实现它。

https://github.com/davidyaha/ecmascript-optionals-proposal

更新(2017-08-01):如果您想使用官方插件,可以使用新变换尝试使用Babel 7的alpha版本。 您的里程可能会有所不同

https://www.npmjs.com/package/babel-plugin-transform-optional-chaining

答案 1 :(得分:48)

它不如?运算符,但要实现类似的结果,您可以这样做:

user && user.address && user.address.postcode

由于nullundefined都是 falsy 值(see this reference),因此&&运算符后的属性只有先例才会被访问它不是null或未定义。

或者,您可以编写如下函数:

function _try(func, fallbackValue) {
    try {
        var value = func();
        return (value === null || value === undefined) ? fallbackValue : value;
    } catch (e) {
        return fallbackValue;
    }
}

用法:

_try(() => user.address.postcode) // return postcode or undefined 

或者,使用后备值:

_try(() => user.address.postcode, "none") // return postcode or a custom string

答案 2 :(得分:31)

没有。您可以在JavaScript中使用lodash#get或类似内容。

答案 3 :(得分:12)

Vanilla替代安全财产访问

try { b = a.b.c.d.e } catch(e) {}

最简洁的条件赋值可能就是这个

{
    "AWSTemplateFormatVersion": "2010-09-09",
    "Parameters": {
        "AGS": {
            "Type": "String",
            "AllowedPattern": "[a-zA-Z0-9_]+"
        },
        "Secret": {
            "Type": "String",
            "AllowedPattern": "[a-zA-Z0-9_]+",
            "NoEcho": true
        },
        "SDLC": {
            "Type": "String",
            "AllowedValues": [
                "D",
                "I",
                "J",
                "Q",
                "S",
                "U",
                "P",
                "C"
            ]
        },
        "Component": {
            "Type": "String",
            "AllowedPattern": "[a-zA-Z0-9_]+"
        },
        "Topic": {
            "Type": "String",
            "AllowedPattern": "[a-zA-Z0-9_]+"
        },
        "TopicLambda": {
            "Type": "String",
            "AllowedPattern": "[a-zA-Z0-9_-]+"
        }
    },
    "Resources": {
        "BRIDGE2ESBTOPIC": {
            "Type": "Custom::EnterpriseTopic",
            "Version": 1,
            "Properties": {
                "ServiceToken": {
                    "Fn::Join": [
                        ":",
                        [
                            "arn",
                            "aws",
                            "lambda",
                            {
                                "Ref": "AWS::Region"
                            },
                            {
                                "Ref": "AWS::AccountId"
                            },
                            "function",
                            {
                                "Ref": "TopicLambda"
                            }
                        ]
                    ]
                },
                "AGS": {
                    "Ref": "AGS"
                },
                "Secret": {
                    "Ref": "Secret"
                },
                "SDLC": {
                    "Ref": "SDLC"
                },
                "Component": {
                    "Ref": "Component"
                },
                "ResourceName": {
                    "Ref": "Topic"
                }
            }
        }
    },

    //This will only output to CloudFormation console, not to command line.
    "Outputs": {
        "Topic": {
            "Description" : "Topic created by this template",
            "Value": {"Ref": "BRIDGE2ESBTOPIC"}
        }
    }
}

答案 4 :(得分:6)

不,ES6中没有空传播运算符。您必须使用其中一个known patterns

您可以使用解构,但是:

({thing: aThing} = possiblyNull);

在ES7中添加这样的运算符有很多讨论(例如this),但没有一个真正起步。

答案 5 :(得分:6)

2020解决方案?.??

现在,您可以直接直接使用?.(可选链接)来安全地测试是否存在。所有现代的浏览器都支持它。

??(空位合并)可用于设置默认值(如果未定义或为空)。

aThing = possiblyNull ?? aThing
aThing = a?.b?.c ?? possiblyNullFallback ?? aThing

如果存在属性,?.进行下一个检查,或返回有效值。任何故障都会立即短路并返回undefined

const example = {a: ["first", {b:3}, false]}

example?.a  // ["first", {b:3}, false]
example?.b  // undefined

example?.a?.[0]     // "first"
example?.a?.[1]?.a  // undefined
example?.a?.[1]?.b  // 3

domElement?.parentElement?.children?.[3]?.nextElementSibling

null?.()                // undefined
validFunction?.()       // result
(() => {return 1})?.()  // 1

要确保使用默认的定义值,可以使用??。如果您需要第一个真实值,则可以使用||

example?.c ?? "c"  // "c"
example?.c || "c"  // "c"

example?.a?.[2] ?? 2  // false
example?.a?.[2] || 2  // 2

如果不检查大小写,则必须存在left-side属性。如果没有,它将引发异常。

example?.First         // undefined
example?.First.Second  // Uncaught TypeError: Cannot read property 'Second' of undefined

?. Browser Support-84%,2020年12月

?? Browser Support-84%

Mozilla Documentation

-

逻辑无效分配,2020年以上的解决方案

当前正在将新的运算符添加到浏览器??=||=&&=中。它们并不能完全满足您的要求,但根据您的代码目标,可能会导致相同的结果。

注意:这些在公共浏览器版本中并不常见,但是Babel应该可以很好地移植。将随着可用性的变化而更新。

??=检查左侧是否未定义或为null,如果已定义则短路。如果不是,则为左侧分配右侧值。 ||=&&=相似,但是基于||&&运算符。

基本示例

let a          // undefined
let b = null
let c = false

a ??= true  // true
b ??= true  // true
c ??= true  // false

对象/数组示例

let x = ["foo"]
let y = { foo: "fizz" }

x[0] ??= "bar"  // "foo"
x[1] ??= "bar"  // "bar"

y.foo ??= "buzz"  // "fizz"
y.bar ??= "buzz"  // "buzz"

x  // Array [ "foo", "bar" ]
y  // Object { foo: "fizz", bar: "buzz" }

Browser Support 2020年12月-79%

Mozilla Documentation

答案 6 :(得分:3)

从列表here开始,目前没有提议向Ecmascript添加安全遍历。因此,不仅没有很好的方法可以做到这一点,而且在可预见的未来不会被添加。

答案 7 :(得分:0)

一个安全的深度get方法似乎非常适合underscore.js,但问题是避免字符串编程。修改@ Felipe的答案以避免字符串编程(或者至少将边缘情况推回调用者):

function safeGet(obj, props) {
   return (props.length==1) ? obj[keys[0]] :safeGet(obj[props[0]], props.slice(1))
}

示例:

var test = { 
  a: { 
    b: 'b property value',
    c: { }
  } 
}
safeGet(test, ['a', 'b']) 
safeGet(test, "a.b".split('.'))  

答案 8 :(得分:0)

// Typescript
static nullsafe<T, R>(instance: T, func: (T) => R): R {
    return func(instance)
}

// Javascript
function nullsafe(instance, func) {
    return func(instance);
};

// use like this
const instance = getSomething();
let thing = nullsafe(instance, t => t.thing0.thing1.thingx);

答案 9 :(得分:-1)

我知道这是一个JavaScript问题,但我认为Ruby会以所有要求的方式处理这个问题,所以我认为这是一个相关的参考点。

.&try和&amp;&amp;有自己的优势和潜在的陷阱。这里有很好的选择:http://mitrev.net/ruby/2015/11/13/the-operator-in-ruby/

TLDR; Rubyists的结论是dig更容易在眼睛上,并且更有力地保证null将被分配。

这是TypeScript中的一个简单的imeplementation:

export function dig(target: any, ...keys: Array<string>): any {
  let digged = target
  for (const key of keys) {
    if (typeof digged === 'undefined') {
      return undefined // can also return null or a default value
    }
    if (typeof key === 'function') {
      digged = key(digged)
    } else {
      digged = digged[key]
    }
  }
  return digged
}

这可以用于任何深度的嵌套和处理函数。

a = dig(b, 'c', 'd', 'e');
foo = () => ({});
bar = dig(a, foo, 'b', 'c')

try方法在JS中同样很好用,如前面的答案所示。它也不需要循环,这是该实现的一个缺点。

答案 10 :(得分:-3)

我认为这个问题需要2018年的一些刷新。这可以很好地完成,没有任何库使用Object.defineProperty(),可以按如下方式使用:

myVariable.safeGet('propA.propB.propC');

我认为这是安全的(和js-ethical),因为writeableenumerable defineProperty方法现在可用于Object,因为documented in MDN }

下面的函数定义:

Object.defineProperty(Object.prototype, 'safeGet', { 
    enumerable: false,
    writable: false,
    value: function(p) {
        return p.split('.').reduce((acc, k) => {
            if (acc && k in acc) return acc[k];
            return undefined;
        }, this);
    }
});

我已经将jsBin与控制台输出放在一起来演示这一点。请注意,在jsBin版本中,我还为空值添加了自定义异常。这是可选的,所以我把它从上面的最小定义中删除了。

欢迎改进