cara menggunakan Object.defineProperty javascript

183

Saya mencari-cari cara menggunakan Object.definePropertymetode ini, tetapi tidak menemukan sesuatu yang layak.

Seseorang memberi saya potongan kode ini :

Object.defineProperty(player, "health", {
    get: function () {
        return 10 + ( player.level * 15 );
    }
})

Tapi saya tidak mengerti. Terutama, getitulah yang tidak bisa saya dapatkan (pun intended). Bagaimana cara kerjanya?

Chiller matematika
sumber
1
developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… ini adalah tutorial yang sangat baik di sini.
Martian2049

Jawaban:

499

Karena Anda mengajukan pertanyaan serupa , mari selangkah demi selangkah. Ini sedikit lebih lama, tetapi mungkin menghemat lebih banyak waktu daripada yang saya habiskan untuk menulis ini:

Properti adalah fitur OOP yang dirancang untuk pemisahan kode klien secara bersih. Misalnya, di beberapa toko elektronik Anda mungkin memiliki objek seperti ini:

function Product(name,price) {
  this.name = name;
  this.price = price;
  this.discount = 0;
}

var sneakers = new Product("Sneakers",20); // {name:"Sneakers",price:20,discount:0}
var tshirt = new Product("T-shirt",10);  // {name:"T-shirt",price:10,discount:0}

Kemudian dalam kode klien Anda (toko online), Anda dapat menambahkan diskon ke produk Anda:

function badProduct(obj) { obj.discount+= 20; ... }
function generalDiscount(obj) { obj.discount+= 10; ... }
function distributorDiscount(obj) { obj.discount+= 15; ... }

Kemudian, pemilik e-shop mungkin menyadari bahwa diskon tidak boleh lebih besar dari mengatakan 80%. Sekarang Anda perlu menemukan SETIAP kemunculan modifikasi diskon dalam kode klien dan menambahkan baris

if(obj.discount>80) obj.discount = 80;

Kemudian pemilik e-shop lebih lanjut dapat mengubah strateginya, seperti "jika pelanggan adalah reseller, diskon maksimal bisa 90%" . Dan Anda perlu melakukan perubahan di beberapa tempat lagi ditambah Anda harus ingat untuk mengubah garis-garis ini kapan saja strategi diubah. Ini desain yang buruk. Itu sebabnya enkapsulasi adalah prinsip dasar OOP. Jika konstruktornya seperti ini:

function Product(name,price) {
  var _name=name, _price=price, _discount=0;
  this.getName = function() { return _name; }
  this.setName = function(value) { _name = value; }
  this.getPrice = function() { return _price; }
  this.setPrice = function(value) { _price = value; }
  this.getDiscount = function() { return _discount; }
  this.setDiscount = function(value) { _discount = value; } 
}

Kemudian Anda bisa mengubah metode getDiscount( accessor ) dan setDiscount( mutator ). Masalahnya adalah bahwa sebagian besar anggota berperilaku seperti variabel umum, hanya diskon membutuhkan perawatan khusus di sini. Tetapi desain yang baik membutuhkan enkapsulasi dari setiap anggota data untuk menjaga kode dapat diperluas. Jadi, Anda perlu menambahkan banyak kode yang tidak melakukan apa-apa. Ini juga desain yang buruk, antipattern boilerplate . Kadang-kadang Anda tidak bisa hanya refactor bidang ke metode nanti (kode eshop dapat tumbuh besar atau beberapa kode pihak ketiga mungkin tergantung pada versi lama), sehingga boilerplate lebih sedikit jahat di sini. Tapi tetap saja, itu jahat. Itu sebabnya properti diperkenalkan ke banyak bahasa. Anda dapat menyimpan kode asli, cukup mengubah anggota diskon menjadi properti dengangetdan setblok:

function Product(name,price) {
  this.name = name;
  this.price = price;
//this.discount = 0; // <- remove this line and refactor with the code below
  var _discount; // private member
  Object.defineProperty(this,"discount",{
    get: function() { return _discount; },
    set: function(value) { _discount = value; if(_discount>80) _discount = 80; }
  });
}

