มีตัวดำเนินการ null-coalescing (Elvis) หรือตัวดำเนินการนำทางที่ปลอดภัยใน javascript หรือไม่


210

ฉันจะอธิบายด้วยตัวอย่าง:

Elvis Operator (?:)

"ตัวดำเนินการ Elvis" เป็นการย่อของตัวดำเนินการแบบไตรภาคของ Java อินสแตนซ์หนึ่งที่ซึ่งมีประโยชน์สำหรับการส่งคืนค่า 'ค่าเริ่มต้นที่สมเหตุสมผล' หากนิพจน์แก้ไขเป็นเท็จหรือเป็นโมฆะ ตัวอย่างง่ายๆอาจมีลักษณะเช่นนี้:

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

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

ผู้ดำเนินการนำทางที่ปลอดภัย (?.)

ตัวดำเนินการการนำทางที่ปลอดภัยถูกใช้เพื่อหลีกเลี่ยง NullPointerException โดยทั่วไปเมื่อคุณมีการอ้างอิงไปยังวัตถุคุณอาจต้องตรวจสอบว่ามันไม่เป็นโมฆะก่อนที่จะเข้าถึงวิธีการหรือคุณสมบัติของวัตถุ เพื่อหลีกเลี่ยงปัญหานี้ตัวดำเนินการการนำทางที่ปลอดภัยจะคืนค่า 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

9
'Elvis Operator' มีอยู่ใน C # - แต่เรียกว่าตัวดำเนินการรวมศูนย์ (น่าตื่นเต้นน้อยกว่า) :-)
Cameron

หากคุณต้องการไวยากรณ์ทางเลือกคุณสามารถดู cofeescript ได้
Lime

คำถามนี้เป็นระเบียบ ... มันกำลังผสมตัวดำเนินการต่างกัน 3 ตัวใช่ไหม : (ผู้ประกอบการ ternery สะกดในคำถามอาจเป็นพิมพ์ผิด), ?? (รวมตัวกันว่างซึ่งมีอยู่ใน JavaScript) และ? (Elvis) ซึ่งไม่มีอยู่ใน JavaScript คำตอบไม่ได้ทำให้ความแตกต่างนี้ชัดเจนมาก
JoelFan

2
@JoelFan คุณสามารถให้ลิงก์ไปยังเอกสารประกอบเกี่ยวกับ null-coalescence ( ??) ที่เหมาะสมใน javascript ได้หรือไม่ ทุกสิ่งที่ฉันค้นพบชี้ให้เห็นว่า JS มีการรวมกันของ "falsey" เท่านั้น (โดยใช้||)
Charles Wood

1
ฉันไม่ได้ตั้งใจจะพูดว่า JS มีตัวอักษร? แต่มันก็มีค่าว่างรวม ... แต่ถึงแม้ฉันจะมีชนิดของผิด ที่ถูกกล่าวว่าฉันได้เห็นรหัส JS จำนวนมากที่ใช้ || ในฐานะที่เป็นโมฆะรวมกันแม้จะมีข้อผิดพลาดที่ผิดพลาด
JoelFan

คำตอบ:


139

คุณสามารถใช้ตัวดำเนินการ 'OR' แบบโลจิคัลแทนตัวดำเนินการ Elvis:

displayname = user.name || "Anonymous"เช่น

แต่ในปัจจุบัน Javascript ไม่มีฟังก์ชั่นอื่น ๆ ฉันขอแนะนำให้ดูที่CoffeeScriptถ้าคุณต้องการไวยากรณ์ทางเลือก มีชวเลขบางอย่างที่คล้ายกับสิ่งที่คุณกำลังมองหา

ตัวอย่างเช่นผู้ประกอบการที่มีอยู่

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

ฟังก์ชั่นทางลัด

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

ฟังก์ชั่นการโทรเซ็กซี่

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

นอกจากนี้ยังมีความคิดเห็นหลายชั้นและชั้นเรียน เห็นได้ชัดว่าคุณต้องรวบรวมสิ่งนี้เพื่อจาวาสคริปต์หรือแทรกลงในหน้า<script type='text/coffeescript>'แต่มันเพิ่มฟังก์ชั่นมากมาย :) การใช้<script type='text/coffeescript'>นั้นมีไว้สำหรับการพัฒนาเท่านั้นและไม่ใช่การผลิต


