javascript中是否存在null-coalescing(Elvis)运算符或安全导航运算符?

2020/10/14 12:41 · javascript ·  · 0评论

我将举例说明:

猫王算子(?:)

“ Elvis运算符”是Java三元运算符的缩写。一个方便的例子是,如果表达式解析为false或null,则返回“明智的默认值”。一个简单的示例可能如下所示:

def gender = user.male ? "male" : "female"  //traditional ternary operator usage

def displayName = user.name ?: "Anonymous"  //more compact Elvis operator

安全导航操作员(?。)

安全导航运算符用于避免NullPointerException。通常,当您具有对对象的引用时,可能需要在访问对象的方法或属性之前验证其是否为null。为了避免这种情况,安全的导航运算符将只返回null而不是引发异常,例如:

def user = User.find( "admin" )           //this might be null if 'admin' does not exist
def streetName = user?.address?.street    //streetName will be null if user or user.address is null - no NPE thrown

您可以使用逻辑“ OR”运算符代替Elvis运算符:

例如displayname = user.name || "Anonymous"

但是Javascript当前没有其他功能。如果您需要其他语法,建议您查看CoffeeScript它具有一些速记,与您要查找的类似。

例如存在运算符

zip = lottery.drawWinner?().address?.zipcode

功能快捷键

()->  // equivalent to function(){}

性感函数调用

func 'arg1','arg2' // equivalent to func('arg1','arg2')

也有多行注释和类。显然,您必须将其编译为javascript或插入为页面,<script type='text/coffeescript>'但这会增加很多功能:)。使用<script type='text/coffeescript'>实际上仅用于开发而非生产。

我认为以下内容等同于安全导航运算符,尽管更长一些:

var streetName = user && user.address && user.address.street;

streetName然后将是任一值user.address.streetundefined

如果您希望它默认为其他选项,则可以将其与上述快捷方式结合使用或给出:

var streetName = (user && user.address && user.address.street) || "Unknown Street";

JavaScript的逻辑OR运算符短路,并可以取代你的“猫王”操作符:

var displayName = user.name || "Anonymous";

但是,据我所知,这与您的?.操作员不相上下。

我偶尔发现以下成语很有用:

a?.b?.c

可以重写为:

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

这利用了以下事实:在对象上获取未知属性会返回未定义,而不是像null上那样引发异常undefined,因此我们在导航之前将null和undefined替换为空对象。

2020更新

JavaScript现在具有猫王操作员和安全导航操作员的等效项。


安全财产访问

可选的链接运营商?.)是目前阶段4 ECMAScript的建议您现在可以在Babel中使用它

// `undefined` if either `a` or `b` are `null`/`undefined`. `a.b.c` otherwise.
const myVariable = a?.b?.c;

逻辑AND运算符&&)是“老”,更详细的处理这种情况的方式。

const myVariable = a && a.b && a.b.c;

提供默认值

所述nullish合并运算符??)是目前阶段4的ECMAScript提案您现在可以在Babel中使用它如果运算符的左侧为空值(null/ undefined,则可以设置默认值

const myVariable = a?.b?.c ?? 'Some other value';

// Evaluates to 'Some other value'
const myVariable2 = null ?? 'Some other value';

// Evaluates to ''
const myVariable3 = '' ?? 'Some other value';

逻辑OR运算符||)是一种替代解决方案略有不同的行为如果运算符的左侧为falsy,则可以设置默认值请注意,myVariable3下面的结果myVariable3上面的结果不同

const myVariable = a?.b?.c || 'Some other value';

// Evaluates to 'Some other value'
const myVariable2 = null || 'Some other value';

// Evaluates to 'Some other value'
const myVariable3 = '' || 'Some other value';

我认为lodash_.get()可以为您提供帮助,例如和中_.get(user, 'name'),以及诸如_.get(o, 'a[0].b.c', 'default-value')

当前有一个规范草案:

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

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

现在,虽然,我喜欢用lodashget(object, path [,defaultValue])DLVdelve(obj, keypath)

更新(截至2019年12月23日):

可选链接已移至阶段4

对于前者,您可以使用||Javascript的“逻辑或”运算符,而不是简单地返回固定的true和false值,遵循遵循以下规则:如果为true,则返回其left参数;否则,求值并返回其right参数。当您只对真值感兴趣时,它的工作原理相同,但这也意味着foo || bar || baz返回包含真值的foo,bar或baz中最左边的一个

不过,您不会找到可以区分false和null的值,并且0和empty string是false值,因此请避免使用可以合法为0或value || default构造value""

就在这里!🍾