// the client code
var sneakers = new Product("Sneakers",20);
sneakers.discount = 50; // 50, setter is called
sneakers.discount+= 20; // 70, setter is called
sneakers.discount+= 20; // 80, not 90!
alert(sneakers.discount); // getter is called

Perhatikan baris terakhir tetapi satu: tanggung jawab untuk nilai diskon yang benar dipindahkan dari kode klien (definisi e-shop) ke definisi produk. Produk bertanggung jawab untuk menjaga anggota datanya konsisten. Desain yang bagus adalah (secara kasar dikatakan) jika kode bekerja dengan cara yang sama dengan pikiran kita.

Begitu banyak tentang properti. Tapi javascript berbeda dari bahasa berorientasi objek murni seperti C # dan kode fitur-fiturnya berbeda:

Di C # , mentransformasikan bidang menjadi properti merupakan perubahan besar , jadi bidang publik harus dikodekan sebagai Properti yang Diimplementasikan Otomatis jika kode Anda mungkin digunakan dalam klien yang dikompilasi secara terpisah.

Dalam Javascript , properti standar (anggota data dengan pengambil dan penyetel yang dijelaskan di atas) ditentukan oleh deskriptor accessor (dalam tautan yang Anda miliki dalam pertanyaan Anda). Secara eksklusif, Anda dapat menggunakan deskriptor data (sehingga Anda tidak dapat menggunakan nilai yaitu dan disetel pada properti yang sama):

  • deskriptor accessor = dapatkan + set (lihat contoh di atas)
    • dapatkan harus menjadi fungsi; nilai pengembaliannya digunakan dalam membaca properti; jika tidak ditentukan, defaultnya adalah tidak terdefinisi , yang berperilaku seperti fungsi yang mengembalikan tidak terdefinisi
    • set harus berupa fungsi; parameternya diisi dengan RHS dalam memberikan nilai ke properti; jika tidak ditentukan, standarnya tidak ditentukan , yang berperilaku seperti fungsi kosong
  • deskriptor data = nilai + dapat ditulis (lihat contoh di bawah)
    • nilai default tidak terdefinisi ; jika dapat ditulis , dapat dikonfigurasi ,dan dapat dihitung (lihat di bawah) adalah benar, properti berperilaku seperti bidang data biasa
    • dapat ditulis - default salah ; jika tidak benar , properti hanya dibaca; usaha menulis diabaikan tanpa kesalahan *!

Kedua deskriptor dapat memiliki anggota ini:

  • dapat dikonfigurasi - default salah ; jika tidak benar, properti tidak dapat dihapus; upaya untuk menghapus diabaikan tanpa kesalahan *!
  • enumerable - default false ; jika benar, itu akan diulang difor(var i in theObject); jika salah, itu tidak akan diulangi, tetapi masih dapat diakses sebagai publik

* kecuali dalam mode ketat - dalam hal ini JS menghentikan eksekusi dengan TypeError kecuali jika tertangkap dalam blok try-catch

Untuk membaca pengaturan ini, gunakan Object.getOwnPropertyDescriptor().

Belajar dengan contoh:

var o = {};
Object.defineProperty(o,"test",{
  value: "a",
  configurable: true
});
console.log(Object.getOwnPropertyDescriptor(o,"test")); // check the settings    

for(var i in o) console.log(o[i]); // nothing, o.test is not enumerable
console.log(o.test); // "a"
o.test = "b"; // o.test is still "a", (is not writable, no error)
delete(o.test); // bye bye, o.test (was configurable)
o.test = "b"; // o.test is "b"
for(var i in o) console.log(o[i]); // "b", default fields are enumerable

