รับและตั้งค่าใน TypeScript


658

ฉันกำลังพยายามสร้างและตั้งค่าวิธีการสำหรับทรัพย์สิน:

private _name: string;

Name() {
    get:
    {
        return this._name;
    }
    set:
    {
        this._name = ???;
    }
}

คำหลักเพื่อกำหนดค่าคืออะไร


12
ขีดล่างและ PascalCase ขัดแย้งกับแนวทางการเข้ารหัส typescript: github.com/Microsoft/TypeScript/wiki/Coding-guidelines
Niels Steenbeek

2
สวัสดี @NielsSteenbeek - ทำตามแนวทางของผู้สนับสนุน TypeScript พร้อมด้วยคุณสมบัติและฟิลด์สำรองที่คุณจะต้องเจอกับชื่อที่ขัดแย้งกัน วิธีที่แนะนำคืออะไร
Jordan

อาจจะ: typescript private name: string; getName() { get: { return this.name; } set: { this.name = ???; } }
จอร์แดน

7
สิ่งที่ดีเหล่านั้นแนวทางการเข้ารหัส typescript จะไม่สวย ฉันจะใช้พวกเขาภายใต้การบีบบังคับเท่านั้น (เช่นฉันถูกจ่ายให้ทำ)
โทมัส Eding

14
@NielsSteenbeek: คุณอ่านเอกสารนั้นแล้วหรือยัง "นี่ไม่ใช่แนวทางที่กำหนดไว้สำหรับชุมชน TypeScript"
Jonathan Cast

คำตอบ:


1083

TypeScript ใช้ไวยากรณ์ getter / setter ที่เหมือนกับ ActionScript3

class foo {
    private _bar: boolean = false;
    get bar(): boolean {
        return this._bar;
    }
    set bar(value: boolean) {
        this._bar = value;
    }
}

ที่จะผลิต JavaScript นี้ใช้ ECMAScript 5 Object.defineProperty()คุณลักษณะ

var foo = (function () {
    function foo() {
        this._bar = false;
    }
    Object.defineProperty(foo.prototype, "bar", {
        get: function () {
            return this._bar;
        },
        set: function (value) {
            this._bar = value;
        },
        enumerable: true,
        configurable: true
    });
    return foo;
})();

ดังนั้นที่จะใช้มัน

var myFoo = new foo();
if(myFoo.bar) {         // calls the getter
    myFoo.bar = false;  // calls the setter and passes false
}

อย่างไรก็ตามเพื่อให้สามารถใช้งานได้คุณต้องแน่ใจว่าคอมไพเลอร์ TypeScript มีเป้าหมาย ECMAScript5 หากคุณใช้คอมไพเลอร์บรรทัดคำสั่งให้ใช้--targetแฟล็กเช่นนี้

tsc --target ES5

หากคุณใช้ Visual Studio คุณต้องแก้ไขไฟล์โครงการของคุณเพื่อเพิ่มการตั้งค่าสถานะให้กับการกำหนดค่าสำหรับเครื่องมือสร้าง TypeScriptCompile คุณสามารถดูได้ที่นี่ :

ตามที่ @DanFromGermany แนะนำด้านล่างหากคุณเพียงแค่อ่านและเขียนอสังหาริมทรัพย์ในพื้นที่เช่นfoo.bar = trueนั้นแล้วการมีคู่เซทเทอร์และทะเยอทะยานก็เกินความจริง คุณสามารถเพิ่มพวกเขาได้ในภายหลังหากคุณต้องการทำบางสิ่งเช่นการบันทึกเมื่อใดก็ตามที่คุณสมบัติถูกอ่านหรือเขียน


59
คำตอบที่ดี นอกจากนี้โปรดทราบว่าคุณสมบัตินี้ไม่ได้จำลองเสมือนใน TypeScript (v0.9.5) ซึ่งแตกต่างจากใน C # ในขณะนี้ เมื่อคุณใช้ "get bar ()" ในคลาสที่ได้รับคุณกำลังแทนที่ "get bar ()" ในพาเรนต์ ความหมายรวมถึงการไม่สามารถเรียกคลาสพื้นฐานของ accessor จาก accessor ที่ได้รับ สิ่งนี้เป็นจริงสำหรับคุณสมบัติเท่านั้น - เมธอดจะทำงานตามที่คุณคาดหวัง ดูคำตอบของ SteveFenton ได้ที่นี่: stackoverflow.com/questions/13121431/ …
David Cuccia