可选链接处于阶段4,这使您可以使用user?.address?.street公式。

如果您迫不及待想要发布,请安装@babel/plugin-proposal-optional-chaining并使用它。这是适用于我的设置,或者只是阅读Nimmo的文章

// package.json

{
  "name": "optional-chaining-test",
  "version": "1.0.0",
  "main": "index.js",
  "devDependencies": {
    "@babel/plugin-proposal-optional-chaining": "7.2.0",
    "@babel/core": "7.2.0",
    "@babel/preset-env": "^7.5.5"
  }
  ...
}
// .babelrc

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "debug": true
      }
    ]
  ],
  "plugins": [
    "@babel/plugin-proposal-optional-chaining"
  ]
}
// index.js

console.log(user?.address?.street);  // it works

这是等效的简单elvis运算符:

function elvis(object, path) {
    return path ? path.split('.').reduce(function (nestedObject, key) {
        return nestedObject && nestedObject[key];
    }, object) : object;
}

> var o = { a: { b: 2 }, c: 3 };
> elvis(o)

{ a: { b: 2 }, c: 3 }

> elvis(o, 'a');

{ b: 2 }

> elvis(o, 'a.b');

2

> elvis(o, 'x');

undefined

2019年9月更新

是的,JS现在支持此功能。可选链接即将在v8中推出了解更多信息

这通常称为空合并运算符。Javascript没有。

您可以说出大致相同的效果:

var displayName = user.name || "Anonymous";

我有一个解决方案,可以根据您的需要进行调整,摘自我的一个库:

    elvisStructureSeparator: '.',

    // An Elvis operator replacement. See:
    // http://coffeescript.org/ --> The Existential Operator
    // http://fantom.org/doc/docLang/Expressions.html#safeInvoke
    //
    // The fn parameter has a SPECIAL SYNTAX. E.g.
    // some.structure['with a selector like this'].value transforms to
    // 'some.structure.with a selector like this.value' as an fn parameter.
    //
    // Configurable with tulebox.elvisStructureSeparator.
    //
    // Usage examples: 
    // tulebox.elvis(scope, 'arbitrary.path.to.a.function', fnParamA, fnParamB, fnParamC);
    // tulebox.elvis(this, 'currentNode.favicon.filename');
    elvis: function (scope, fn) {
        tulebox.dbg('tulebox.elvis(' + scope + ', ' + fn + ', args...)');

        var implicitMsg = '....implicit value: undefined ';

        if (arguments.length < 2) {
            tulebox.dbg(implicitMsg + '(1)');
            return undefined;
        }

        // prepare args
        var args = [].slice.call(arguments, 2);
        if (scope === null || fn === null || scope === undefined || fn === undefined 
            || typeof fn !== 'string') {
            tulebox.dbg(implicitMsg + '(2)');
            return undefined;   
        }

        // check levels
        var levels = fn.split(tulebox.elvisStructureSeparator);
        if (levels.length < 1) {
            tulebox.dbg(implicitMsg + '(3)');
            return undefined;
        }

        var lastLevel = scope;

        for (var i = 0; i < levels.length; i++) {
            if (lastLevel[levels[i]] === undefined) {
                tulebox.dbg(implicitMsg + '(4)');
                return undefined;
            }
            lastLevel = lastLevel[levels[i]];
        }

        // real return value
        if (typeof lastLevel === 'function') {
            var ret = lastLevel.apply(scope, args);
            tulebox.dbg('....function value: ' + ret);
            return ret;
        } else {
            tulebox.dbg('....direct value: ' + lastLevel);
            return lastLevel;
        }
    },

奇迹般有效。享受更少的痛苦!

您可以自己滚动:

function resolve(objectToGetValueFrom, stringOfDotSeparatedParameters) {
    var returnObject = objectToGetValueFrom,
        parameters = stringOfDotSeparatedParameters.split('.'),
        i,
        parameter;

    for (i = 0; i < parameters.length; i++) {
        parameter = parameters[i];

        returnObject = returnObject[parameter];

        if (returnObject === undefined) {
            break;
        }
    }
    return returnObject;
};

并像这样使用它:

var result = resolve(obj, 'a.b.c.d'); 

*如果a,b,c或d中的任何一个未定义,则结果不确定。

