การเข้าถึงคุณสมบัติที่ไม่ปลอดภัย (และการกำหนดเงื่อนไข) ใน ES6 / 2015


149

มีตัวnullดำเนินการ -safe property (การขยายพันธุ์ / การดำรงอยู่ของ null) ใน ES6 (ES2015 / JavaScript.next / Harmony) เช่น?.ในCoffeeScriptหรือไม่? หรือจะมีการวางแผนสำหรับ ES7

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

สิ่งนี้จะประมาณ:

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

เป็นการดีที่โซลูชันไม่ควรกำหนด (แม้undefined) ให้กับaThingถ้าpossiblyNullเป็นnull


3
@naomik การตรวจสอบโมฆะแบบนี้จะมีประโยชน์มากหากข้อความที่คุณกำลังตรวจสอบคุณสมบัติที่ซ้อนกันอย่างลึกล้ำเช่นif( obj?.nested?.property?.value )แทนที่จะเป็นif( obj && obj.nested && obj.nested.property && obj.nested.property.value )
Sean Walsh

@SeanWalsh หากวัตถุของคุณซ้อนกันอย่างลึกล้ำหรือหากฟังก์ชันของคุณกำลังขุดลึกลงไปในวัตถุของคุณอาจมีปัญหาอื่น ๆ อีกมากมายที่แอพของคุณเช่นกัน
ขอบคุณ

1
เปรียบเทียบไปvar appConfig = loadConfig(config, process.env); connect(appConfig.database); connect(config)คุณสามารถส่งผ่านวัตถุที่ง่ายมากในการconnectแทนการส่งผ่านทั้งconfigวัตถุที่คุณสามารถใช้conf.username, conf.passwordแทนที่จะพยายามบางอย่างเช่น,config[process.env]?.database?.username config[process.env]?.database?.passwordอ้างอิง: กฎหมายของ Demeter
ขอบคุณ

นอกจากนี้หากคุณทำบางสิ่งเช่นชุดค่าเริ่มต้นหรือคุณสมบัติฆ่าเชื้อ (ซึ่งสามารถทำได้ในloadConfigตัวอย่างด้านบน) คุณสามารถตั้งสมมติฐานเกี่ยวกับการมีอยู่ของคุณสมบัติและข้ามการตรวจสอบ null ในพื้นที่ที่นับไม่ถ้วนของแอปของคุณ
ขอบคุณ

4
@naomik ตราบใดที่ภาษารองรับการวางซ้อนวัตถุมันยังคงเป็นคุณสมบัติที่มีประโยชน์ - ไม่ว่าคุณหรือฉันจะคิดอย่างไรกับสถาปัตยกรรมของแอปเอง กราฟวัตถุที่ซับซ้อนเช่นนี้เป็นเรื่องธรรมดามากใน ORMs ที่กำลังสร้างโมเดลโมเดลข้อมูลที่ซับซ้อน
Sean Walsh

คำตอบ:


98

Update (2020-01-31): ดูเหมือนว่าผู้คนยังคงค้นหาสิ่งนี้อยู่นี่คือเรื่องราวปัจจุบัน:

อัปเดต (2017-08-01): หากคุณต้องการใช้ปลั๊กอินอย่างเป็นทางการคุณสามารถลองใช้ตัวสร้างอัลฟาของ Babel 7 ด้วยการแปลงโฉมใหม่ ไมล์สะสมของคุณอาจแตกต่างกันไป

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


ฉันเข้าใจอย่างถูกต้องหรือไม่ว่าไม่รวมการมอบหมายตามเงื่อนไข street = user.address?.streetจะตั้งค่าstreetในกรณีใด ๆ ?
ᆼᆺᆼ

1
น่าเสียดายจริงๆที่ฉันคิดว่าคุณพูดถูก Street ผมคิดว่าundefinedจะได้รับมอบหมาย แต่อย่างน้อยก็จะไม่พยายามเข้าถึงคุณสมบัติที่ไม่ได้กำหนด
วันที่