14
ตรรกะหรือไม่ใช่สิ่งที่จำเป็นในกรณีส่วนใหญ่เนื่องจากคุณอาจต้องการให้ตัวถูกดำเนินการที่ถูกต้องเฉพาะถ้าด้านซ้ายนั้นไม่ได้ถูกกำหนด แต่ไม่ใช่เมื่อมันถูกกำหนดและเป็นเท็จ
user2451227

มันเป็นความผิดพลาดของฉันหรือเป็นจริง<script type='coffee/script>'เหรอ?
JCCM

2
CoffeeScript <script type="text/coffeescript">ใช้หน้าบ้าน
อีเลียสซามาเรีย

19
ขณะนี้ตอบคำถามมันเกือบทั้งหมดเกี่ยวกับ coffeescript มากกว่า javascript และมากกว่าครึ่งเกี่ยวกับการอธิบายประโยชน์ coffeescript ที่ไม่เกี่ยวข้องกับ OP ฉันขอแนะนำให้ต้มลงไปในสิ่งที่เกี่ยวข้องกับคำถามเพราะมีประโยชน์อื่น ๆ ของกาแฟ
jinglesthula

4
ฉันจะไปกล้วยเหรอ แน่นอนว่าการคัดค้านของผู้ใช้งาน 2,212,1227 (ปัจจุบันมี 4 คะแนน) ไม่ถูกต้องเพราะตัวถูกดำเนินการกลางของ ternary (เช่นตัวถูกดำเนินการด้านขวากับตัวดำเนินการ Elvis) จะไม่ถูกเลือกอย่างเท่าเทียมกันหากมีการกำหนดตัวถูกดำเนินการนิพจน์ / ซ้าย x === undefinedในทั้งสองกรณีแล้วคุณต้องไป
ไมค์หนู

114

ฉันคิดว่าสิ่งต่อไปนี้เทียบเท่ากับโอเปอเรเตอร์ที่ปลอดภัยแม้ว่าจะนานกว่านี้เล็กน้อย:

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

streetNameจากนั้นจะเป็นได้ทั้งค่าของหรือuser.address.streetundefined

หากคุณต้องการให้ค่าเริ่มต้นเป็นอย่างอื่นคุณสามารถรวมกับทางลัดด้านบนหรือเพื่อให้:

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

7
บวกหนึ่งสำหรับตัวอย่างที่ดีของทั้งการขยายพันธุ์โมฆะและการรวมกันเป็นโมฆะ!
Jay Wick

1
งานนี้ยกเว้นว่าคุณจะไม่ทราบว่าคุณได้รับโมฆะหรือไม่ได้กำหนดออกมา
เดฟ Cousineau

82

ตัวดำเนินการ OR ตรรกะของ Javascript กำลังลัดวงจรและสามารถแทนที่โอเปอเรเตอร์ "Elvis" ของคุณได้:

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

อย่างไรก็ตามสำหรับความรู้ของฉันไม่มีอะไรเทียบเท่ากับ?.ผู้ให้บริการของคุณ


13
+1 ฉันลืมว่า||สามารถใช้วิธีนี้ได้ โปรดทราบว่านี้จะรวมกันไม่เพียง แต่เมื่อการแสดงออกเป็นnullแต่ยังเมื่อมันไม่ได้กำหนดfalse, 0หรือสตริงที่ว่างเปล่า
คาเมรอน

@Cameron แน่นอน แต่ถูกกล่าวถึงในคำถามและดูเหมือนว่าจะเป็นเจตนาของผู้ถาม ""หรือ0อาจคาดไม่ถึง :)
Frédéric Hamidi

72

ฉันพบบางครั้งสำนวนที่เป็นประโยชน์ต่อไปนี้:

a?.b?.c

สามารถเขียนใหม่เป็น:

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