我阅读了这篇文章(https://www.beyondjava.net/elvis-operator-aka-safe-navigation-javascript-typescript),并使用代理修改了解决方案。

function safe(obj) {
    return new Proxy(obj, {
        get: function(target, name) {
            const result = target[name];
            if (!!result) {
                return (result instanceof Object)? safe(result) : result;
            }
            return safe.nullObj;
        },
    });
}

safe.nullObj = safe({});
safe.safeGet= function(obj, expression) {
    let safeObj = safe(obj);
    let safeResult = expression(safeObj);

    if (safeResult === safe.nullObj) {
        return undefined;
    }
    return safeResult;
}

您这样称呼它:

safe.safeGet(example, (x) => x.foo.woo)

对于沿其路径遇到null或undefined的表达式,结果将是不确定的。您可以疯狂修改Object原型!

Object.prototype.getSafe = function (expression) {
    return safe.safeGet(this, expression);
};

example.getSafe((x) => x.foo.woo);

跳转到很晚,目前在第2阶段有一个关于可选链的建议[1],并提供了babel插件[2]。我目前所知道的任何浏览器中都没有它。

  1. https://github.com/tc39/proposal-optional-chaining
  2. https://www.npmjs.com/package/@babel/plugin-proposal-optional-chaining

长期以来,这对我来说都是一个问题。我必须想出一个解决方案,一旦获得Elvis操作员之类的东西,就可以轻松移植。

这就是我用的;适用于数组和对象

把它放在tools.js文件或其他东西中

// this will create the object/array if null
Object.prototype.__ = function (prop) {
    if (this[prop] === undefined)
        this[prop] = typeof prop == 'number' ? [] : {}
    return this[prop]
};

// this will just check if object/array is null
Object.prototype._ = function (prop) {
    return this[prop] === undefined ? {} : this[prop]
};

用法示例:

let student = {
    classes: [
        'math',
        'whatev'
    ],
    scores: {
        math: 9,
        whatev: 20
    },
    loans: [
        200,
        { 'hey': 'sup' },
        500,
        300,
        8000,
        3000000
    ]
}

// use one underscore to test

console.log(student._('classes')._(0)) // math
console.log(student._('classes')._(3)) // {}
console.log(student._('sports')._(3)._('injuries')) // {}
console.log(student._('scores')._('whatev')) // 20
console.log(student._('blabla')._('whatev')) // {}
console.log(student._('loans')._(2)) // 500 
console.log(student._('loans')._(1)._('hey')) // sup
console.log(student._('loans')._(6)._('hey')) // {} 

// use two underscores to create if null

student.__('loans').__(6)['test'] = 'whatev'

console.log(student.__('loans').__(6).__('test')) // whatev

好吧,我知道这会使代码有些不可读,但这是一个简单的线性解决方案,效果很好。我希望它可以帮助某人:)

对于使用某些mixin的安全导航操作员来说,这是一个有趣的解决方案。

http://jsfiddle.net/avernet/npcmv/

  // Assume you have the following data structure
  var companies = {
      orbeon: {
          cfo: "Erik",
          cto: "Alex"
      }
  };

  // Extend Underscore.js
  _.mixin({ 
      // Safe navigation
      attr: function(obj, name) { return obj == null ? obj : obj[name]; },
      // So we can chain console.log
      log: function(obj) { console.log(obj); }
  });

  // Shortcut, 'cause I'm lazy
  var C = _(companies).chain();

  // Simple case: returns Erik
  C.attr("orbeon").attr("cfo").log();
  // Simple case too, no CEO in Orbeon, returns undefined
  C.attr("orbeon").attr("ceo").log();
  // IBM unknown, but doesn't lead to an error, returns undefined
  C.attr("ibm").attr("ceo").log();

我创建了一个软件包,使它更易于使用。

NPM jsdig
Github jsdig

您可以处理诸如和对象之类的简单事情:

const world = {
  locations: {
    europe: 'Munich',
    usa: 'Indianapolis'
  }
};

world.dig('locations', 'usa');
// => 'Indianapolis'

world.dig('locations', 'asia', 'japan');
// => 'null'

或更复杂:

const germany = () => 'germany';
const world = [0, 1, { location: { europe: germany } }, 3];
world.dig(2, 'location', 'europe') === germany;
world.dig(2, 'location', 'europe')() === 'germany';

我个人使用

function e(e,expr){try{return eval(expr);}catch(e){return null;}};

例如,安全获取:

var a = e(obj,'e.x.y.z.searchedField');
本文地址:http://javascript.askforanswer.com/javascriptzhongshifoucunzainull-coalescingelvisyunsuanfuhuoanquandaohangyunsuanfu.html
文章标签: ,   ,   ,  
版权声明:本文为原创文章,版权归 javascript 所有,欢迎分享本文,转载请保留出处!

文件下载

老薛主机终身7折优惠码boke112

上一篇:
下一篇:

评论已关闭!