2
ดูเหมือนว่าคุณจะสามารถใช้โอเปอเรเตอร์ทางด้านซ้ายมือได้และถ้าหากมันมีอะไรที่ไม่ได้กำหนดมือขวาจะไม่ถูกประเมิน ปลั๊กอิน babel อาจแตกต่างกันเล็กน้อยยังไม่ได้ทดสอบตัวเองมากนัก
วันธรรมดา

1
เกี่ยวกับการมอบหมายแบบมีเงื่อนไขทางด้านซ้ายมือของ=ดูเหมือนว่าไม่ได้รับการสนับสนุนในสเป็คอย่างเป็นทางการในปัจจุบัน github.com/tc39/proposal-optional-chaining#not-supported
วัน

1
เมื่อเดือนพฤษภาคม 2020 ดูเหมือนว่าเบราว์เซอร์ปัจจุบันและ typescript ได้ดำเนินการนี้!
Josh Diehl

65

มันไม่ดีเท่าไหร่หรอก ผู้ประกอบการ แต่เพื่อให้ได้ผลลัพธ์ที่คล้ายกันคุณสามารถทำได้:

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

เนื่องจาก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

ถูกต้องฉันควรชี้แจงคำถามสิ่งสำคัญที่ฉันเป็นหลังจากนั้นคือการมอบหมายตามเงื่อนไข
ᆼᆺᆼ

เป็นเพียงข้อพิสูจน์ว่า เพื่อค้นหาผกผันอย่างปลอดภัยคุณสามารถใช้ !(user && user.address && user.address.postcode) :)
rob2d

foo && foo.bar && foo.bar.quux ...ใน codebase ขนาดใหญ่สิ่งนี้น่าเกลียดและเพิ่มความซับซ้อนมากมายที่คุณควรหลีกเลี่ยง
Skylar Saveland

1
นี่เป็นคำตอบที่สะอาดที่สุดที่ฉันพบบนอินเทอร์เน็ต ฉันใช้สิ่งนี้กับ typescript:_get<T>(func: () => T, fallbackValue?: T) :T
tomwassing

3
ถ้าuser.address.postcodeเป็นไม่ได้กำหนด_try(() => user.address.postcode, "")จะกลับมาแทนundefined ""ดังนั้นรหัส_try(() => user.address.postcode, "").lengthจะเพิ่มข้อยกเว้น
Alexander Chen

33

ไม่คุณอาจใช้lodash # getหรืออะไรทำนองนี้ใน JavaScript


4
มีข้อเสนอที่จะเพิ่มลงใน ES7 หรือไม่?
ᆼᆺᆼ

1
ยังไม่เคยเจอเลย
Girafa

3
@PeterVarga: หลายคน การสนทนามากเกินไป ไวยากรณ์ไม่ใช่เรื่องง่ายสำหรับฟีเจอร์นี้
Bergi

1
lodash.getแตกต่างกันเล็กน้อยในเรื่องที่ไม่สามารถทำการกำหนดแบบมีเงื่อนไขได้
ᆼᆺᆼ

17

ทางเลือกวานิลลาสำหรับการเข้าถึงสถานที่ปลอดภัย

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

การกำหนดเงื่อนไขที่รัดกุมที่สุดน่าจะเป็นสิ่งนี้

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

ดังนั้นคุณจะเขียนการมอบหมายในคำถามโดยใช้กลไกนี้อย่างไร คุณยังไม่จำเป็นต้องมีเงื่อนไขหรือไม่ในกรณีที่possiblyNullไม่ได้กำหนดไว้ / null?
ᆼᆺᆼ

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

แน่นอนเช่นCoffeeScriptมันก็มี||=
ᆼᆺᆼ

1 (((a.b || {}).c || {}).d || {}).eแม้ความคิดแรกของฉันคือ แต่จะแม่นยำกว่านี้อีก((((a || {}).b || {}).c || {}).d || {}).e
IsmailS

6

ไม่ไม่มีโอเปอเรเตอร์การเผยแพร่ว่างใน ES6 คุณจะต้องไปกับหนึ่งในรูปแบบที่รู้จักกัน