สิ่งนี้ใช้ประโยชน์จากความจริงที่ว่าการได้รับคุณลักษณะที่ไม่รู้จักบนวัตถุจะส่งคืนไม่ได้กำหนดแทนที่จะส่งข้อยกเว้นตามที่ทำnullหรือundefinedดังนั้นเราจึงเปลี่ยน null และไม่ได้กำหนดด้วยวัตถุเปล่าก่อนการนำทาง


14
อ่านยาก แต่ก็ดีกว่า&&วิธีverbose +1
กรีด

1
นั่นเป็นโอเปอเรเตอร์ที่ปลอดภัยเพียงแห่งเดียวในจาวาสคริปต์จริงๆ ตัวดำเนินการ 'OR' แบบโลจิคัลที่กล่าวถึงข้างต้นเป็นอย่างอื่น
vasilakisfil

@Filippos คุณสามารถยกตัวอย่างพฤติกรรมที่แตกต่างในตรรกะ OR และวิธีการ && ฉันไม่สามารถนึกถึงความแตกต่าง
Red Pea

นอกจากนี้ยังช่วยให้การนำทางค่าที่ไม่ระบุตัวตนโดยไม่ต้องกำหนดให้กับตัวแปรก่อน
Matt Jenkins

1
รักมัน! มีประโยชน์จริง ๆ ถ้าคุณต้องการได้รับคุณสมบัติของวัตถุหลังจากการดำเนินการ array.find () ที่อาจไม่ส่งคืนผลลัพธ์ใด ๆ
Shiraz

24

ฉันคิดว่า lodash _.get()สามารถช่วยได้ที่นี่เช่นเดียวกับใน_.get(user, 'name')และงานที่ซับซ้อนมากขึ้นเช่น_.get(o, 'a[0].b.c', 'default-value')


5
ปัญหาหลักของฉันด้วยวิธีนี้คือความจริงที่ว่าเนื่องจากชื่อของคุณสมบัติเป็นสตริงคุณจึงไม่สามารถใช้ฟังก์ชันการปรับโครงสร้างของ IDE ของคุณด้วยความเชื่อถือ 100% อีกต่อไป
RPDeshaies

21

ขณะนี้มีข้อมูลจำเพาะฉบับร่าง:

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

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

แต่สำหรับตอนนี้ฉันชอบใช้lodashget(object, path [,defaultValue])หรือdlvdelve(obj, keypath)

อัปเดต (ณ วันที่ 23 ธันวาคม 2019):

การผูกมัดเสริมได้ย้ายไปยังขั้นตอนที่ 4 แล้ว


Lodash ทำให้การเขียนโปรแกรมใน javascript เป็นที่พอใจมากขึ้น
ตุ๊กแก

2
การผูกมัดตัวเลือกเพิ่งย้ายไปยังขั้นตอนที่ 4ดังนั้นเราจะเห็นมันใน ES2020
Nick Parsons

1
@ NickParsons ขอบคุณ! ได้อัปเดตคำตอบ
Jack Tuck

18

อัพเดต 2019

ขณะนี้ JavaScript มีสิ่งที่เทียบเท่าสำหรับทั้ง Elvis Operator และ Safe Navigation Operator


การเข้าถึงคุณสมบัติที่ปลอดภัย

ประกอบการผูกมัดตัวเลือก ( ?.) ปัจจุบันเป็น4 ขั้นตอน ECMAScript ข้อเสนอ คุณสามารถใช้กับ Babelได้แล้ววันนี้

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

ตรรกะและผู้ประกอบการ ( &&) เป็น "เก่า" วิธีมากขึ้นอย่างละเอียดเพื่อจัดการกับสถานการณ์นี้

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

ให้ค่าเริ่มต้น