Jika Anda tidak ingin mengizinkan kode klien menipu seperti itu, Anda dapat membatasi objek dengan tiga tingkat kurungan:

  • Object.preventExtensions (yourObject) mencegah properti baru ditambahkan ke yourObject . GunakanObject.isExtensible(<yourObject>)untuk memeriksa apakah metode itu digunakan pada objek. Pencegahannya dangkal (baca di bawah).
  • Object.seal (yourObject) sama seperti di atas dan properti tidak dapat dihapus (secara efektif disetelconfigurable: false ke semua properti). GunakanObject.isSealed(<yourObject>)untuk mendeteksi fitur ini pada objek. Segel itu dangkal (baca di bawah).
  • Object.freeze (yourObject) sama seperti di atas dan properti tidak dapat diubah (secara efektif disetelwritable: false ke semua properti dengan deskriptor data). Properti Setter yang dapat ditulisi tidak terpengaruh (karena tidak memilikinya). Pembekuan itu dangkal : itu berarti bahwa jika properti adalah Obyek, propertinya TIDAK beku (jika Anda mau, Anda harus melakukan sesuatu seperti "deep freeze", mirip dengan kloning penyalinan dalam ). GunakanObject.isFrozen(<yourObject>)untuk mendeteksinya.

Anda tidak perlu repot dengan ini jika Anda menulis hanya beberapa baris yang menyenangkan. Tetapi jika Anda ingin membuat kode permainan (seperti yang Anda sebutkan dalam pertanyaan terkait), Anda harus benar-benar peduli dengan desain yang bagus. Coba Google sesuatu tentang antipatterns dan kode bau . Ini akan membantu Anda menghindari situasi seperti "Oh, saya harus menulis ulang kode saya sepenuhnya!" , itu bisa menyelamatkanmu dari keputusasaan selama berbulan-bulan jika ingin banyak kode. Semoga berhasil.