14
ฉันสับสนเล็กน้อยเกี่ยวกับขีดล่าง การประชุม typescript บอกว่าจะไม่ใช้ขีดล่างสำหรับตัวแปรส่วนตัว? แต่ในกรณีนี้เราจะต้องใช้ขีด - หรือเราจะได้รับความขัดแย้งระหว่างภาครัฐและเอกชน "บาร์" ซึ่งเป็น
Kokodoko

4
ใช้ขีดล่างเป็นการตั้งค่าส่วนตัวสำหรับคุณสมบัติส่วนตัว อย่างไรก็ตามฉันเชื่อว่าคุณมีสิทธิ์ที่เราต้องการให้ทรัพย์สินมีชื่อแตกต่างจากวิธี getter / setter
Ezward

3
ทำไมคุณใช้myFoo.bar = trueแทนmyFoo.bar(true);หรือmyFoo.setBar(true);?
Daniel W.

6
@DanFromGermany สถานที่ให้บริการคือ "น้ำตาล syntactic" สำหรับคู่ของ "รับ" และวิธีการ "ตั้ง" Microsoft ได้สร้างแนวคิดของคุณสมบัติด้วย Visual Basic และนำไปใช้กับภาษา. NET เช่น C # และ VB.NET ตัวอย่างเช่นดูอสังหาริมทรัพย์ (C # Programming Guide) คุณสมบัติลดความซับซ้อนในการเข้าถึงสถานะของวัตถุและ (ในความคิดของฉัน) กำจัด "noisiness" ของการจัดการกับคู่ของ "get / set" วิธีการ (หรือบางครั้งมีเพียงวิธี "รับ" ที่ต้องการการเปลี่ยนแปลงไม่ได้)
DavidRR

112

Ezward ได้ให้คำตอบที่ดีอยู่แล้ว แต่ฉันสังเกตเห็นว่าหนึ่งในความคิดเห็นถามว่ามันถูกใช้อย่างไร สำหรับคนอย่างฉันที่สะดุดกับคำถามนี้ฉันคิดว่ามันจะมีประโยชน์ที่จะมีลิงก์ไปยังเอกสารอย่างเป็นทางการเกี่ยวกับ getters และ setters บนเว็บไซต์ typescript ตามที่อธิบายได้ดีหวังว่าจะได้รับการปรับปรุงให้ทันสมัยอยู่เสมอเมื่อมีการเปลี่ยนแปลง ทำและแสดงการใช้งานตัวอย่าง:

http://www.typescriptlang.org/docs/handbook/classes.html

โดยเฉพาะอย่างยิ่งสำหรับผู้ที่ไม่คุ้นเคยโปรดทราบว่าคุณไม่ได้รวมคำว่า 'รับ' ไว้ในการโทรไปยังผู้ทะเยอทะยาน (และในทำนองเดียวกันสำหรับผู้ตั้งค่า):

var myBar = myFoo.getBar(); // wrong    
var myBar = myFoo.get('bar');  // wrong

คุณควรทำสิ่งนี้:

var myBar = myFoo.bar;  // correct (get)
myFoo.bar = true;  // correct (set) (false is correct too obviously!)

รับชั้นเรียนเช่น:

class foo {
  private _bar:boolean = false;

  get bar():boolean {
    return this._bar;
  }
  set bar(theBar:boolean) {
    this._bar = theBar;
  }
}

จากนั้นจะเรียกคุณสมบัติ 'บาร์' สำหรับ '_bar' ส่วนตัว


