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.isExtensiblevalueOf 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