ประกอบ nullish coalescing ( ??) ขณะนี้เป็นขั้นตอนที่ 3 ECMAScript ข้อเสนอ คุณสามารถใช้กับ Babelได้แล้ววันนี้ อนุญาตให้คุณตั้งค่าเริ่มต้นหากด้านซ้ายมือของผู้ประกอบการเป็นค่า Nullary ( 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';

หรือผู้ประกอบการลอจิคัล ( ||) เป็นโซลูชั่นทางเลือกที่มีพฤติกรรมแตกต่างกันเล็กน้อย อนุญาตให้คุณตั้งค่าเริ่มต้นหากด้านซ้ายมือของผู้ปฏิบัติงานผิดพลาด โปรดทราบว่าผลลัพธ์ของ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';

1
คำตอบนี้ต้องการ upvotes เพิ่มเติม Nullish Coalescing Operator อยู่ในขั้นตอนที่ 4
Yerke

13

||สำหรับอดีตคุณสามารถใช้ ตัวดำเนินการ Javascript "แบบลอจิคัลหรือ" ไม่ใช่เพียงแค่คืนค่าจริงและเท็จแบบกระป๋องตามกฎการคืนค่าอาร์กิวเมนต์ด้านซ้ายหากเป็นจริงและประเมินและคืนค่าอาร์กิวเมนต์ที่ถูกต้อง เมื่อคุณกำลังสนใจเฉพาะในค่าความจริงการทำงานออกมาเหมือนกัน แต่มันก็หมายความว่าfoo || bar || bazผลตอบแทนที่หนึ่งด้านซ้ายสุดของ foo บาร์หรือ baz ที่มีมูลค่าที่แท้จริง

คุณจะไม่พบหนึ่งที่สามารถแยกแยะความแตกต่างเท็จ null, แม้ว่าและ 0 และสตริงที่ว่างเปล่าเป็นค่าเท็จเพื่อหลีกเลี่ยงการใช้value || defaultโครงสร้างที่valueถูกต้องตามกฎหมายสามารถเป็น 0 ""หรือ


4
งานที่ดีสังเกตว่าสิ่งนี้สามารถส่งผลให้เกิดพฤติกรรมที่ไม่คาดคิดเมื่อตัวถูกดำเนินการด้านซ้ายเป็นค่าที่ไม่เป็นค่าว่าง
Shog9

11

ใช่แล้ว! 🍾

การผูกมัดเสริมอยู่ในขั้นตอนที่ 4 และสิ่งนี้จะช่วยให้คุณใช้user?.address?.streetสูตรได้

หากคุณไม่สามารถรอการเปิดตัวติดตั้ง@babel/plugin-proposal-optional-chainingและคุณสามารถใช้งานได้ นี่คือการตั้งค่าของฉันอยู่ที่ทำงานสำหรับฉันหรือเพียงแค่อ่านบทความของนิมโม

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

4
เขาถามว่ามีหรือไม่คุณสามารถเพิ่มได้หรือไม่ ฉันคิดว่านี่ไม่ใช่สิ่งที่มีประโยชน์มากเพราะมันไม่ใช่สิ่งที่ถูกถาม
DeanMWake

2
มาถึงขั้นตอนที่ 3 ของกระบวนการมาตรฐาน ECMAScript es2020 🚀 - babeljs.io/docs/en/babel-plugin-proposal-optional-chaining
WEDI

ฉันคิดว่าคำตอบนี้ทำให้เข้าใจผิดเหมือนที่เป็นอยู่
Leonardo Raele

1
คำตอบนี้ไม่ถูกต้องนัก! การผูกมัดเสริมยังอยู่ในขั้นตอนที่ 3 และ ES2020 ยังไม่ออกหรือสิ้นสุดลง อย่างน้อยคุณก็พูดถึงวิธีการใช้งานโดยไม่ต้องรอให้วางจำหน่าย
Maxie

@gazdagergo ไม่มีปัญหา :)
Maxie

6

ต่อไปนี้เป็นตัวดำเนินการ 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

5

อัปเดตกันยายน 2019

ใช่ตอนนี้ JS สนับสนุนสิ่งนี้ การผูกมัดตัวเลือกกำลังจะมาเร็ว ๆ นี้เพื่อ v8 อ่านเพิ่มเติม


ไม่เหมือนกันเลยทีเดียว OP เปิดใช้งานเกี่ยวกับการรวมกันเป็นโมฆะ แต่คำตอบที่ดียังคงอยู่
Maxie

4

ซึ่งเป็นที่รู้จักกันทั่วไปว่าเป็นตัวดำเนินการรวมศูนย์ว่าง Javascript ไม่มี