Jan Turoň
sumber
Bagian ini jelas. function Product(name,price) { this.name = name; this.price = price; var _discount; // private member Object.defineProperty(this,"discount",{ get: function() { return _discount; }, set: function(value) { _discount = value; if(_discount>80) _discount = 80; } }); } var sneakers = new Product("Sneakers",20); sneakers.discount = 50; // 50, setter is called sneakers.discount+= 20; // 70, setter is called sneakers.discount+= 20; // 80, not 90! alert(sneakers.discount); // getter is called
abu abu
27

getadalah fungsi yang dipanggil saat Anda mencoba membaca nilai player.health, seperti di:

console.log(player.health);

Secara efektif tidak jauh berbeda dari:

player.getHealth = function(){
  return 10 + this.level*15;
}
console.log(player.getHealth());

Kebalikan dari get diatur, yang akan digunakan saat Anda menetapkan nilai. Karena tidak ada setter, tampaknya menetapkan untuk kesehatan pemain tidak dimaksudkan:

player.health = 5; // Doesn't do anything, since there is no set function defined

Contoh yang sangat sederhana:

var player = {
  level: 5
};

Object.defineProperty(player, "health", {
  get: function() {
    return 10 + (player.level * 15);
  }
});

console.log(player.health); // 85
player.level++;
console.log(player.health); // 100

player.health = 5; // Does nothing
console.log(player.health); // 100

Paul
sumber
itu seperti fungsi yang sebenarnya tidak perlu Anda gunakan ()untuk menelepon ... Saya tidak mengerti apa idenya ketika mereka menemukan benda ini. Fungsinya benar-benar sama: jsbin.com/bugipi/edit?js,console,output
vsync
15

defineProperty adalah metode pada Object yang memungkinkan Anda mengkonfigurasi properti untuk memenuhi beberapa kriteria. Berikut adalah contoh sederhana dengan objek karyawan dengan dua properti firstName & lastName dan menambahkan dua properti dengan menimpa metode toString pada objek.

var employee = {
    firstName: "Jameel",
    lastName: "Moideen"
};
employee.toString=function () {
    return this.firstName + " " + this.lastName;
};
console.log(employee.toString());

Anda akan mendapatkan Output sebagai: Jameel Moideen

Saya akan mengubah kode yang sama dengan menggunakan defineProperty pada objek

var employee = {
    firstName: "Jameel",
    lastName: "Moideen"
};
Object.defineProperty(employee, 'toString', {
    value: function () {
        return this.firstName + " " + this.lastName;
    },
    writable: true,
    enumerable: true,
    configurable: true
});
console.log(employee.toString());

Parameter pertama adalah nama objek dan kemudian parameter kedua adalah nama properti yang kita tambahkan, dalam kasus ini adalah toString dan kemudian parameter terakhir adalah objek json yang memiliki nilai akan menjadi fungsi dan tiga parameter dapat ditulis, dapat dihitung dan dapat dikonfigurasi. Sekarang, saya hanya menyatakan semuanya benar.

Jika Anda menjalankan contoh, Anda akan mendapatkan Output sebagai: Jameel Moideen

Mari kita mengerti mengapa kita membutuhkan tiga properti seperti dapat ditulis, enumerable dan dapat dikonfigurasi.

dapat ditulis

Salah satu bagian yang sangat menjengkelkan dari javascript adalah, jika Anda mengubah properti toString ke sesuatu yang lain misalnya

masukkan deskripsi gambar di sini

jika Anda menjalankan ini lagi, semuanya akan rusak. Mari kita ubah tulisan menjadi false. Jika menjalankan hal yang sama lagi, Anda akan mendapatkan output yang benar sebagai 'Jameel Moideen'. Properti ini akan mencegah menimpa properti ini nanti.

tak terhitung

jika Anda mencetak semua kunci di dalam objek, Anda dapat melihat semua properti termasuk toString.

console.log(Object.keys(employee));

masukkan deskripsi gambar di sini

jika Anda menetapkan enumerable menjadi false, Anda dapat menyembunyikan properti toString dari orang lain. Jika menjalankan ini lagi Anda akan mendapatkan firstName, lastName

dapat dikonfigurasi

jika seseorang kemudian mendefinisikan ulang objek di kemudian hari misalnya dihitung untuk true dan menjalankannya. Anda dapat melihat properti toString datang lagi.

var employee = {
    firstName: "Jameel",
    lastName: "Moideen"
};
Object.defineProperty(employee, 'toString', {
    value: function () {
        return this.firstName + " " + this.lastName;
    },
    writable: false,
    enumerable: false,
    configurable: true
});

//change enumerable to false
Object.defineProperty(employee, 'toString', {

    enumerable: true
});
employee.toString="changed";
console.log(Object.keys(employee));

masukkan deskripsi gambar di sini

Anda dapat membatasi perilaku ini dengan mengatur yang dapat dikonfigurasi ke false.

Referensi asli informasi ini dari Blog pribadi saya

Kode-EZ
sumber
1
Saya mengetahui bahwa Anda memiliki ini di blog Anda dan hanya menempelkannya di sini, tetapi setidaknya tahu ini untuk masa depan: screencaps tidak populer di SO. Anda tidak dapat menyalin kode kasta untuk mencobanya dan kodenya tidak akan terlihat oleh mesin pencari atau teknologi bantuan.
Domino
@JacqueGoupil Anda benar. Saya akan memperbarui dengan menambahkan kode alih-alih tangkapan layar
Code-EZ
3

Pada dasarnya, definePropertyadalah metode yang mengambil 3 parameter - objek, properti, dan deskriptor. Apa yang terjadi dalam panggilan khusus ini adalah "health"properti playerobjek mendapatkan 10 ditambah 15 kali level objek pemain.

Cole Pilegard
sumber
0

ya tidak ada lagi fungsi yang diperluas untuk setup setter & pengambil ini contoh saya Object.defineProperty (obj, nama, func)

var obj = {};
['data', 'name'].forEach(function(name) {
    Object.defineProperty(obj, name, {
        get : function() {
            return 'setter & getter';
        }
    });
});


console.log(obj.data);
console.log(obj.name);
Faizal Pribadi
sumber
0

Object.defineProperty () adalah fungsi global..Tidak tersedia di dalam fungsi yang menyatakan objek sebaliknya. Anda harus menggunakannya secara statis ...

ISONecroMAn
sumber
0

Ringkasan:

Object.defineProperty(player, "health", {
    get: function () {
        return 10 + ( player.level * 15 );
    }
});

Object.definePropertydigunakan untuk membuat properti baru pada objek pemain. Object.definePropertyadalah fungsi yang secara native hadir dalam lingkungan runtime JS dan mengambil argumen berikut:

Object.defineProperty(obj, prop, descriptor)

  1. The objek yang kita ingin mendefinisikan properti baru
  2. The nama properti baru kita ingin mendefinisikan
  3. objek deskriptor

Objek deskriptor adalah bagian yang menarik. Di sini kita dapat mendefinisikan hal-hal berikut:

  1. dapat dikonfigurasi <boolean> : Jika true deskriptor properti dapat diubah dan properti dapat dihapus dari objek. Jika dapat dikonfigurasi false, properti deskriptor yang diteruskan Object.definePropertytidak dapat diubah.
  2. Tertulis <boolean> : Jika trueproperti dapat ditimpa menggunakan operator penugasan.
  3. Enumerable <boolean> : Jika true properti dapat for...indiulang dalam satu lingkaran. Juga saat menggunakan Object.keysfungsi tombol akan hadir. Jika properti itu falsemereka tidak akan for..indiulang menggunakan loop dan tidak muncul saat menggunakan Object.keys.
  4. get <function> : Suatu fungsi yang dipanggil setiap kali dibutuhkan properti. Alih-alih memberikan nilai langsung, fungsi ini disebut dan nilai yang dikembalikan diberikan sebagai nilai properti
  5. set <function> : Suatu fungsi yang dipanggil setiap kali properti ditugaskan. Alih-alih mengatur nilai langsung, fungsi ini dipanggil dan nilai yang dikembalikan digunakan untuk mengatur nilai properti.

Contoh:

const player = {
  level: 10
};

Object.defineProperty(player, "health", {
  configurable: true,
  enumerable: false,
  get: function() {
    console.log('Inside the get function');
    return 10 + (player.level * 15);
  }
});

console.log(player.health);
// the get function is called and the return value is returned as a value

for (let prop in player) {
  console.log(prop);
  // only prop is logged here, health is not logged because is not an iterable property.
  // This is because we set the enumerable to false when defining the property
}

Willem van der Veen
sumber
0

import { CSSProperties } from 'react'
import { BLACK, BLUE, GREY_DARK, WHITE } from '../colours'

export const COLOR_ACCENT = BLUE
export const COLOR_DEFAULT = BLACK
export const FAMILY = "'Segoe UI', sans-serif"
export const SIZE_LARGE = '26px'
export const SIZE_MEDIUM = '20px'
export const WEIGHT = 400

type Font = {
  color: string,
  size: string,
  accent: Font,
  default: Font,
  light: Font,
  neutral: Font,
  xsmall: Font,
  small: Font,
  medium: Font,
  large: Font,
  xlarge: Font,
  xxlarge: Font
} & (() => CSSProperties)

function font (this: Font): CSSProperties {
  const css = {
    color: this.color,
    fontFamily: FAMILY,
    fontSize: this.size,
    fontWeight: WEIGHT
  }
  delete this.color
  delete this.size
  return css
}

const dp = (type: 'color' | 'size', name: string, value: string) => {
  Object.defineProperty(font, name, { get () {
    this[type] = value
    return this
  }})
}

dp('color', 'accent', COLOR_ACCENT)
dp('color', 'default', COLOR_DEFAULT)
dp('color', 'light', COLOR_LIGHT)
dp('color', 'neutral', COLOR_NEUTRAL)
dp('size', 'xsmall', SIZE_XSMALL)
dp('size', 'small', SIZE_SMALL)
dp('size', 'medium', SIZE_MEDIUM)

export default font as Font

Alvin Smith
sumber
0

Mendefinisikan properti baru secara langsung pada objek, atau memodifikasi properti yang ada pada objek, dan mengembalikan objek.

Catatan: Anda memanggil metode ini secara langsung pada konstruktor Objek daripada pada instance dari tipe Object.

   const object1 = {};
   Object.defineProperty(object1, 'property1', {
      value: 42,
      writable: false, //If its false can't modify value using equal symbol
      enumerable: false, // If its false can't able to get value in Object.keys and for in loop
      configurable: false //if its false, can't able to modify value using defineproperty while writable in false
   });

masukkan deskripsi gambar di sini

Penjelasan sederhana tentang define Property.

Kode contoh: https://jsfiddle.net/manoj_antony32/pu5n61fs/

Mano
sumber
0

Object.defineProperty(Array.prototype, "last", {
  get: function() {
    if (this[this.length -1] == undefined) { return [] }
    else { return this[this.length -1] }
  }
});

console.log([1,2,3,4].last) //returns 4

Jaeyson Anthony Y.
sumber