หากฉันต้องการแทนที่วาร์ระดับสาธารณะด้วยคุณสมบัติมันเป็นการแทนที่แบบดรอปดาวน์ที่ฉันสามารถใส่ได้โดยไม่ต้องกังวล กล่าวอีกนัยหนึ่งถ้าฉันถดถอยทดสอบหนึ่ง accessor และหนึ่ง setter ฉันจะถือว่าประสบความสำเร็จหรือไม่ หรือมีกรณีที่จะไม่ทำงานเหมือนกับ var และฉันต้องการทดสอบทั้งหมด 100 แห่งที่ใช้ var / prop นี้หรือไม่
Adam Plocher

ฉันสงสัยว่ามีวิธีแก้ปัญหาสำหรับการใช้ขีดเส้นใต้เพื่อแยกชื่อคุณสมบัติจากเมธอด getter หรือ setter ในหลักสูตรเดียวที่ฉันทำพวกเขากล่าวว่าขีดเส้นใต้ไม่เป็นที่ต้องการ แต่ไม่ได้ให้ทางเลือก
จาม

1
@cham คุณไม่จำเป็นต้องใช้เครื่องหมายขีดล่างที่นี่ ... คุณสามารถเรียกใช้ตัวแปรส่วนบุคคลถ้าคุณต้องการ
Robert McKee

58

นี่คือตัวอย่างการทำงานที่ควรชี้คุณไปในทิศทางที่ถูกต้อง:

class Foo {
    _name;

    get Name() {
        return this._name;
    }

    set Name(val) {
        this._name = val;
    }
}

Getters และ setters ใน JavaScript เป็นเพียงฟังก์ชั่นปกติ setter เป็นฟังก์ชั่นที่รับพารามิเตอร์ซึ่งค่าคือค่าที่ตั้งไว้


30
เพื่อความชัดเจนไม่จำเป็นต้องมีทรัพย์สินผู้ทะเยอทะยานและผู้ตั้งstaticตน
Drew Noakes

1
การอ้างอิงตัวแปรยังคงไม่เปลี่ยนแปลง Foo._nameควรถูกแทนที่ด้วยthis._name
โยฮันเนส

6

คุณสามารถเขียนสิ่งนี้

class Human {
    private firstName : string;
    private lastName : string;

    constructor (
        public FirstName?:string, 
        public LastName?:string) {

    }

    get FirstName() : string {
        console.log("Get FirstName : ", this.firstName);
        return this.firstName;
    }
    set FirstName(value : string) {
        console.log("Set FirstName : ", value);
        this.firstName = value;
    } 

    get LastName() : string {
        console.log("Get LastName : ", this.lastName);
        return this.lastName;
    }
    set LastName(value : string) {
        console.log("Set LastName : ", value);
        this.lastName = value;
    } 

}

2
ทำไมคนในคอนสตรัคท์?
MuriloKunze

17
ใช่ไม่สามารถเป็นสาธารณะในตัวสร้างในรหัสนี้ publicที่นี่กำหนดสมาชิกที่ซ้ำกัน
orad

2
คุณสามารถเขียนได้ แต่จะไม่คอมไพล์
Justin

3

TS เสนอตัวเชื่อมต่อและตัวตั้งค่าซึ่งอนุญาตให้คุณสมบัติของวัตถุมีการควบคุมวิธีเข้าถึง (getter) หรืออัพเดต (ตัวตั้ง) ภายนอกมากขึ้นวัตถุได้มากขึ้น แทนที่จะเข้าถึงหรืออัพเดตคุณสมบัติโดยตรงจะมีการเรียกใช้ฟังก์ชันพร็อกซี

ตัวอย่าง:

class Person {
    constructor(name: string) {
        this._name = name;
    }

    private _name: string;

    get name() {
        return this._name;
    }

    // first checks the length of the name and then updates the name.
    set name(name: string) {
        if (name.length > 10) {
            throw new Error("Name has a max length of 10");
        }

        this._name = name;  
    }

    doStuff () {
        this._name = 'foofooooooofoooo';
    }


}

const person = new Person('Willem');

// doesn't throw error, setter function not called within the object method when this._name is changed
person.doStuff();  

// throws error because setter is called and name is longer than 10 characters
person.name = 'barbarbarbarbarbar';  