3
จริงในแง่ที่เข้มงวด แต่อย่างที่คำตอบอื่น ๆ ได้สังเกตว่าตัวดำเนินการหรือตรรกะของ JavaScript สามารถทำงานได้เหมือนตัวดำเนินการเท็จที่รวมตัวกันช่วยให้คุณบรรลุความกระชับเท่ากันในหลาย ๆ สถานการณ์
Shog9

1
นี่ไม่ใช่ตัวดำเนินการรวมศูนย์ว่าง การรวมกันแบบ Null ทำงานได้กับค่าเดียวเท่านั้นไม่ได้อยู่ในเครือข่ายของการเรียกใช้คุณสมบัติ / การเรียกใช้ฟังก์ชัน คุณสามารถทำการรวมกันเป็นโมฆะด้วยตัวดำเนินการตรรกะหรือ OR ใน JavaScript ได้แล้ว

ไม่ได้คุณสามารถทำการเชื่อมโยงเท็จด้วยตรรกะ OR ใน JavaScript
andresp


2

ฉันมีทางออกสำหรับสิ่งนั้นปรับให้เหมาะกับความต้องการของคุณตัดตอนมาจากหนึ่งใน libs ของฉัน:

    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;
        }
    },

ทำงานเหมือนจับใจ เพลิดเพลินกับความเจ็บปวดน้อยลง!


ดูเหมือนว่าคุณสัญญาได้หรือไม่ คุณมีที่สาธารณะบ้างไหม? (เช่น GitHub)
Eran Medan

1
ฉันจะสร้างข้อความที่ตัดตอนมาเล็ก ๆ จากรหัสที่ฉันใช้ในและจะโพสต์บน GitHub ในหนึ่งสัปดาห์หรือมากกว่านั้น
balazstth

2

คุณสามารถม้วนของคุณเอง:

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 ใด ๆ ไม่ได้ถูกกำหนด


1

ฉันอ่านบทความนี้ ( 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)

ผลลัพธ์จะไม่ถูกกำหนดสำหรับนิพจน์ที่พบว่าเป็นโมฆะหรือไม่ได้กำหนดตามเส้นทาง คุณสามารถไปป่าและปรับเปลี่ยนต้นแบบวัตถุ!

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

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

1

ในช่วงปลายปีนี้มีข้อเสนอ [1] สำหรับการผูกมัดเพิ่มเติมที่ขั้นตอนที่ 2 โดยมีปลั๊กอินของ Babel [2] ขณะนี้ยังไม่มีเบราว์เซอร์ใด ๆ ที่ฉันรู้จัก

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

1

นี่เป็นปัญหาสำหรับฉันมานาน ฉันต้องหาวิธีแก้ปัญหาที่สามารถโยกย้ายได้ง่ายเมื่อเราได้ผู้ประกอบการ 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

ฉันรู้ว่ามันทำให้โค้ดไม่สามารถอ่านได้ แต่มันเป็นวิธีแก้ปัญหาซับที่ง่ายและใช้งานได้ดี ฉันหวังว่ามันจะช่วยให้ใครบางคน :)


0

นี่เป็นวิธีแก้ปัญหาที่น่าสนใจสำหรับผู้ควบคุมระบบนำทางที่ปลอดภัยโดยใช้มิกซ์บางตัว ..

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

0

ฉันสร้างแพ็คเกจที่ทำให้ใช้งานง่ายขึ้นมาก

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';

-6

ส่วนตัวผมใช้

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

และตัวอย่างเช่นรับปลอดภัย:

var a = e(obj,'e.x.y.z.searchedField');

2
ครั้งแรกของคุณมันไม่ควรใช้ EVAL ประการที่สองสิ่งนี้ไม่ได้ผล: e({a:{b:{c:{d:'test'}}}}, 'a.b.c.d')กลับnullมา
Pylinux

@Pylinux โดยทั่วไปสิ่งที่จะทำงานคือe = eval, var a = eval('obj.a.b.c.d'). evalไม่ได้ใช้พารามิเตอร์ตัวที่สอง ... developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/
Dorian
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.