Как использовать javascript Object.defineProperty

Я посмотрел, как использовать метод Object.defineProperty, но не нашел ничего достойного.

Кто-то дал мне этот фрагмент кода:

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

Но я этого не понимаю. В основном, get - это то, что я не могу получить (каламбур). Как это работает?

Ответ 1

Поскольку вы задали аналогичный вопрос, дайте ему шаг за шагом. Это немного дольше, но это может сэкономить вам гораздо больше времени, чем я потратил на это:

Свойство - это функция ООП, предназначенная для чистого разделения кода клиента. Например, в некоторых интернет-магазинах у вас могут быть такие объекты:

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}

Затем в вашем клиентском коде (в интернет-магазине) вы можете добавлять скидки в свои продукты:

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

Позже владелец интернет-магазина может понять, что скидка не может превышать 80%. Теперь вам нужно найти КАЖДОЕ появление модификации скидок в клиентском коде и добавить строку

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

Затем владелец интернет-магазина может дополнительно изменить свою стратегию, например, "если клиент является реселлером, максимальная скидка может составлять 90%". И вам нужно сделать изменения на нескольких местах снова, плюс вам нужно помнить об изменении этих строк в любое время, когда стратегия будет изменена. Это плохой дизайн. Именно поэтому инкапсуляция является основным принципом ООП. Если конструктор был таким:

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

Затем вы можете просто изменить методы getDiscount (accessor) и setDiscount (mutator). Проблема в том, что большинство членов ведут себя как обычные переменные, просто скидка нуждается в особой заботе. Но для хорошего дизайна требуется инкапсуляция каждого элемента данных, чтобы код был расширяемым. Поэтому вам нужно добавить много кода, который ничего не делает. Это также плохой дизайн, шаблонный антипаттерн. Иногда вы не можете просто рефакторировать поля методам позже (код eshop может увеличиться, или какой-либо сторонний код может зависеть от старой версии), поэтому здесь шаблон здесь меньше. Но все же это зло. Именно поэтому свойства были введены на многие языки. Вы можете сохранить исходный код, просто преобразуйте элемент скидки в свойство с блоками get и set:

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

Обратите внимание на последнюю, но одну строку: ответственность за правильное значение скидки была перенесена с кода клиента (определение электронного магазина) на определение продукта. Продукт отвечает за постоянство сохранения своих данных. Хороший дизайн (грубо говоря), если код работает так же, как наши мысли.

Так много о свойствах. Но javascript отличается от чистых объектно-ориентированных языков, таких как С#, и кодирует функции по-разному:

В С# преобразование полей в свойства - это нарушение изменений, поэтому публичные поля должны быть закодированы как Auto-Implemented Properties, если ваш код может использоваться в отдельно скомпилированном клиенте.

В Javascript стандартные свойства (элемент данных с геттером и установщиком, описанным выше) определяются дескриптором доступа (в ссылке, которая у вас есть в вашем вопросе). Исключительно, вы можете использовать дескриптор данных (поэтому вы не можете использовать значение i.e и устанавливать на одно и то же свойство):

  • дескриптор доступа= get + set (см. пример выше)
    • получить должна быть функция; его возвращаемое значение используется при чтении свойства; если не указано, по умолчанию используется undefined, который ведет себя как функция, которая возвращает undefined
    • set должен быть функцией; его параметр заполняется RHS при присвоении значения свойству; если не указано, по умолчанию используется undefined, который ведет себя как пустая функция
  • дескриптор данных= значение + запись (см. пример ниже)
    • значение по умолчанию undefined; если они доступны для записи, конфигурируются и перечислены (см. ниже), это свойство ведет себя как обычное поле данных
    • записываемый - значение по умолчанию false; если это не так, свойство читается только; попытка записи игнорируется без ошибок *!

Оба дескриптора могут иметь следующие элементы:

  • настраиваемый - значение по умолчанию false; Если это не так, свойство не может быть удалено; попытка удаления игнорируется без ошибок *!
  • перечислимый - по умолчанию false; если true, оно будет повторяться в for(var i in theObject); если false, он не будет повторяться, но он все еще доступен как общедоступный

*, если только в строгом режиме - в этом случае JS прекращает выполнение с помощью TypeError, если оно не попадает в блок try-catch

Чтобы прочитать эти настройки, используйте Object.getOwnPropertyDescriptor().

Изучите пример:

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

Если вы не хотите разрешать код клиента такими читами, вы можете ограничить объект тремя уровнями ограничения:

  • Object.preventExtensions(yourObject) предотвращает добавление новых свойств в ваш объект. Используйте Object.isExtensible(<yourObject>), чтобы проверить, был ли метод использован для объекта. Предупреждение неглубоко (см. Ниже).
  • Object.seal(yourObject), как указано выше, и свойства не могут быть удалены (эффективно устанавливает configurable: false ко всем свойствам). Используйте Object.isSealed(<yourObject>) для обнаружения этой функции на объекте. Печать неглубокая (см. Ниже).
  • Object.freeze(yourObject), как указано выше, и свойства не могут быть изменены (эффективно устанавливает writable: false ко всем свойствам с дескриптором данных). Записанное свойство Setter не влияет (поскольку оно не имеет одного). Замораживание неглубоко: это означает, что если свойство Object, его свойства НЕ ЗАМОРОЖЕНЫ (если вы хотите, вы должны выполнить что-то вроде "глубокого замораживания", похожего на глубокую копию - клонирование). Используйте Object.isFrozen(<yourObject>) для его обнаружения.