1

มันคล้ายกันมากกับการสร้างวิธีการทั่วไปเพียงแค่ใส่คำหลักที่สงวนไว้getหรือsetที่จุดเริ่มต้น

class Name{
    private _name: string;

    getMethod(): string{
        return this._name;
    }

    setMethod(value: string){
        this._name = value
    }

    get getMethod1(): string{
        return this._name;
    }

    set setMethod1(value: string){
        this._name = value
    }
}

class HelloWorld {

    public static main(){

        let test = new Name();

        test.setMethod('test.getMethod() --- need ()');
            console.log(test.getMethod());

        test.setMethod1 = 'test.getMethod1 --- no need (), and used = for set ';
            console.log(test.getMethod1);
    }
}
HelloWorld.main();

ในกรณีนี้คุณสามารถข้ามประเภทกลับมา get getMethod1() {

    get getMethod1() {
        return this._name;
    }

1

ฉันคิดว่าฉันคงเข้าใจว่าทำไมมันถึงสับสน ในตัวอย่างของคุณเราต้องการผู้ได้รับและผู้ตั้ง_nameค่า แต่เราบรรลุว่าด้วยการสร้าง getters และ setters Nameสำหรับตัวแปรระดับที่ไม่เกี่ยวข้องกัน

พิจารณาสิ่งนี้:

class Car{
    private tiresCount = 4;
    get yourCarTiresCount(){
        return this.tiresCount ;
    }
    set yourCarTiresCount(count) {
        alert('You shouldn't change car tire count')
    }
}

โค้ดด้านบนทำตาม:

  1. getและsetสร้าง getter และ setter สำหรับyourCarTiresCount( ไม่ใช่สำหรับtiresCount )

ทะเยอทะยานคือ:

function() {
    return this.tiresCount ;
}

และสุนัขเป็น:

function(count) {
    alert('You shouldn't change car tire count');
}

ความหมายเวลาที่เราทำทุกnew Car().yourCarTiresCount, getter วิ่ง และสำหรับnew Car().yourCarTiresCount('7')setter ทุกตัวที่ทำงาน

  1. อ้อมสร้างทะเยอทะยาน tireCountแต่ไม่ได้ตั้งค่าสำหรับส่วนตัว

0

หากคุณกำลังมองหาวิธีใช้รับและตั้งค่าบนวัตถุใด ๆ (ไม่ใช่คลาส) Proxy อาจเป็นประโยชน์: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy

const target = {
  message1: "hello",
  message2: "everyone"
};

const handler3 = {
  get: function (target, prop, receiver) {
    if (prop === "message2") {
      return "world";
    }
    return Reflect.get(...arguments);
  },
};

const proxy3 = new Proxy(target, handler3);

console.log(proxy3.message1); // hello
console.log(proxy3.message2); // world

หมายเหตุ: โปรดทราบว่านี่ไม่ใช่ api ใหม่ที่ไม่รองรับและจำเป็นต้องใช้ polifill สำหรับเบราว์เซอร์รุ่นเก่า


-6

หากคุณทำงานกับโมดูล TypeScript และกำลังพยายามเพิ่มทะลวงที่ส่งออกคุณสามารถทำสิ่งนี้:

// dataStore.ts
export const myData: string = undefined;  // just for typing support
let _myData: string;  // for memoizing the getter results

Object.defineProperty(this, "myData", {
    get: (): string => {
        if (_myData === undefined) {
            _myData = "my data";  // pretend this took a long time
        }

        return _myData;
    },
});

จากนั้นในไฟล์อื่นคุณมี:

import * as dataStore from "./dataStore"
console.log(dataStore.myData); // "my data"

8
นั่นเป็นคำแนะนำที่แย่มาก โดยเฉพาะอย่างยิ่งthisจะต้องไม่ได้กำหนดที่ขอบเขตระดับบนสุดของโมดูล คุณสามารถใช้exportsแทน แต่คุณไม่ควรทำเลยเพราะรับประกันว่าจะทำให้เกิดปัญหาความเข้ากันได้จริง
Aluan Haddad
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.