Proxy
and Symbol
are related to the ECMAScript 6 specification, are partially implemented and implemented only in some of the modern browsers. /** * @param {string} prefix * @constructor */ function Product(prefix) { /** * @private * @type {string} */ this.prefix_ = prefix; /** * @private * @type {string} */ this.type_ = ""; } /** * @param {string} newType */ Product.prototype.setType = function (newType) { this.type_ = newType; }; /** * @return {string} */ Product.prototype.type = function () { return this.prefix_ + ": " + this.type_; } var product = new Product("fruit"); product.setType("apple"); console.log(product.type()); //logs fruit: apple
/** * @param {string} prefix * @constructor */ function Product(prefix) { /** * @private * @type {number} */ this.prefix_ = prefix; /** * @private * @type {string} */ this.type_ = ""; } /** * @param {string} newType */ Product.prototype = { /** * @return {string} */ get type () { return this.prefix_ + ": " + this.type_; }, /** * @param {string} */ set type (newType) { this.type_ = newType; } }; var product = new Product("fruit"); product.type = "apple"; console.log(product.type); //logs "fruit: apple" console.log(product.type = "orange"); //logs "orange" console.log(product.type); //logs "fruit: orange"
get
and set
become more pronounced during their direct use. I found for myself that: product.type = "apple"; console.log(product.type);
product.setType("apple"); console.log(product.type());
console.log(product.type = "orange"); //logs "orange" console.log(product.type); //logs "fruit: orange"
“orange”
is output to the console and only then “fruit: orange”
. The getter is not executed while the set value is being returned, so with this form of abbreviated notation you may stumble into trouble. Returns using set
are ignored. Add return this.type;
to set
does not solve this problem. This is usually solved by reusing the set value, but there may be problems with the property that has a getter.get propertyname ()
works with object literals and in the previous example I assigned a literal to the Product.prototype
object. There is nothing wrong with that, but using literals like this complicates the prototype call chain to implement inheritance. It is possible to define getters and setters in the prototype without using literals - using defineProperty
/** * @param {string} prefix * @constructor */ function Product(prefix) { /** * @private * @type {number} */ this.prefix_ = prefix; /** * @private * @type {string} */ this.type_ = ""; } /** * @param {string} newType */ Object.defineProperty(Product.prototype, "type", { /** * @return {string} */ get: function () { return this.prefix_ + ": " + this.type_; }, /** * @param {string} */ set: function (newType) { this.type_ = newType; } });
defineProperty
. The third argument in defineProperty
is the handle and, in addition to set
and get
it gives you the opportunity to customize accessibility and set the value. With defineProperty
you can create something like a constant — a property that will never be deleted or redefined. var obj = { foo: "bar", }; //A normal object property console.log(obj.foo); //logs "bar" obj.foo = "foobar"; console.log(obj.foo); //logs "foobar" delete obj.foo; console.log(obj.foo); //logs undefined Object.defineProperty(obj, "foo", { value: "bar", }); console.log(obj.foo); //logs "bar", we were able to modify foo obj.foo = "foobar"; console.log(obj.foo); //logs "bar", write failed silently delete obj.foo; console.log(obj.foo); //logs bar, delete failed silently
bar foobar undefined bar bar bar
foo.bar
in the example failed (even if they were not interrupted by an error message), because this defineProperty
behavior is to prohibit changes. To change this behavior, you can use the configurable
and writable
. If you use strict mode, errors will be thrown, as they are common JavaScript errors. var obj = {}; Object.defineProperty(obj, "foo", { value: "bar", configurable: true, writable: true, }); console.log(obj.foo); //logs "bar" obj.foo = "foobar"; console.log(obj.foo); //logs "foobar" delete obj.foo; console.log(obj.foo); //logs undefined
configurable
key prevents the property from being deleted from the object. In addition, it makes it possible to prevent the subsequent change of a property with another call to defineProperty
. The writable
key allows you to write to a property or change its value.configurable
set to false
(as is the default), attempts to call defineProperty
a second time will cause an error to be thrown. var obj = {}; Object.defineProperty(obj, "foo", { value: "bar", }); Object.defineProperty(obj, "foo", { value: "foobar", }); // Uncaught TypeError: Cannot redefine property: foo
configurable
set to true
, then you can change the property in the future. This can be used to change the value of a recordable property. var obj = {}; Object.defineProperty(obj, "foo", { value: "bar", configurable: true, }); obj.foo = "foobar"; console.log(obj.foo); // logs "bar", write failed Object.defineProperty(obj, "foo", { value: "foobar", configurable: true, }); console.log(obj.foo); // logs "foobar"
defineProperty
not iterated in a for in
loop. var i, inventory; inventory = { "apples": 10, "oranges": 13, }; Object.defineProperty(inventory, "strawberries", { value: 3, }); for (i in inventory) { console.log(i, inventory[i]); }
apples 10 oranges 13
enumerable
property enumerable
var i, inventory; inventory = { "apples": 10, "oranges": 13, }; Object.defineProperty(inventory, "strawberries", { value: 3, enumerable: true, }); for (i in inventory) { console.log(i, inventory[i]); }
apples 10 oranges 13 strawberries 3
isPropertyEnumerable
to check whether a property appears in a for in
isPropertyEnumerable
var i, inventory; inventory = { "apples": 10, "oranges": 13, }; Object.defineProperty(inventory, "strawberries", { value: 3, }); console.log(inventory.propertyIsEnumerable("apples")); //console logs true console.log(inventory.propertyIsEnumerable("strawberries")); //console logs false
propertyIsEnumerable
will also return false
for properties defined above in a chain of prototypes, or for properties not defined in any other way for this object, which, however, is obvious.defineProperty
: it will be an error to combine the set
and get
access methods with writable: true
or combine them with value
. Defining a property with a number will result in the number to a string, as it would in any other circumstances. You can also use defineProperty
to define value
as a function.defineProperties
. This method allows you to define several properties at once. I caught the eye of jsperf, comparing the use of defineProperties
with defineProperty
and, at least in Chrome, there was not much difference in which of the methods to use. var foo = {} Object.defineProperties(foo, { bar: { value: "foo", writable: true, }, foo: { value: function() { console.log(this.bar); } }, }); foo.bar = "foobar"; foo.foo(); //logs "foobar"
Object.create
is an alternative to new
, which Object.create
it possible to create an object with a specific prototype. This function takes two arguments: the first is the prototype from which you want to create an object, and the second is the same descriptor that is used when you call Object.defineProperties
var prototypeDef = { protoBar: "protoBar", protoLog: function () { console.log(this.protoBar); } }; var propertiesDef = { instanceBar: { value: "instanceBar" }, instanceLog: { value: function () { console.log(this.instanceBar); } } } var foo = Object.create(prototypeDef, propertiesDef); foo.protoLog(); //logs "protoBar" foo.instanceLog(); //logs "instanceBar"
var prototypeDef = { bar: "protoBar", }; var propertiesDef = { bar: { value: "instanceBar", }, log: { value: function () { console.log(this.bar); } } } var foo = Object.create(prototypeDef, propertiesDef); foo.log(); //logs "instanceBar"
Array
or Object
as the values ​​of the defined properties may be an error, since these properties will be shared with all created instances. var prototypeDef = { protoArray: [], }; var propertiesDef = { propertyArray: { value: [], } } var foo = Object.create(prototypeDef, propertiesDef); var bar = Object.create(prototypeDef, propertiesDef); foo.protoArray.push("foobar"); console.log(bar.protoArray); //logs ["foobar"] foo.propertyArray.push("foobar"); console.log(bar.propertyArray); //also logs ["foobar"]
propertyArray
with a value of null
, then adding the required array, or doing something hipster, for example, using a getter: var prototypeDef = { protoArray: [], }; var propertiesDef = { propertyArrayValue_: { value: null, writable: true }, propertyArray: { get: function () { if (!this.propertyArrayValue_) { this.propertyArrayValue_ = []; } return this.propertyArrayValue_; } } } var foo = Object.create(prototypeDef, propertiesDef); var bar = Object.create(prototypeDef, propertiesDef); foo.protoArray.push("foobar"); console.log(bar.protoArray); //logs ["foobar"] foo.propertyArray.push("foobar"); console.log(bar.propertyArray); //logs []
Object.create
descriptor Object.create
executed at the time the handle is defined. This is the reason why arrays became common to all instances of a class. I also recommend never counting on a fixed order when several properties are defined together. If it is really necessary - to define one property before others - it is better to use Object.defineProperty
for it in this case.Object.create
does not call a constructor function, you cannot use instanceof
to verify the identity of objects. Instead, you can use isPrototypeOf
, which checks against the object's prototype
property. This will be MyFunction.prototype in the case of the constructor, or the object passed as the first argument in Object.create
function Foo() { } var prototypeDef = { protoArray: [], }; var propertiesDef = { propertyArrayValue_: { value: null, writable: true }, propertyArray: { get: function () { if (!this.propertyArrayValue_) { this.propertyArrayValue_ = []; } return this.propertyArrayValue_; } } } var foo1 = new Foo(); //old way using instanceof works with constructors console.log(foo1 instanceof Foo); //logs true //You check against the prototype object, not the constructor function console.log(Foo.prototype.isPrototypeOf(foo1)); //true var foo2 = Object.create(prototypeDef, propertiesDef); //can't use instanceof with Object.create, test against prototype object... //...given as first agument to Object.create console.log(prototypeDef.isPrototypeOf(foo2)); //true
isPrototypeOf
goes down the prototype chain and returns true
if any of them corresponds to the object with which the comparison is made. var foo1Proto = { foo: "foo", }; var foo2Proto = Object.create(foo1Proto); foo2Proto.bar = "bar"; var foo = Object.create(foo2Proto); console.log(foo.foo, foo.bar); //logs "foo bar" console.log(foo1Proto.isPrototypeOf(foo)); // logs true console.log(foo2Proto.isPrototypeOf(foo)); // logs true
defineProperty
, it is possible to limit the changes to the object as a whole. Object.preventExtensions
, Object.seal
and Object.freeze
- each of these methods imposes more stringent restrictions on changes to the object. In strict mode, violation of the restrictions imposed by these methods will lead to an error being thrown, otherwise errors will occur, but “silently”.Object.preventExtensions
method prevents the addition of new properties to an object. It does not hurt either to change the properties that are open for writing, or to remove those that are customizable. In addition, Object.preventExtensions
also does not Object.defineProperty
use of the Object.defineProperty
call to modify existing properties. var obj = { foo: "foo", }; obj.bar = "bar"; console.log(obj); // logs Object {foo: "foo", bar: "bar"} Object.preventExtensions(obj); delete obj.bar; console.log(obj); // logs Object {foo: "foo"} obj.bar = "bar"; console.log(obj); // still logs Object {foo: "foo"} obj.foo = "foobar" console.log(obj); // logs {foo: "foobar"} can still change values
Object.seal
goes further. than Object.preventExtensions
. In addition to prohibiting the addition of new properties to an object, this method also limits the ability to further customize and delete existing properties. Once the object has been “sealed”, you can no longer modify existing properties with defineProperty
. As mentioned above, violation of these prohibitions in strict mode will result in an error being thrown. "use strict"; var obj = {}; Object.defineProperty(obj, "foo", { value: "foo" }); Object.seal(obj); //Uncaught TypeError: Cannot redefine property: foo Object.defineProperty(obj, "foo", { value: "bar" });
"use strict"; var obj = {}; Object.defineProperty(obj, "foo", { value: "foo", writable: true, configurable: true, }); Object.seal(obj); console.log(obj.foo); //logs "foo" obj.foo = "bar"; console.log(obj.foo); //logs "bar" delete obj.foo; //TypeError, cannot delete
Object.freeze
makes the object completely protected from changes. You cannot add, delete or change the property values ​​of a frozen "object". Also, there is no possibility to use Object.defineProperty
to change the values ​​of existing properties of an object. "use strict"; var obj = { foo: "foo1" }; Object.freeze(obj); //All of the following will fail, and result in errors in strict mode obj.foo = "foo2"; //cannot change values obj.bar = "bar"; //cannot add a property delete obj.bar; //cannot delete a property //cannot call defineProperty on a frozen object Object.defineProperty(obj, "foo", { value: "foo2" });
Object.isFrozen
, Object.isSealed
and Object.isExtensible
valueOf
and toString
to customize the behavior of an object in context when JavaScript is expecting a primitive value.toString
: function Foo (stuff) { this.stuff = stuff; } Foo.prototype.toString = function () { return this.stuff; } var f = new Foo("foo"); console.log(f + "bar"); //logs "foobar"
valueOf
: function Foo (stuff) { this.stuff = stuff; } Foo.prototype.valueOf = function () { return this.stuff.length; } var f = new Foo("foo"); console.log(1 + f); //logs 4 (length of "foo" + 1);
function Foo (stuff) { this.stuff = stuff; } Foo.prototype.valueOf = function () { return this.stuff.length; } Foo.prototype.toString = function () { return this.stuff; } var f = new Foo("foo"); console.log(f + "bar"); //logs "3bar" instead of "foobar" console.log(1 + f); //logs 4 (length of "foo" + 1);
toString
is to make the object hashed: function Foo (stuff) { this.stuff = stuff; } Foo.prototype.toString = function () { return this.stuff; } var f = new Foo("foo"); var obj = {}; obj[f] = true; console.log(obj); //logs {foo: true}
Object.getOwnPropertyNames
. If you are familiar with python, then it is, in general, similar to the keys
method of the dictionary, although the Object.keys
method also exists. The main difference between Object.keys
and Object.getOwnPropertyNames
is that the latter also returns “non-enumerable” properties, those that will not be taken into account when running the for in
loop. var obj = { foo: "foo", }; Object.defineProperty(obj, "bar", { value: "bar" }); console.log(Object.getOwnPropertyNames(obj)); //logs ["foo", "bar"] console.log(Object.keys(obj)); //logs ["foo"]
Symbol
is a special new primitive defined in ECMAScrpt 6 harmony, and it will be available in the next iteration of JavaScript. You can already try it in Chrome Canary and Firefox Nightly and the following examples on jsfiddle will work only in these browsers, at least for the time of writing this post, in August 2014.Symbol
can be used as a way to create and reference object properties. var obj = {}; var foo = Symbol("foo"); obj[foo] = "foobar"; console.log(obj[foo]); //logs "foobar"
Symbol
is unique and unchanged. //console logs false, symbols are unique: console.log(Symbol("foo") === Symbol("foo"));
Symbol
can be used with Object.defineProperty
: var obj = {}; var foo = Symbol("foo"); Object.defineProperty(obj, foo, { value: "foobar", }); console.log(obj[foo]); //logs "foobar"
Symbol
will not be iterated in a for in
loop, but the hasOwnProperty
call will work fine: var obj = {}; var foo = Symbol("foo"); Object.defineProperty(obj, foo, { value: "foobar", }); console.log(obj.hasOwnProperty(foo)); //logs true
Symbol
will not fall into an array returned by the Object.getOwnPropertyNames
function, but there is an Object. getOwnPropertySymbols
method Object. getOwnPropertySymbols
Object. getOwnPropertySymbols
var obj = {}; var foo = Symbol("foo"); Object.defineProperty(obj, foo, { value: "foobar", }); //console logs [] console.log(Object.getOwnPropertyNames(obj)); //console logs [Symbol(foo)] console.log(Object.getOwnPropertySymbols(obj));
Symbol
can be handy if you want to not only protect a property from accidental changes, but you don’t even want to show it in the course of normal work. I have not thought seriously about all the potentialities yet, but I think that there could be more of them.Proxy
. As of August 2014, proxies only work in Firefox. The following example with jsfiddle will only work in Firefox and, in fact, I tested it in Firefox beta, which I had installed. var obj = { foo: "foo", }; var handler = { get: function (target, name) { if (target.hasOwnProperty(name)) { return target[name]; } return "foobar"; }, }; var p = new Proxy(obj, handler); console.log(p.foo); //logs "foo" console.log(p.bar); //logs "foobar" console.log(p.asdf); //logs "foobar"
obj
. We create a handler
object that will handle the interaction with the created object. The get
method is pretty simple. It takes an object and the name of a property to access. This information can be returned at any time, but in our example the actual value is returned if the key is and “foobar” if it is not. I see a huge field of possibilities and interesting ways to use proxies, one of which is a bit like a switch
, such as in Scala
.get
there are also other handlers: set
, has
, others. When the proxy gets better support, I will not hesitate to give them a whole post on my blog. I advise you to look at the MDN proxy documentation and pay attention to the examples given.Source: https://habr.com/ru/post/241465/
All Articles