คุณอาจสามารถใช้การทำลายล้างได้แม้ว่า:

({thing: aThing} = possiblyNull);

มีการสนทนามากมาย (เช่นนี้ ) เพื่อเพิ่มผู้ประกอบการดังกล่าวใน ES7 แต่ไม่มีใครถอด


นั่นดูน่าสนใจ แต่อย่างน้อยสิ่งที่บาเบลทำกับมันก็ไม่ได้แตกต่างไปจากแค่aThing = possiblyNull.thing
ᆼᆺᆼ

1
@PeterVarga: อ๊ะคุณขวา destructuring งานเมื่อทรัพย์สินที่ไม่ได้มีอยู่ nullแต่ไม่ได้เมื่อวัตถุอยู่ คุณต้องระบุค่าเริ่มต้นเหมือนรูปแบบนี้แต่มีไวยากรณ์ที่สับสนมากกว่า
Bergi

4

ดำเนินการตามรายการที่นี่ไม่มีข้อเสนอในการเพิ่มการสำรวจเส้นทางที่ปลอดภัยใน Ecmascript ดังนั้นไม่เพียง แต่จะไม่มีวิธีที่ดีในการทำเช่นนี้ แต่มันจะไม่ถูกเพิ่มเข้ามาในอนาคตอันใกล้


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

1

ตัวเลือกการ?.ผูกมัดและการรวมกันเป็นศูนย์??

ตอนนี้คุณสามารถใช้?.อินไลน์เพื่อทดสอบการมีอยู่ได้อย่างปลอดภัย เบราว์เซอร์สมัยใหม่ทั้งหมดรองรับ

?? สามารถใช้เพื่อตั้งค่าเริ่มต้นหากไม่ได้กำหนดหรือเป็นโมฆะ

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

??เพื่อให้แน่ใจว่าการกำหนดค่าเริ่มต้นคุณสามารถใช้ หากคุณจำเป็นต้องมีค่า truthy ||แรกคุณสามารถใช้

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

?.การสนับสนุนเบราว์เซอร์ - 78%, กรกฎาคม 2020

??รองรับเบราว์เซอร์ - 78%

เอกสาร Mozilla

-

การกำหนดค่า nullish แบบลอจิคัลโซลูชัน 2020+

ผู้ประกอบการใหม่กำลังถูกเพิ่มเข้าไปในเบราว์เซอร์, ??=, และ||= &&=พวกเขาไม่ได้ทำสิ่งที่คุณกำลังมองหา แต่อาจนำไปสู่ผลลัพธ์เดียวกันขึ้นอยู่กับจุดประสงค์ของรหัสของคุณ

หมายเหตุ: เหล่านี้ไม่ได้ทั่วไปในเบราว์เซอร์รุ่นประชาชนเลยแต่บาเบลควร transpile ดี จะอัปเดตเมื่อมีการเปลี่ยนแปลงความพร้อมให้บริการ

??=ตรวจสอบว่าด้านซ้ายไม่ได้ถูกกำหนดหรือเป็นโมฆะการลัดวงจรถ้ากำหนดไว้แล้ว ถ้าไม่ด้านซ้ายจะถูกกำหนดค่าด้านขวา ||=และ&&=มีความคล้ายคลึง แต่ขึ้นอยู่กับ||และ&&ผู้ประกอบการ

ตัวอย่างพื้นฐาน

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

รองรับบราวเซอร์กรกฎาคม 2563 - .03%

เอกสาร Mozilla


1
คำถามนี้เกี่ยวกับการมอบหมายตามเงื่อนไข
ᆼᆺᆼ

เพิ่มตัวอย่างบางส่วนด้วย?.และ??และวิธีแก้ปัญหาที่จะเกิดขึ้นโดยละเอียดซึ่งอาจเหมาะกับสถานการณ์ของคุณ วิธีการแก้ปัญหาที่ดีที่สุดในปัจจุบันน่าจะเป็นเพียงแค่การทำaThing = possiblyNull?.thing ?? aThing
Gibolt

0

วิธีการลึกที่ปลอดภัยดูเหมือนว่าจะเหมาะสำหรับ 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('.'))  