Вам не нужно беспокоиться об этом, если вы напишете всего несколько строк. Но если вы хотите закодировать игру (как вы упомянули в связанном вопросе), вам действительно нужно заботиться о хорошем дизайне. Попробуйте что-нибудь сказать о антипаттерах и запахах кода. Это поможет вам избежать ситуаций типа "О, мне нужно полностью переписать мой код снова!", Это может сэкономить вам месяцы отчаяния, если вы хотите много кода. Удачи.

Ответ 2

get - это функция, вызываемая при попытке прочитать значение player.health, например:

console.log(player.health);

Эффект не сильно отличается от:

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

Поставлена ​​противоположность get, которая будет использоваться при назначении значения. Поскольку нет сеттера, кажется, что назначение игроку здоровья не предназначено:

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

Очень простой пример:

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

Ответ 3

defineProperty - это метод на объекте, который позволяет настраивать свойства в соответствии с некоторыми критериями. Вот простой пример с объектом сотрудника с двумя свойствами firstName & lastName и добавьте два свойства, переопределив метод toString для объекта.

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

Вы получите вывод в виде: Jameel Moideen

Я собираюсь изменить тот же код с помощью defineProperty на объекте

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

Первый параметр - это имя объекта, а второй параметр - это имя добавляемого нами свойства, в нашем случае его toString, а затем последний параметр - объект json, значение которого будет функцией и тремя параметрами. доступный для записи, перечислимый и настраиваемый. Прямо сейчас я просто объявил все как истину.

Если вы запустите пример, вы получите вывод: Jameel Moideen

Давайте поймем, почему нам нужны три свойства, такие как для записи, перечисления и конфигурирования.

перезаписываемый

Одна из самых раздражающих частей javascript - если вы измените свойство toString на что-то другое, например

enter image description here

если вы запустите это снова, все станет перерывом. Позволяет изменить запись на ложь. Если запустить то же самое снова, вы получите правильный вывод, как ame Jameel Moideen. Это свойство предотвратит перезапись этого свойства позже.

перечислим

если вы напечатаете все ключи внутри объекта, вы сможете увидеть все свойства, включая toString.

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

enter image description here

если вы установите enumerable в false, вы можете скрыть свойство toString от всех остальных. Если запустить это снова, вы получите firstName, lastName

конфигурируемый

если кто-то позже переопределит объект позже, например, перечислим в true и запустим его. Вы можете видеть, что свойство toString пришло снова.

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

enter image description here

Вы можете ограничить это поведение, установив для false значение.

Первоначальная ссылка на эту информацию из моего личного блога

Ответ 4

В принципе, defineProperty - это метод, который принимает 3 параметра - объект, свойство и дескриптор. То, что происходит в этом конкретном вызове, - это свойство "health" объекта player получает назначение на 10 плюс 15 раз выше уровня объекта игрока.

Ответ 5

да нет больше функции, расширяющейся для установщика установки и getter это мой пример Object.defineProperty(obj, name, 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);

Ответ 6

Object.defineProperty() - глобальная функция. Она не доступна внутри функции, которая объявляет объект иначе. Вам придется использовать ее статически...

Ответ 7

Резюме:

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

Object.defineProperty используется для создания нового свойства на объекте игрока. Object.defineProperty - это функция, которая изначально присутствует в среде выполнения JS и принимает следующие аргументы:

Object.defineProperty(obj, prop, descriptor)

  1. объект, для которого мы хотим определить новое свойство
  2. имя нового свойства, которое мы хотим определить
  3. дескриптор объекта

Объект дескриптора - интересная часть. Здесь мы можем определить следующие вещи:

  1. настраиваемый <boolean>: если true дескриптор свойства может быть изменен, а свойство может быть удалено из объекта. Если настраиваемым является false, свойства дескриптора, которые передаются в Object.defineProperty, не могут быть изменены.
  2. Доступно для записи <boolean>: если true, свойство может быть перезаписано с помощью оператора присваивания.
  3. Enumerable <boolean>: если true, свойство может быть повторено в цикле for...in. Также при использовании функции Object.keys будет присутствовать клавиша. Если это свойство false, они не будут повторяться с использованием цикла for..in и не будут отображаться при использовании Object.keys.
  4. get <function>: функция, которая вызывается всякий раз, когда требуется свойство. Вместо прямого значения эта функция вызывается, а возвращаемое значение указывается в качестве значения свойства
  5. .set <function>: функция, которая вызывается всякий раз, когда назначается свойство. Вместо установки прямого значения вызывается эта функция, а возвращаемое значение используется для установки значения свойства.

Пример:

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
}

Ответ 8

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

Ответ 9

Определяет новое свойство непосредственно для объекта или изменяет существующее свойство для объекта и возвращает объект.

Примечание: Вы вызываете этот метод непосредственно в конструкторе Object, а не чем в экземпляре типа Object.

   const object1 = {};
   Object.defineProperty(object1, 'property1', {
      value: 42,
      writable: true,
      enumerable: true,
      configurable: true
   });

enter image description here

Простое объяснение определения свойства.