在ES6 (ES2015/JavaScript.next/Harmony)中是否有一个空安全的属性访问(空传播/存在)操作符?比如CoffeeScript ?还是计划在ES7中?

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

大致如下:

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

理想情况下,解决方案不应该分配(甚至未定义)一个东西,如果posblynull是空的


不。你可以在JavaScript中使用lodash#get或类似的东西。


不,在ES6中没有空传播运算符。你必须选择一个已知的模式。

不过,你可以使用析构:

({thing: aThing} = possiblyNull);

有很多关于在ES7中添加这样一个操作符的讨论(例如这个),但直到几年后可选的链接语法在ES2020中标准化,才真正开始流行起来。


香草替代安全的财产访问

(((a.b || {}).c || {}).d || {}).e

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

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

根据这里的列表,目前还没有建议将安全遍历添加到Ecmascript中。所以不仅没有很好的方法,而且在可预见的将来也不会加入。

编辑:由于这篇文章最初是我写的,所以它实际上是被添加到语言中的。


它不像?那样好。运算符,但要实现类似的结果,你可以这样做:

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

因为null和undefined都是假值(参见参考),所以只有当前面的属性不是null或undefined时,才会访问&&运算符后面的属性。

或者,你可以这样写一个函数:

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

更新(2022-01-13):似乎人们仍在发现这一点,以下是目前的情况:

可选链接现在在规范(ES2020)中,并且被所有现代浏览器支持,更多的在存档建议中:https://github.com/tc39/proposal-optional-chaining babel-preset-env:如果你需要支持没有它的旧环境,这可能是你想要的https://babeljs.io/docs/en/babel-preset-env Babel v7插件:https://babeljs.io/docs/en/babel-plugin-proposal-optional-chaining

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

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

原:

实现这一功能的功能目前处于阶段1:可选链接。

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

如果你现在就想使用它,有一个Babel插件可以实现这一点。

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


我认为这个问题在2018年需要重新审视一下。这可以在不使用Object.defineProperty()的任何库的情况下很好地完成,并可以如下使用:

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

我认为这是安全的(而且合乎js-ethical),因为Object的defineProperty方法现在有可写和可枚举的定义,如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版本中,我还为空值添加了一个自定义异常。这是可选的,所以我把它排除在上面的最小定义之外。

欢迎改进


一个安全的deep 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('.'))  

// 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);

2020年解决方案?和? ?

你现在可以直接使用?(可选链接)内联安全测试的存在。所有现代浏览器都支持它。

?? (Nullish Coalescing)可以用来设置一个默认值,如果是undefined或null。

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

如果属性存在,?。继续进行下一个检查,或返回有效值。任何故障都将立即短路并返回未定义。

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

如果不检查大小写,左侧属性必须存在。如果不是,它将抛出异常。

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

?. 浏览器支持- 92%,2021年11月

?? 浏览器支持- 92%

Mozilla的文档

--

逻辑空赋值,2020+解决方案

新的操作符目前正在添加到浏览器中,??=, ||=和&&=。它们做的并不是你想要的,但根据你代码的目的,它们可能会导致相同的结果。

注意:这些在公共浏览器版本中还不常见,但Babel应该可以很好地编译。将随着可用性更改而更新。

??=检查左边是否未定义或为空,如果已经定义则短路。如果不是,则将左边的值赋给右边的值。||=和&&=类似,但基于||和&&操作符。

基本的例子

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" }

浏览器支持2021年11月- 90%

Mozilla的文档



// The code for the regex isn't great, 
// but it suffices for most use cases.

/**
 * Gets the value at `path` of `object`.
 * If the resolved value is `undefined`,
 * or the property does not exist (set param has: true),
 * the `defaultValue` is returned in its place.
 *
 * @param {Object} object The object to query.
 * @param {Array|string} path The path of the property to get.
 * @param {*} [def] The value returned for `undefined` resolved values.
 * @param {boolean} [has] Return property instead of default value if key exists.
 * @returns {*} Returns the resolved value.
 * @example
 *
 * var object = { 'a': [{ 'b': { 'c': 3 } }], b: {'c-[d.e]': 1}, c: { d: undefined, e: 0 } };
 *
 * dotGet(object, 'a[0].b.c');
 * // => 3
 * 
 * dotGet(object, ['a', '0', 'b', 'c']);
 * // => 3
 *
 * dotGet(object, ['b', 'c-[d.e]']);
 * // => 1
 *
 * dotGet(object, 'c.d', 'default value');
 * // => 'default value'
 *
 * dotGet(object, 'c.d', 'default value', true);
 * // => undefined
 *
 * dotGet(object, 'c.d.e', 'default value');
 * // => 'default value'
 *
 * dotGet(object, 'c.d.e', 'default value', true);
 * // => 'default value'
 *
 * dotGet(object, 'c.e') || 5; // non-true default value
 * // => 5 
 * 
 */
var dotGet = function (obj, path, def, has) {
    return (typeof path === 'string' ? path.split(/[\.\[\]\'\"]/) : path)
    .filter(function (p) { return 0 === p ? true : p; })
    .reduce(function (o, p) {
        return typeof o === 'object' ? ((
            has ? o.hasOwnProperty(p) : o[p] !== undefined
        ) ? o[p] : def) : def;
    }, obj);
}