ในการถอดความ Rick: แค่ฟังดูเหมือนการเขียนโปรแกรมสตริงด้วยขั้นตอนพิเศษ
tocqueville

1
lodash ตอนนี้ดำเนินการรับ / ชุดลึก _.get(obj, array_or_dotstring)
ต้นแบบ

1
แต่เพื่อความเป็นธรรมแม้สัญกรณ์จุดและสตริงของ Javascript นั้นโดยทั่วไปแล้วจะเป็นการเขียนโปรแกรมแบบสตริงobj.a.b.cvsobj['a']['b']['c']
prototype

1
อยู่ที่ไหนkeysมาจากไหน
Levi Roberts

-2

ฉันรู้ว่านี่เป็นคำถาม JavaScript แต่ฉันคิดว่า Ruby จัดการสิ่งนี้ได้ทุกวิธีที่ร้องขอดังนั้นฉันคิดว่านี่เป็นจุดอ้างอิงที่เกี่ยวข้อง

.&, tryและ && มีจุดแข็งและข้อผิดพลาดที่อาจเกิดขึ้น ตัวเลือกมากมายเหล่านี้หมดลงที่นี่: http://mitrev.net/ruby/2015/11/13/the-operator-in-ruby/

TLDR; ข้อสรุปของ Rubyists นั้นdigง่ายกว่าในสายตาและรับประกันได้ว่าค่าหรือnullจะได้รับมอบหมายที่แข็งแกร่งขึ้น

นี่เป็นตัวอย่างง่ายๆใน TypeScript:

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 ดังแสดงในคำตอบก่อน นอกจากนี้ยังไม่ต้องใช้การวนซ้ำซึ่งเป็นข้อเสียเปรียบประการหนึ่งของการใช้งานนี้


ฉันวางคำตอบนี้ไว้ที่นี่เพื่อแก้ปัญหาความสนุก / สมบูรณ์ / ทางเลือก b / c ฉันชอบวิธีที่ทับทิมทำเช่นนี้กับน้ำตาลในรูปแบบประโยค เพิ่งนำสิ่งนี้ไปใช้กับชุมชน - โหวตอีกครั้งและฉันจะลบโพสต์นี้อย่างมีความสุข ไชโย!
theUtherSide

เจ๋งจริง ๆ ที่จะเห็น ECMAScript2019 มีข้อเสนอที่ยอดเยี่ยมสำหรับ Ruby-like "Optional Chaining": github.com/tc39/proposal-optional-chaining มันมีให้บริการแล้ววันนี้ผ่านทาง Babel Plugin และมันก็ใช้งานได้สำหรับการเรียกใช้บนวัตถุด้วย: babeljs.io / docs / en / babel-plugin-ข้อเสนอทางเลือก - ผูกมัด
theUtherSide

-5

ฉันคิดว่าคำถามนี้ต้องการการรีเฟรชเล็กน้อยสำหรับปีพ. ศ. 2561 ซึ่งสามารถทำได้อย่างดีโดยไม่ต้องใช้ห้องสมุดใด ๆObject.defineProperty()และสามารถใช้ดังนี้:

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

ฉันพิจารณาความปลอดภัยนี้ (และ js-ethical) เพราะตอนนี้มีคำจำกัดความwriteableและenumerableคำจำกัดความสำหรับdefinePropertyวิธีการObjectตามที่ได้บันทึกไว้ใน 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 ฉันได้เพิ่มข้อยกเว้นที่กำหนดเองสำหรับค่าว่าง นี่เป็นตัวเลือกและดังนั้นฉันจึงทิ้งมันไปจากคำจำกัดความด้านบน

ยินดีต้อนรับการปรับปรุง


2
ด้วยวิธีนี้คุณจะพฤตินัยเขียนโค้ดเป็นสตริง เป็นความคิดที่ดีที่จะทำเช่นนั้น มันทำให้รหัสของคุณเป็นไปไม่ได้ที่จะ refactor และไม่เป็นมิตรกับ IDE
tocqueville
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.