One boy wrote everything in JavaScript, and the client and the server said what he liked, convenient, readable. Then they took him to Durkee, of course.
- From the Internet
setTimeout , belong to web standards. The console is not standardized at all - you have to rely on the de facto standard.Array#forEach, Function#bind, Object.create and consoles, and there is, for example, Node.js, on which many are already using the possibilities of the future ES6.noConflict method is often present, which rolls back changes, the possibility of conflicts with other libraries is minimal, better than the other methods mentioned here as a safe dependency for other libraries.Function#bind and several more, ignoring most of the standards. As for conflicts, everything is very bad here. Such libraries often extend native objects with methods with the same name but different signatures. In order to avoid conflicts, when developing the final application you should not use more than one library that extends native objects, not counting polyfiles, and when writing a library, such dependencies are generally unacceptable. console.log(Array.from(new Set([1, 2, 3, 2, 1]))); // => [1, 2, 3] console.log('*'.repeat(10)); // => '**********' Promise.resolve(32).then(console.log); // => 32 setImmediate(console.log, 42); // => 42 core object or to the modular system. For example, the Promise constructor is available as core.Promise , and the Array.from method as core.Array.from . Methods designed to add existing constructors to the prototype, rather than those added by the library, become static, for example, core.String.repeat is a static version of the String.prototype.repeat method. var log = core.console.log; log(core.Array.from(new core.Set([1, 2, 3, 2, 1]))); // => [1, 2, 3] log(core.String.repeat('*', 10)); // => '**********' core.Promise.resolve(32).then(log); // => 32 core.setImmediate(log, 42); // => 42 npm i core-js // : require('core-js'); // : var core = require('core-js/library'); // , : require('core-js/shim'); npm i -g grunt-cli npm i core-js cd node_modules/core-js && npm i grunt build:date,console,library --path=custom uglify custom.js , custom.min.js weighing 4.8kb and custom.min.map . The library flag indicates a build without extending native objects. You can see which module the required functionality belongs to here (last column).-1 if the value is not found. [1, 2, 3, 2, 1].indexOf(2); // => 1 [1, 2, 3, 2, 1].lastIndexOf(2); // => 3 [1, 2, 3].forEach(function(val, key){ console.log(val); // => 1, 2, 3 console.log(key); // => 0, 1, 2 }); [1, 2, 3].map(function(it){ return it * it; }); // => [1, 4, 9] [1, 2, 3].filter(function(it){ return it % 2; }); // => [1, 3] function isNum(it){ return typeof it == 'number'; } [1, '2', 3].every(isNum); // => false [1, '2', 3].some(isNum); // => true function add(a, b){ return a + b; } [1, 2, 3].reduce(add); // => 6 [1, 2, 3].reduceRight(add, ''); // => '321' 'string'[2] // => 'r' , and in old IE they are not. In the case of applying these methods in the context of strings, we cast the strings to an array. To solve this problem, if necessary, we replace Array#slice and Array#join in old IE. Array.prototype.map.call('123', function(it){ return it * it; }); // => [1, 4, 9] Array.prototype.slice.call('qwe', 1); // => ['w', 'e'] Array.prototype.join.call('qwe', '|'); // => 'q|w|e' for-in loop . This is not only slow, but also compelling, if IE8- support is needed, to check whether the key is proper - otherwise, bypass not only the array elements, but also the methods of its prototype :) Array.isArray([1, 2, 3]); // => true Array.isArray(Object.create(Array.prototype)); // => false null , you can create an object without a prototype, which cannot be done on the basis of ECMAScript 3. It is necessary to use fierce thrash based on iframe . Why this will be revealed to us in the second part . Optionally accepts an object # of descriptors , similarly to Object.defineProperties . function Parent(/*...*/){ /*...*/ } Parent.prototype = {constructor: Parent /*, ... */} function Child(/*...*/){ Parent.call(this /*, ...*/); // ... } // ES3 ( inherit extend'): function Tmp(){} Tmp.prototype = Parent.prototype; Child.prototype = new Tmp; Child.prototype.constructor = Child; // ES5: Child.prototype = Object.create(Parent.prototype, {constructor: {value: Child}}); var dict = Object.create(null); dict.key = 42; console.log(dict instanceof Object); // => false console.log(dict.toString) // => undefined console.log(dict.key) // => 42 constructor property, the constructor will probably be the prototype. For objects created through Object.create , add the # symbol containing the prototype, and we will ignore it when receiving keys . But on the instance of the constructor, the prototype of which was redefined without specifying the “correct” property of the constructor , Object.getPrototypeOf will not work correctly. var parent = {foo: 'bar'} , child = Object.create(parent); console.log(Object.getPrototypeOf(child) === parent); // => true function F(){} console.log(Object.getPrototypeOf(new F) === F.prototype); // => true F.prototype = {constructor: F /*, ...*/}; console.log(Object.getPrototypeOf(new F) === F.prototype); // => true F.prototype = {}; console.log(Object.getPrototypeOf(new F) === F.prototype); // IE8- Object.keys , it seems, everything is simple - we Object.keys over the object through for-in and check if the properties are proper. If it were not for the bug with "non-enumerable enumerable" properties in IE. So you have to check the availability of such properties separately. Similarly, with an additional check on the list of available hidden properties, Object.getOwnPropertyNames also Object.getOwnPropertyNames . console.log(Object.keys({q: 1, w: 2, e: 3})); // => ['q', 'w', 'e'] console.log(Object.keys([1, 2, 3])); // => ['0', '1', '2'] console.log(Object.getOwnPropertyNames([1, 2, 3])); // => ['0', '1', '2', 'length'] Object#__define[GS]etter__ , but Object.defineProperty missing, have long since become extinct. In old IE, it is possible to create an object with getters / setters through perversions with VBScript , but this is a separate topic. enumerable: false properties are enumerable: false , they are not set, but it is possible to check whether it is such through Object # propertyIsEnumerable . In IE8, there are methods for working with descriptors, but it would be better if they did not exist (they work only with DOM objects). So, all we can do for IE8- is a stub. Setting the property value descriptor value in Object.defineProperty and Object.defineProperties and honestly getting the value and enumerable in Object.getOwnPropertyDescriptor .Object.freeze, Object.preventExtensions, Object.seal ? Not only is their emulation impossible, you can only do stubs, but there is such a point of view:Object.freeze, Object.preventExtensions, Object.seal, with, evalAnd I completely agree with her, so we can do without them.
Crazy shit Stay away from it.
- Felix Geisendörfer
var fn = console.log.bind(console, 42); fn(43); // => 42 43 +new Date . Date.now(); // => 1400263401642 '\n \n'.trim(); // => '' JSON module, it is supported by IE8 and, within the framework of this library, I see no point in implementing it. If you need it in absolutely prehistoric IE - no one bothers to use, for example, this polyfill.chrome://flags/#enable-javascript-harmony link chrome://flags/#enable-javascript-harmony ), and Node.js to start with the --harmony . Something is available without a checkbox, for example, Promise , WeakMap and WeakSet , and starting with Chrome 38, Symbol , Map , Set , iterators are also available. Node.js lags far behind in this regard, since, especially in a stable branch, v8 is rarely updated there. But it, unlike the user's browser, no one will prevent you from launching with a flag.Proxy (and this is one of the most delicious features), based on ECMAScript 5 and even more so ECMAScript 3 is impossible to implement, but most can be implemented if not completely, then at least partially and unfairly.Symbol.iterator . Default -es6-shim and es6-symbol . Our library easily replaces them, which makes this preprocessor an ideal pair of it. Converts code to ECMAScript 5, but mainly it concerns the standard library - with stub methods like #Object.defineProperties almost everything will work in old IE. var foo = {q: 1, w: 2} , bar = {e: 3, r: 4} , baz = {t: 5, y: 6}; Object.assign(foo, bar, baz); // => foo = {q: 1, w: 2, e: 3, r: 4, t: 5, y: 6} Object.mixinthat also copied non-enumerable properties, took into account descriptors and reassigned the parent obtained via the keyword super. However, it was decided to postpone its addition. Its counterpart is in the # bicycle part of the library.==with his ghosts, look at ===: NaN === NaN // => false 0 === -0 // => true, : 1/0 === 1/-0 // => false NaNis equal NaN, and, +0and -0different. In ECMAScript 6, they wanted to bring it out as operators isand isnt, it seems, having understood that comparison operators in the language are already quite a few, and for backward compatibility, they were rendered as an Object.is method . Example : Object.is(NaN, NaN); // => true Object.is(0, -0); // => false Object.is(42, 42); // => true, '===' Object.is(42, '42'); // => false, '===' NaNis equal to NaN, and, unlike the previous one, it -0is equal to +0. They ensure the uniqueness of the # collections key ; it is used when checking whether an item is in the collection through #Array#includes .Object.setPrototypeOf. Example : function Parent(){} function Child(){} Object.setPrototypeOf(Child.prototype, Parent.prototype); new Child instanceof Child; // => true new Child instanceof Parent; // => true function fn(){} Object.setPrototypeOf(fn, []); typeof fn == 'function'; // => true fn instanceof Array; // => true var object = {}; Object.setPrototypeOf(object, null); object instanceof Object; // => false __proto__ . , , IE10-, — / Object .Object.setPrototypeOf — __proto__ : var setPrototypeOf = Function.call.bind(Object.getOwnPropertyDescriptor(Object.prototype, '__proto__').set); __proto__cannot be used as a function. To implement Object.setPrototypeOfthem, it remains only to set the value by key __proto__.Object.setPrototypeOfis completely missing, since it is impossible.Object.setPrototypeOfdoes not work, if the prototype chain of the target is absent Object.prototypeor property __proto__overridden through, for example Object.defineProperty.Object#toString. The topic is serious, but we'll talk about it in the second part of the article .Array.fromand Array.of- generics, if they are running in the context of a function other than Array, they create its instances. If there is a desire to get acquainted with this in more detail, it is well described in this article on new array methods .Array.from. This is a universal cast to an array of # iterable and array-like objects. In most cases, it will replaceArray.prototype.slice.callwithout specifying the starting and ending positions. Additionally, the method accepts an optional map callback and its execution context. In the case of the transfer of the object being iterated and without a map callback, the result is similar to the use of the # spread operatorin the array literal -[...iterable]. Example : Array.from(new Set([1, 2, 3, 2, 1])); // => [1, 2, 3] Array.from({0: 1, 1: 2, 2: 3, length: 3}); // => [1, 2, 3] Array.from('123', Number); // => [1, 2, 3] Array.from('123', function(it){ return it * it; }); // => [1, 4, 9] Array, as an analogue of an array literal[]. Example : Array.of(1); // => [1] Array.of(1, 2, 3); // => [1, 2, 3] function isOdd(val){ return val % 2; } [4, 8, 15, 16, 23, 42].find(isOdd); // => 15 [4, 8, 15, 16, 23, 42].findIndex(isOdd); // => 2 [4, 8, 15, 16, 23, 42].find(isNaN); // => undefined [4, 8, 15, 16, 23, 42].findIndex(isNaN); // => -1 Array(5).map(function(){ return 42; }); // => [undefined × 5], .map "" Array(5).fill(42); // => [42, 42, 42, 42, 42] String#contains, but #Array#includes drew it along, as long as it is available and by the old name) checks the occurrence of the substring in the string. String # startsWith and String # endsWith check if the string begins or ends for a given substring. These 3 methods take an additional argument - the starting position. Example : 'foobarbaz'.includes('bar'); // => true 'foobarbaz'.includes('bar', 4); // => false 'foobarbaz'.startsWith('foo'); // => true 'foobarbaz'.startsWith('bar', 3); // => true 'foobarbaz'.endsWith('baz'); // => true 'foobarbaz'.endsWith('bar', 6); // => true 'string'.repeat(3); // => 'stringstringstring' enumerable: false , for-in Object.keys , — , , , - Object.defineProperty -, . var Person = (function(){ var NAME = Symbol('name'); function Person(name){ this[NAME] = name; } Person.prototype.getName = function(){ return this[NAME]; }; return Person; })(); var person = new Person(''); console.log(person.getName()); // => '' console.log(person['name']); // => undefined console.log(person[Symbol('name')]); // => undefined, Symbol for(var key in person)console.log(key); // => 'getName', console.log(typeof Symbol()); // => 'symbol' Object.getOwnPropertySymbols , . # WeakMap . , , .for-innon-returnedObject.keyskeys — are fairly simple, for example: window.Symbol || (function(){ var id = 0; window.Symbol = function(description){ if(this instanceof Symbol)throw new TypeError('Symbol is not a constructor'); var symbol = Object.create(Symbol.prototype) , tag = 'Symbol(' + description + ')_' + (++id + Math.random()).toString(36); symbol.tag = tag; Object.defineProperty(Object.prototype, tag, { configurable: true, set: function(it){ Object.defineProperty(this, tag, { enumerable : false, configurable: true, writable : true, value : it }); } }); return symbol; } Symbol.prototype.toString = function(){ return this.tag; } })(); Symbolwe call, we generate a unique key string, for example "Symbol(description)_m.y9oth2pcqaypsyvi",, and by this key we Object.prototypeset the setter. When trying to set the value by the key string, to which our “symbol” is brought, the setter sets the enumerable: falseproperty to the current object. However, these "characters" have a huge number of minuses, only a part:Object.prototype: Do not particularly abuse, may affect performance.Object.defineProperty ), Object.prototype , «» .Object.prototype (, Object.create(null) ), «» .Symbol() in {} true — .typeof Symbol() 'object' — JavaScript .Object.getOwnPropertySymbols — Object.getOwnPropertyNames , , Object.getOwnPropertyNames , , «».Object.prototype , . . Symbol.pure , , , — - Object.prototype , Symbol.set , — , — , Object.defineProperty enumerable: false . , . Symbol : var Person = function(){ var NAME = Symbol.pure('name'); function Person(name){ Symbol.set(this, NAME, name); } Person.prototype.getName = function(){ return this[NAME]; }; return Person; }(); Object.getOwnPropertySymbolswe do not add themethod. And you want to have a more or less universal, moreover, standardized way to bypass all keys, both strings and characters. ECMAScript 6 adds a moduleReflect - first of all, a set of stubs forProxy. Since we do not have the ability to emulateProxy,Reflectwe don’t really need themodule. However, it has a methodReflect.ownKeysthat returns all the object's own keys — both strings and symbols, i.e.Object.getOwnPropertyNames + Object.getOwnPropertySymbols. Add this method. Example : var O = {a: 1}; Object.defineProperty(O, 'b', {value: 2}); O[Symbol('c')] = 3; Reflect.ownKeys(O); // => ['a', 'b', Symbol(c)] Symbol.forsearches in a register and returns a character by key-string, does not find it - creates a new one, adds it to the register and returns it. Symbol.keyForreturns the string which in the register corresponds to the passed character. Example : var symbol = Symbol.for('key'); symbol === Symbol.for('key'); // true Symbol.keyFor(symbol); // 'key' Symbol.iterator and #Symbol.toStringTag .Map, Set, WeakMapand WeakSet. There are also typed arrays , but for now we can do without them.MapandSet.forEach, the number of elements is available through the property.size. Example : var a = [1]; var map = new Map([['a', 1], [42, 2]]); map.set(a, 3).set(true, 4); console.log(map.size); // => 4 console.log(map.has(a)); // => true console.log(map.has([1])); // => false console.log(map.get(a)); // => 3 map.forEach(function(val, key){ console.log(val); // => 1, 2, 3, 4 console.log(key); // => 'a', 42, [1], true }); map.delete(a); console.log(map.size); // => 3 console.log(map.get(a)); // => undefined console.log(Array.from(map)); // => [['a', 1], [42, 2], [true, 4]] Map, there is the possibility of a detour. Example : var set = new Set(['a', 'b', 'a', 'c']); set.add('d').add('b').add('e'); console.log(set.size); // => 5 console.log(set.has('b')); // => true set.forEach(function(it){ console.log(it); // => 'a', 'b', 'c', 'd', 'e' }); set.delete('b'); console.log(set.size); // => 4 console.log(set.has('b')); // => false console.log(Array.from(set)); // => ['a', 'c', 'd', 'e'] .forEach, there is no property.size. This is another way to store private data, more “honest”, but also more resource-intensive , compared with the use of # symbols . If in the future JavaScript will add abstract references , a convenient syntax will appear for such private fields. Example : var a = [1] , b = [2] , c = [3]; var wmap = new WeakMap([[a, 1], [b, 2]]); wmap.set(c, 3).set(b, 4); console.log(wmap.has(a)); // => true console.log(wmap.has([1])); // => false console.log(wmap.get(a)); // => 1 wmap.delete(a); console.log(wmap.get(a)); // => undefined // var Person = (function(){ var names = new WeakMap; function Person(name){ names.set(this, name); } Person.prototype.getName = function(){ return names.get(this); }; return Person; })(); var person = new Person(''); console.log(person.getName()); // => '' for(var key in person)console.log(key); // => 'getName' var a = [1] , b = [2] , c = [3]; var wset = new WeakSet([a, b, a]); wset.add(c).add(b).add(c); console.log(wset.has(b)); // => true console.log(wset.has([2])); // => false wset.delete(b); console.log(wset.has(b)); // => false Map, Setand WeakMap. In the nightly assemblies appeared and WeakSet. Mapand Settake the object being iterated. Mapand Sethave iterators and a method .forEach.WeakMap WeakSet . # . v8 , Map Set .forEach , Map Set , .Map, Set WeakMap . . Map Set , .forEach .Map, Set WeakMap . . , , , , , next . forEach , 3 , , 1 Set 2 Map ..addand .setdo not return this- to fill the collection with a chain of these methods will not work. But it is easily treated.Map? In the instance Map- 2 arrays, keys and values. When an item is received, we look for a match in the array of keys and return the element from the array of values ​​at the resulting index. Or, to optimize the deletion, an alternative option is a chain of entry objects. What is wrong in both cases? This is very slow, the complexity of the search element - O (n), the complexity of the operation uniq - O (n 2 ). What does it threaten us with? Here is a small test: var array = []; for(var i = 0; i < 100000; i++)array.push([{}, {}]); array = array.concat(array); console.time('Map test'); var map = new Map(array); console.timeEnd('Map test'); console.log('Map size: ' + map.size); Map.Map, for example, in Firefox: Map test: Map test: 46.25 Map size: 100000 Mapfrom the most popular ECMAScript 6 polyfile: Map test: Map test: 506823.31 Map size: 100000 Objectthat accepts only strings as keys. In the polyfile tested above, there is a small optimization - the search index for a key-string or a number through a simple function, which reduces the average difficulty of searching for a collection item to O (1): function fastKey(key){ if(typeof key === 'string')return '$' + key; else if(typeof key === 'number')return key; return null; }; Map, as keys which you can effectively use only primitives? Object.create(null)it perfectly copes with this . var STOREID = Symbol('storeId') , id = 0; function fastKey(it){ // 'S' 'P' if(it !== Object(it))return (typeof it == 'string' ? 'S' : 'P') + it; // - if(!Object.hasOwnProperty.call(it, STOREID))it[STOREID] = ++id; // 'O' return 'O' + it[STOREID]; } Map 2 -, 2 Object , . / : , -, -, . , : Map test: Map test: 669.93 Map size: 100000 Set , 1 ..forEach, properties .size. In the case of storing keys and values ​​in the collection object, it will no longer be loosely coupled - the keys / values ​​will not be deleted, we will get just a trimmed version Setand Map. The only more or less reasonable solution has long been known - to keep the values ​​on the key, and in the collection object only its identifier. Since values ​​are stored on the key, full privacy of the stored data is lost in the polyfile. window.WeakMap || (function(){ var id = 0 , has = Function.call.bind(Object.prototype.hasOwnProperty) , WEAKDATA = Symbol('WeakData') , ID = Symbol('ID'); window.WeakMap = function(){ if(!(this instanceof WeakMap))throw TypeError(); this[ID] = id++; } Object.assign(WeakMap.prototype, { 'delete': function(key){ return this.has(key) && delete key[WEAKDATA][this[ID]]; }, has: function(key){ return key === Object(key) && has(key, WEAKDATA) && has(key[WEAKDATA], this[ID]); }, get: function(key){ if(key === Object(key) && has(key, WEAKDATA))return key[WEAKDATA][this[ID]]; }, set: function(key, value){ if(key !== Object(key))throw TypeError(); if(!has(key, WEAKDATA))key[WEAKDATA] = {}; key[WEAKDATA][this[ID]] = value; return this; } }); })(); // <- snapshot 1 var array = []; for(var i = 0; i < 100000; i++)array[i] = {}; var wm = new WeakMap(); for(var i = 0; i < 100000; i++)wm.set(array[i], {}); // <- snapshot 2 array = null; // <- snapshot 3 
WeakMaplive longer than their keys. Someone is trying to circumvent the problem of memory leakage, but this is from the category of esotericism - memory leaks exactly in the same cases .WeakSetthis problem remains, but minimized - instead of the value that can be a heavy object, only the presence flag in the collection is stored on the key.Symbol.iteratorfor-of$forthat implements some of the capabilities of these syntactic constructions. If you are going to use the library with the ES6 + # preprocessor , you can safely build it without this module..nextthat should return an object with fields.done- whether the iterator traversal is completed, and.valueis the value of the current step. An example is a method that creates an iterator for a positive number, allowing you to bypass all integers from 0 to the given ( sandbox ): function NumberIterator(number){ var i = 0; return { next: function(){ return i < number ? {done: false, value: i++} : {done: true}; } } } var iter = NumberIterator(3); iter.next(); // => {done: false, value: 0} iter.next(); // => {done: false, value: 1} iter.next(); // => {done: false, value: 2} iter.next(); // => {done: true} Symbol.iteratorcontaining a method that returns an iterator for it. Accordingly, in order for an iterator to be iterable,Symbol.iteratorit should have a method that returns akeythis. For example, let's make numbers iterable ( sandbox ): Number.prototype[Symbol.iterator] = function(){ return NumberIterator(this); } Array.from(10); // => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] Symbol.iterator. Firefox supports the iterator protocol, but in it, in stable assemblies, there are no # characters and the Symbol.iteratorstring is used instead "@@iterator". Symbols and even appeared in the nightly assemblies Symbol.iterator, but the string is still used in the iterator protocol "@@iterator". In order not to break the protocol of iterators in Firefox, in it in our library we will duplicate methods for getting an iterator both by key Symbol.iterator(create, if absent), and by key "@@iterator". In v8, full support for the iterator protocol appeared with Chrome 38. Number.prototype[Symbol.iterator] = function*(){ for(var i = 0; i < this;)yield i++; } Array.from(10); // => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] for-of designed to bypass the iterated objects. In our example with iterated numbers, it works like this ( sandbox ): for(var num of 5)console.log(num); // => 0, 1, 2, 3, 4 String, Array, Map, SetandArguments. In addition,Array, MapandSethave methods.keys, .valuesand.entries, which return iterators, respectively, by key, value, and key-value pair. Core.js adds data iterators and methods. Together with the cyclefor-ofit looks like this ( sandbox ): var string = 'abc'; for(var val of string)console.log(val); // => 'a', 'b', 'c' var array = ['a', 'b', 'c']; for(var val of array)console.log(val); // => 'a', 'b', 'c'. - .values for(var val of array.values())console.log(val); // => 'a', 'b', 'c' for(var key of array.keys())console.log(key); // => 0, 1, 2 for(var [key, val] of array.entries()){ console.log(key); // => 0, 1, 2 console.log(val); // => 'a', 'b', 'c' } var map = new Map([['a', 1], ['b', 2], ['c', 3]]); for(var [key, val] of map){ // - .entries console.log(key); // => 'a', 'b', 'c' console.log(val); // => 1, 2, 3 } for(var val of map.values())console.log(val); // => 1, 2, 3 for(var key of map.keys())console.log(key); // => 'a', 'b', 'c' for(var [key, val] of map.entries()){ console.log(key); // => 'a', 'b', 'c' console.log(val); // => 1, 2, 3 } var set = new Set([1, 2, 3, 2, 1]); for(var val of set)console.log(val); // => 1, 2, 3. - .values for(var val of set.values())console.log(val); // => 1, 2, 3 for(var key of set.keys())console.log(key); // => 1, 2, 3. .keys .values for(var [key, val] of set.entries()){ // Set .entries console.log(key); // => 1, 2, 3 console.log(val); // => 1, 2, 3 } var list = (function(){return arguments})(1, 2, 3); for(var val of list)console.log(val); // => 1, 2, 3 arguments. The cycle works, but on the basis of its own, outdated, protocol.for-of, starting with Chrome 38, works correctly (not counting the destruction of the array) and all of these iterators are available, except arguments. In older versions (with a checkbox), an iterator was expected, not an iterated object.for-of ( ): $for(new Set([1, 2, 3, 2, 1])).of(function(it){ console.log(it); // => 1, 2, 3 }); // 2 $for - entries - 2 $for([1, 2, 3].entries(), true).of(function(key, value){ console.log(key); // => 0, 1, 2 console.log(value); // => 1, 2, 3 }); // 2 .of - $for('abc').of(console.log, console); // => 'a', 'b', 'c' // , false $for([1, 2, 3, 4, 5]).of(function(it){ console.log(it); // => 1, 2, 3 if(it == 3)return false; }); arguments — Object.prototype , . core.js , . , , , — $for.isIterable(foo) , Symbol.iterator in foo $for.getIterator(foo) , foo[Symbol.iterator]() : var list = (function(){return arguments})(1, 2, 3); console.log($for.isIterable(list)); // => true console.log($for.isIterable({})); // => false var iter = $for.getIterator(list); console.log(iter.next()); // => {value: 1, done: false} console.log(iter.next()); // => {value: 2, done: false} console.log(iter.next()); // => {value: 3, done: false} console.log(iter.next()); // => {value: undefined, done: true} Map, Set, WeakMap, WeakSet , arrays # throughArray.from , they expect methodsPromise.all, Promise.raceto work with # promises . [...new Set([1, 2, 3, 2, 1])]; // => [1, 2, 3] console.log(1, ...[2, 3, 4]); // => 1, 2, 3, 4 var map = new Map([[1, 'a'], [2, 'b']]); new Array(...map.keys(), 3); // => [1, 2, 3] filterand mapfor any iterated objects . Examples, except for the latter - the array destruction is not supported, they work in FF . In 6to5 and Traceur everything works. Example : var ar1 = [for(i of [1, 2, 3])i * i]; // => [1, 4, 9] var set = new Set([1, 2, 3, 2, 1]); var ar2 = [for(i of set)if(i % 2)i * i]; // => [1, 9] var iter = (for(i of set)if(i % 2)i * i); iter.next(); // => {value: 1, done: false} iter.next(); // => {value: 9, done: false} iter.next(); // => {value: undefined, done: true} var map1 = new Map([['a', 1], ['b', 2], ['c', 3]]); var map2 = new Map((for([k, v] of map1)if(v % 2)[k + k, v * v])); // => Map {aa: 1, cc: 9} $for. The call$forreturns an iterator, extended by methodsof, about which,filter, mapandarray. Methodsfilterandmapreturn an iterator that, respectively, filters or converts the values ​​of the previous iterator. This iterator is extended by the same methods as the iterator$for. The methodarrayconverts the current iterator into an array; it accepts an optional map callback. For all these methods, the second, optional, argument is the execution context. If it$foraccepts the flagentries, all the chain callbacks are started with a couple of arguments. var ar1 = $for([1, 2, 3]).array(function(v){ return v * v; }); // => [1, 4, 9] var set = new Set([1, 2, 3, 2, 1]); var ar1 = $for(set).filter(function(v){ return v % 2; }).array(function(v){ return v * v; }); // => [1, 9] var iter = $for(set).filter(function(v){ return v % 2; }).map(function(v){ return v * v; }); iter.next(); // => {value: 1, done: false} iter.next(); // => {value: 9, done: false} iter.next(); // => {value: undefined, done: true} var map1 = new Map([['a', 1], ['b', 2], ['c', 3]]); var map2 = new Map($for(map1, true).filter(function(k, v){ return v % 2; }).map(function(k, v){ return [k + k, v * v]; })); // => Map {aa: 1, cc: 9} $for, which would give a universal and lazy (such is the iterator protocol) method of traversing and transforming iterable objects. But postpone it for the future. And maybe to hell.Dict expects the object to be iterated, but we will not drag all the bicycles associated with the iterators into this chapter.setTimeoutwell as setInterval, are provided by the W3C and WHATWG web standards, # let 's talk about them a little further . Unless functions as objects of the first class - convenient transfer of callbacks. This generates a callback ad . One way to simplify parallel and sequential execution of asynchronous functions is libraries like async.js .try-catchdoes not work, you have to pass errors with callback arguments, which sometimes confuses the code even more. At the end of the chain of promises, you can subscribe to any mistake that may arise in it - either abandoned by the method rejector through throw..then(the first argument is a function that will be executed upon successful completion, the second - if an error occurs).Promisethat accepts a function to which 2 callbacks are passed - the first one resolves the promise, the second one completes with an error. In additionthen, the promises of ES6 contain a methodcatch- an abbreviation forthenwith the first argument omitted; with it, you can subscribe to an error. Example : var log = console.log.bind(console); function sleepRandom(time){ return new Promise(function(resolve, reject){ // resolve , reject - // setTimeout(resolve, time * 1e3, 0 | Math.random() * 1e3); }); } log(''); // => sleepRandom(5).then(function(result){ log(result); // => 869, 5 . return sleepRandom(10); }).then(function(result){ log(result); // => 202, 10 . }).then(function(){ log(' '); // => throw Error('!'); }).then(function(){ log(' - '); }).catch(log); // => Error: '!' process.nextTickand the methods of the # setImmediate polyfil .Promise.resolveandPromise.rejectreturn a promise completed, respectively, successfully or with an error with the value passed to them. If hePromise.resolveaccepts a promise, he returns it. It can also be used to convert other thenables (for example, jQuery Deferred) into promises, in the old version of the standard there was a separate method for thisPromise.cast. Example : Promise.resolve(42).then(log); // => 42 Promise.reject(42).catch(log); // => 42 Promise.resolve($.getJSON('/data.json')); // => ES6 promise Promise.allreturns a promise that will be resolved when all promises from the # iterated collectiontransferred to it are resolved(in v8 now it works only with arrays, I haven’t yet fixed it within the library - I don’t see much point in using it with something else). Elements of the collection that are not promises are given to promises throughPromise.resolve. Example : Promise.all([ 'foo', sleepRandom(5), sleepRandom(15), sleepRandom(10) // 15 - ]).then(log); // => ['foo', 956, 85, 382] Promise.raceis similar to the previous one, but returns a promise that will be resolved when at least one promise from the collection given to it is resolved. In my humble opinion, unlikePromise.all, a little less than completely, useless. Is that with its help it becomes a little easier to hang the limit on the time of the resolution of the promise. Example : function timeLimit(promise, time){ return Promise.race([promise, new Promise(function(resolve, reject){ setTimeout(reject, time * 1e3, Error('Await > ' + time + ' sec')); })]); } timeLimit(sleepRandom(5), 10).then(log); // => 5 853 timeLimit(sleepRandom(15), 10).catch(log); // Error: Await > 10 sec async / await # , , :) Traceur , 6to5 . : var delay = time => new Promise(resolve => setTimeout(resolve, time)); async function sleepRandom(time){ await delay(time * 1e3); return 0 | Math.random() * 1e3; } async function sleepError(time, msg){ await delay(time * 1e3); throw Error(msg); } (async () => { try { log(''); // => log(await sleepRandom(5)); // => 936, 5 . var [a, b, c] = await Promise.all([ sleepRandom(5), sleepRandom(15), sleepRandom(10) ]); log(a, b, c); // => 210 445 71, 15 . await sleepError(5, '!'); log(' '); } catch(e){ log(e); // => Error: '!', 5 . } })(); fetch- a simple and convenient wrapper over XMLHttpRequest, returning a promise. Offer to add, and I think that will add a simple, but often necessary, a function delaysimilar to the function in the previous example, returns a promise that is resolved after a specified time - goodbye setTimeout. Perhaps delayit would be nice to add to this library..lengthand elements by the key [from 0 to .length), which can be skipped - then “holes” appear. They do not contain under themselves Array.prototype, respectively, they do not have array methods. These are objects arguments, strings (formally, IE8 +), typed arrays (arrays, but not containing themselves Array.prototype), collections of DOM elements, jQuery objects, etc..concatnot quite. I think many are familiar with these designs: Array.prototype.slice.call(arguments, 1); // [].slice.call(arguments, 1); Array.from , with its help you can lead to an array of iterable and array-like objects. Array.from(arguments).slice(1); Array.slice(arguments, 1); Array.join('abcdef', '+'); // => 'a+b+c+d+e+f' var form = document.getElementsByClassName('form__input'); Array.reduce(form, function(memo, it){ memo[it.name] = it.value; return memo; }, {}); // => , {name: '', age: '42', sex: 'yes, please'} setTimeoutand setInterval. Many do not know that according to the standard ( W3C , WHATWG ), these functions, in addition to the callback and the delay time, take additional arguments with which the transferred callback is started. But here, as usual, the problem is in IE. In IE9- setTimeoutand setIntervaltake only 2 arguments, it is treated with a wrapper in several lines. // : setTimeout(log.bind(null, 42), 1000); // : setTimeout(log, 1000, 42); RangeError: Maximum call stack size exceeded. This number, depending on the platform, varies from several hundred to several tens of thousands. Asynchronous recursive call saves from stack overflow. True, a recursive call is usually easily rewritten into a normal cycle, which is preferable.setTimeoutwith minimal delay, but it will turn out very slowly. The minimum setTimeoutspecification delay is 4 ms, and on some platforms even more. Total, a maximum, ~ 250 recursive calls per second in modern browsers and, for example, ~ 64 in IE8. It was necessary, and it is necessary, to cycle, since there are ways to make effective pending execution.process.nextTick, then the client came to help from where it was not expected. In IE10, Microsoft added the setImmediate method , which sets the task to be executed immediately after the I / O has completed, and proposed it for W3C standardization . He later appeared on Node.js . FF and Chromium do not hasten to add it. Popular such polyfil.process.nextTickpostMessageMessageChannelscript.onreadystatechangesetTimeoutwith minimal delay setImmediate(function(arg1, arg2){ console.log(arg1, arg2); // => }, ' ', ' '); clearImmediate(setImmediate(function(){ console.log(' '); })); setImmediate, there is a faster alternative - the concept asap(as soon as possible, the library ) - if possible, create a microtask that will be executed before any input / output. Add such a global method in the standard language think in tc39 . Maybe it should be added to the library?consoledefined only when the user is watching it. And, of course, not all methods are available on all platforms. We will not invent our Firebug Lite style console,we’ll just make stubs for methods so that you can use them without checking availability. // : if(window.console && console.log)console.log(42); // : console.log(42); console, so if you need to pass them as a callback you will have to bind - for example, #console.log.bind(console) . In IE, Firebug and Node.js can be transferred without binding, which is much more convenient. Accordingly, we tie the methods to the objectconsole. // : setTimeout(console.log.bind(console, 42), 1000); [1, 2, 3].forEach(console.log, console); // : setTimeout(console.log, 1000, 42); [1, 2, 3].forEach(console.log); console.disable(); console.warn(' , .'); console.enable(); console.warn(' .'); Options zero, no options.
I wanted half the world - enough for a bike.
- Loop of addiction
Undefined, Null, Boolean, String, NumberandObject. ECMAScript 6 adds another data type - #Symbol . To determine the type of data there is an operatortypeof. Here it only works specifically. So historically , thattypeof nullreturns'object'and an attempt to fix it in Harmony was not crowned with success. For a type,Object typeofreturns either'object'or'function'depending on the presence of an internal method[[Call]]. Total we get that the operatortypeofreturns exactly the data type only for primitives, and then not all.nulljust enough to compare it with him, then you Objecthave to either write hundreds of code each time, or a helper. You can, of course, do this - Object(foo) === foobut this solution is far from the fastest - it leads the primitive to the object. In the early drafts of ES6, a method was present Object.isObject, but apparently, due to an attempt to correct typeof null, it was deleted. And it is constantly necessary to check whether a variable is an object. So let's add a helperObject.isObject , which is easier to implement ( sandbox ): Object.isObject = function(it){ return it != null && (typeof it == 'object' || typeof it == 'function'); } // ... typeof {}; // => 'object' typeof isNaN; // => 'function' typeof null; // => 'object' Object.isObject({}); // => true Object.isObject(isNaN); // => true Object.isObject(null); // => false instanceofchecks the prototype chain. If you create an object from a prototype or set an object as a prototypeFunction.prototype, it will not make it a function. The instance propertyconstructorcannot guarantee anything, andconstructor.namenot onlydoesit lose any meaning when compressing code, IE is also not supported. An internal property can help with the classification of an object[[Class]]. The only way to tear it out is a terrible construction well known to manyObject.prototype.toString.call(foo).slice(8, -1). Example : Object.prototype.toString.call(1).slice(8, -1); // => 'Number' Object.prototype.toString.call([]).slice(8, -1); // => 'Array' Object.prototype.toString.call(/./).slice(8, -1); // => 'RegExp' Array.isArray : Object.is Type in Sugar , _.is Type in Undescore , etc.Object.classoffor classifying data , similar to the operator typeof!from LiveScript ( example ).Object.prototype.toString.call(foo).slice(8, -1)a couple of problems:null, undefinedand arguments, this construct returns "Object". This is easily treated with additional checks."Object" . Object#toString ECMAScript 6.[[Class]]. Object#toStringES6, through checking special internal properties, returns the variable to Undefined, Null, Array, String, Arguments, Function, Error, Boolean, Number, Dateor RegExp, while for the rest it searches for a hint on the # symbol Symbol.toStringTag . If a method finds a hint and it is not the name of one of the built-in “classes”, it returns it, it does not find it Object.Object#toString, thanks to # one nasty, but fun bug, we can do it in IE8- without breaking it for-in. And, of course, we implement this approach in the method Object.classof. As a bonus, we get the ability to classify instances of custom designers. Example : var classof = Object.classof; classof(null); // => 'Null' classof(undefined); // => 'Undefined' classof(1); // => 'Number' classof(true); // => 'Boolean' classof('string'); // => 'String' classof(Symbol()); // => 'Symbol' classof(new Number(1)); // => 'Number' classof(new Boolean(true)); // => 'Boolean' classof(new String('string')); // => 'String' var fn = function(){} , list = (function(){return arguments})(1, 2, 3); classof({}); // => 'Object' classof(fn); // => 'Function' classof([]); // => 'Array' classof(list); // => 'Arguments' classof(/./); // => 'RegExp' classof(new TypeError); // => 'Error' classof(new Set); // => 'Set' classof(new Map); // => 'Map' classof(new WeakSet); // => 'WeakSet' classof(new WeakMap); // => 'WeakMap' classof(new Promise(fn)); // => 'Promise' classof([].values()); // => 'Array Iterator' classof(new Set().values()); // => 'Set Iterator' classof(new Map().values()); // => 'Map Iterator' classof(Math); // => 'Math' classof(JSON); // => 'JSON' function Example(){} Example.prototype[Symbol.toStringTag] = 'Example'; classof(new Example); // => 'Example' JSON , .Object.prototype ), . Object.prototype .Map . , ( , ). . Map, unlike object dictionaries, there is no simple literal notation; access to properties occurs through methods — not so concise. MapIt's far from being loved by everyone JSONand not so versatile. Yes, and usually in the keys of the dictionary does not require anything except the lines.Object.prototypeand dictionariesDictObject.prototype, as suggested by the Mozilla Developer Network , depending on the implementation, can be: Object.prototype.constructor(); Object.prototype.hasOwnProperty(); Object.prototype.isPrototypeOf(); Object.prototype.propertyIsEnumerable(); Object.prototype.toLocaleString(); Object.prototype.toString(); Object.prototype.valueOf(); Object.prototype.__proto__; Object.prototype.__count__; Object.prototype.__parent__; Object.prototype.__noSuchMethod__; Object.prototype.__defineGetter__(); Object.prototype.__defineSetter__(); Object.prototype.__lookupGetter__(); Object.prototype.__lookupSetter__(); Object.prototype.eval(); Object.prototype.toSource(); Object.prototype.unwatch(); Object.prototype.watch(); var phone = (function(){ var db = { '': '+7987654', '': '+7654321' }; return { has: function(name){ return name in db; }, get: function(name){ return db[name]; }, set: function(name, phone){ db[name] = phone; }, delete: function(name){ delete db[name]; } }; })(); console.log(phone.has('')); // => true console.log(phone.get('')); // => '+7987654' console.log(phone.has('')); // => false console.log(phone.get('')); // => undefined console.log(phone.has('toString')); // => true console.log(phone.get('toString')); // => function toString() { [native code] } insimilarly checks its presence. Let's add / replace inwith a method hasOwnPropertythat checks the presence of a property in an object without taking into account the prototype chain. We get : // ... has: function(name){ return db.hasOwnProperty(name); }, get: function(name){ if(db.hasOwnProperty(name))return db[name]; }, // ... console.log(phone.get('')); // => '+7987654' phone.set('hasOwnProperty', '+7666666'); // "" console.log(phone.get('')); // TypeError: string is not a function hasOwnPropertyuntied from the object. We get the need to use for every sneeze cumbersome checks. Something like this trash : // ... has: function(name){ return Object.prototype.hasOwnProperty.call(db, name); }, get: function(name){ if(Object.prototype.hasOwnProperty.call(db, name))return db[name]; }, // ... in. phone.set('__proto__', '+7666666'); // "" console.log(phone.get('__proto__')); // => undefined Object.prototypeaddition, there is also a “magic” getter / setter __proto__, the installation of a primitive by this key will be ignored, and the object will be damaged, for example, when traversing properties. In the old engines were other "magical" properties. Here helps except that Object.defineProperty( pesochnitsa ): // ... set: function(name, phone){ Object.defineProperty(db, name, { enumerable : true, configurable: true, writable : true, value : phone }); }, // ... for-incan do without checking when crawling through the dictionary hasOwnProperty. Here are just a bug with # "Non-enumerable enumerable" properties still makes a detour through the for-indictionaries under which is found Object.prototypedefective in old IE.Object.create(null)with it you can use the implementation of the methods originally proposed ( sandbox ): var phone = (function(){ var db = Object.create(null); Object.assign(db, { '': '+7987654', '': '+7654321' }); return { has: function(name){ return name in db; }, get: function(name){ return db[name]; }, set: function(name, phone){ db[name] = phone; }, delete: function(name){ delete db[name]; } }; })(); Object.assign, are not particularly compact.toStringand valueOf. What does it threaten us with?+Object.create(null), or the string,, '' + Object.create(null)-TypeError==, with primitives -TypeErrorDictObject.create(null)and filling it out is much more cumbersome than when creating a dictionary through {}. Of course, the most beautiful solution would be to add a literal dictionary to the language , but this, at least in the short term, is unlikely. There is no possibility of initialization by the literal. There is a record {__proto__: null, foo: 'bar'}, but it is not supported everywhere, at the moment it leads to de-optimization of the code , and it is still rather cumbersome. One rather interesting decisionDictObject.create(null). How is it now and is it doing at all, I do not know. But why not take it, slightly expanding? At the same time we will receive a name space for methods for work with objects as dictionaries. Add the ability to initialize with an iterator entriesor an object without an iterator, a sort of version #Array.from for dictionaries.Dict() instanceof Dictwill not work and #Object.classof(Dict()) will return 'Object', we will add a method to identify dictionaries Dict.isDict. function Dict(props){ var dict = Object.create(null); if(props != null){ if(Symbol.iterator in props){ for(var [key, val] of props)dict[key] = val; } else Object.assign(dict, props); } return dict; } Dict.prototype = null; Dict.isDict = function(it){ return Object.isObject(it) && Object.getPrototypeOf(it) === null; } // ... var map = new Map([['a', 1], ['b', 2], ['c', 3]]); Dict(); // => {__proto__: null} Dict({a: 1, b: 2, c: 3}); // => {__proto__: null, a: 1, b: 2, c: 3} Dict(map); // => {__proto__: null, a: 1, b: 2, c: 3} Dict([1, 2, 3].entries()); // => {__proto__: null, 0: 1, 1: 2, 2: 3} Dict((for([k, v] of map)if(v % 2)[k + k, v * v])); // => {__proto__: null, aa: 1, cc: 9} Dict.isDict({}); // => false Dict.isDict(Dict()); // => true Dict.has- trite, static version hasOwnProperty. In drafts of ECMAScript 6, in the module Reflect- a set of stubs for Proxy, until recently there was a static version of the method hasOwnProperty- the method Reflect.hasOwn. However, in recent versions of the draft specification, this method has been removed.Dict.get- getting the value by key with checking whether the property is own. Not is - return undefined.Dict.set- a method for absolutely paranoids. Allows you to set the property of the dictionary, ignore setters, such as __proto__. Uses defineProperty.deleteand so it works as it should. var dict = {a: 1, b: 2, c: 3}; console.log(Dict.has(dict, 'a')); // => true console.log(Dict.has(dict, 'toString')); // => false console.log(Dict.get(dict, 'a')); // => 1 console.log(Dict.get(dict, 'toString')); // => undefined Dict.set(dict, '__proto__', 42); console.log(Dict.get(dict, '__proto__')); // => 42 forEach, map, someand the like) are very convenient. Their static counterparts for dictionaries are present in almost all general purpose frameworks / libraries. But there is no progress with adding them to the standard.Dict. Everything is simple, the methods are similar to the static versions of the array methods. This is: Dict.forEach, Dict.map, Dict.filter, Dict.some, Dict.every, Dict.find, Dict.findKey, Dict.keyOf, # Dict.includes , Dict.reduce, #Dict.turn . Keyin the name it corresponds indexto the array methods. The “right” versions and the optional argument-index (for the time being?) Are absent, since the order of traversing the keys of objects is not always the same everywhere. Only the enumerated elements of the object are searched. These methods are generic on the same plane as Array.fromor Array.of. For example, Dict.map(dict, fn)will return a newDict, and Dict.map.call(Object, dict, fn)- new Object. But in general, everything is primitive, boring and like everywhere ( sandbox ): var dict = {a: 1, b: 2, c: 3}; Dict.forEach(dict, console.log, console); // => 1, 'a', {a: 1, b: 2, c: 3} // => 2, 'b', {a: 1, b: 2, c: 3} // => 3, 'c', {a: 1, b: 2, c: 3} Dict.map(dict, function(it){ return it * it; }); // => {a: 1, b: 4, c: 9} Dict.filter(dict, function(it){ return it % 2; }); // => {a: 1, c: 3} Dict.some(dict, function(it){ return it === 2; }); // => true Dict.every(dict, function(it){ return it === 2; }); // => false Dict.find(dict, function(it){ return it > 2; }); // => 3 Dict.find(dict, function(it){ return it > 4; }); // => undefined Dict.findKey(dict, function(it){ return it > 2; }); // => 'c' Dict.findKey(dict, function(it){ return it > 4; }); // => undefined Dict.keyOf(dict, 2); // => 'b' Dict.keyOf(dict, 4); // => undefined Dict.includes(dict, 2); // => true Dict.includes(dict, 4); // => false Dict.reduce(dict, function(memo, it){ return memo + it; }); // => 6 Dict.reduce(dict, function(memo, it){ return memo + it; }, ''); // => '123' Dict.turn(dict, function(memo, it, key){ memo[key + key] = it; }); // => {aa: 1, bb: 2, cc: 3} Dict.turn(dict, function(memo, it, key){ it % 2 && memo.push(key + it); }, []); // => ['a1', 'c3'] Dictthey are notin the framework of the module, and are not foreseen. Salvation here can be abstract references . But within the framework of #$for and #Map it is quite possible and will appear.for-of . But for objects like dictionaries, this is neither warm nor cold - for them in ES6 iterators are not provided. Accordingly, there is no easy way to iterate through them for-of, initialize # with aMap dictionary, etc. Adding methods .keys, .valuesand .entriesin Object.prototypeunlikely - there is already enough garbage, see the description of the previous problem. But two other scenarios are quite probable:Dict- Dict.{keys, values, entries}. But as I already wrote that with the prospect of adding this module to the standard I do not know.Object.{values, entries} by typeObject.keysreturning an array, not an iterator, and already through the array iterator to bypass the object. var dict = {a: 1, b: 2, c: 3}; console.log(Object.values(dict)); // => [1, 2, 3] console.log(Object.entries(dict)); // => [['a', 1], ['b', 2], ['c', 3]] for(var key of Dict.keys(dict))console.log(key); // => 'a', 'b', 'c' for(var [key, val] of Dict.entries(dict)){ console.log(key); // => 'a', 'b', 'c' console.log(val); // => 1, 2, 3 } $for(Dict.values(dict)).of(console.log); // => 1, 2, 3 new Map(Dict.entries(dict)); // => Map {a: 1, b: 2, c: 3} new Map((for([k, v] of Dict.entries(dict))if(v % 2)[k + k, v * v])); // => Map {aa: 1, cc: 9} Dictnot just an abbreviation for Object.create(null)with the possibility of initialization by an iterator and an object, but a full-fledged constructor with a prototype that does not contain key strings, only # symbols . Like this : function Dict(props){ if(!(this instanceof Dict))return new Dict(props); if(props != null){ if(Symbol.iterator in props){ for(var [key, val] of props)this[key] = val; } else Object.assign(this, props); } } Dict.prototype = Object.create(null); Dict.prototype[Symbol.toStringTag] = 'Dict'; Dict.prototype[Symbol.iterator] = function(){ return Dict.entries(this); }; new Dict instanceof Dict .for(var [key, value] of dict){...}, new Map(dict)without having to get an iterator through Dict.entries.Object.classof(new Dict) would return 'Dict', not 'Object'.Dictget there.Function#bind method . Here are the possibilities of partial application of this method is far from fully disclosed. In this chapter, we will look at things like:Function#bindcombines partial application and context binding this. The latter is needed far from always, and in this case the binding is thisnot only an “extra” argument for what to write. If the context in which the partially applied function is to be launched is unknown in advance, the method is Function#bindnot applicable. For example, if it is a prototype method ( sandbox ): Array.prototype.compact = [].filter.bind(Array.prototype, function(val){ return val != null; }); [0, null, 1, undefined, 2].compact(); // => [] - Array.prototype, // : Array.prototype.compact = function(){ return this.filter(function(val){ return val != null; }); }; [0, null, 1, undefined, 2].compact(); // => [0, 1, 2]; this- Function#part( sandbox ): Array.prototype.compact = [].filter.part(function(val){ return val != null; }); [0, null, 1, undefined, 2].compact(); // => [0, 1, 2]; var fn = console.log.part(1, 2); fn(3, 4); // => 1, 2, 3, 4 Function#bindwe will not be able to help - we will have to write a wrapper manually for each specific case. function fn1(a, c){ console.log(a, 2, c, 4); }; fn1(1, 3); // => 1, 2, 3, 4 function fn2(b, c){ console.log(1, b, c, 4); }; fn2(2, 3); // => 1, 2, 3, 4 _ (, , LiveScript , ), , Undescore.js (, _.partial ) LoDash . , _ , _ , . , core._ . : var fn1 = console.log.part(_, 2, _, 4); fn1(1, 3); // => 1, 2, 3, 4 var fn2 = console.log.part(1, _, _, 4); fn2(2, 3); // => 1, 2, 3, 4 fn1(1, 3, 5); // => 1, 2, 3, 4, 5 fn1(1); // => 1, 2, undefined, 4 Function#bysimilar Function#bind, but with the possibility of using a placeholder for arguments. It would be possible to wrap up Function#bind, forcing to work with a placeholder, but this is a violation of the specification, and this method is already rather inhibited in almost all engines. var fn = console.log.by(console, _, 2, _, 4); fn(1, 3, 5); // => 1, 2, 3, 4, 5 fn(foo.bar.baz.bind(foo.bar)). We have to write foo.bar2 times, this is a clear violation of the principle of DRY. I hope in the future abstract references will save from this problem , but the proposed implementation does not solve the problem . Perhaps the most delicious and beautiful solution would be to add an access operator to the language while preserving the context, similar to ~LiveScript - fn(foo.bar~baz)( sandbox )._.bindKeyfromLoDash (but with early binding), but it is also quite cumbersome and further worsens the readability, or similar in functionality to the method Object.prototype, for example, Object#boundTofrom Eddy.js .Object.prototype. To risk, expanding the Object.prototypemethod with a short key-string, we, at least, for the time being, we will not - it is difficult to avoid conflicts, and we will break it for-inin IE8-. Earlier in this chapter, we already used a global variable _. In order not to produce extra essences and for short, let's apply it here. Replace the _method with the object toString(respectively, if used in conjunction with Undescore.js or LoDash - you need to connect core.jsafter them). It will return a unique key string, similar to the # key of a poly-symbol of a character . Let's add a method using this key Object.prototype. Due to the use of a dirty hack with a fun bug , we add this method to IE8- without breaking it for-in.fn(foo.bar[_]('baz'))- far from the ideal, but at least got rid of the second mention of the object. The returned method is cached. Examples : ['foobar', 'foobaz', 'barbaz'].filter(/bar/[_]('test')); // => ['foobar', 'barbaz'] var has = {}.hasOwnProperty[_]('call'); console.log(has({key: 42}, 'foo')); // => false console.log(has({key: 42}, 'key')); // => true var array = [] , push = array[_]('push'); push(1); push(2, 3); console.log(array); // => [1, 2, 3]; tie, boundTo, bindKey - , .Proxy ES6 — — fn(foo.bar[_].baz) , Proxy (, ), , . var _ = Symbol(); Object.defineProperty(Object.prototype, _, { get: function(){ return new Proxy(this, { apply: function(){ /* [_] */ }, get: function(context, name){ return context[name].bind(context); } }); } }); ['foobar', 'foobaz', 'barbaz'].filter(/bar/[_].test); // => ['foobar', 'barbaz'] var has = {}.hasOwnProperty[_].call; console.log(has({key: 42}, 'foo')); // => false console.log(has({key: 42}, 'key')); // => true var array = [] , push = array[_].push; push(1); push(2, 3); console.log(array); // => [1, 2, 3]; parseInt- rather unintelligible, no one bothers to bring the lines to a number, for example, using Numberthat it does not expect additional arguments. The point here is not their “danger”, but the need to write an extra wrapper. [1, 2, 3].forEach(console.log); // => 1 0 [1, 2, 3] // => 2 1 [1, 2, 3] // => 3 2 [1, 2, 3] .forEach, like many others, passes the optional arguments to the callback — the index and the array itself. And we do not need them. So each time the callback will have to be wrapped into another function: [1, 2, 3].forEach(function(it){ console.log(it); }); // => 1, 2, 3 Function#only. We realize its option. The first argument is the maximum number of arguments, the second, optional is the context. Example : [1, 2, 3].forEach(console.log.only(1)); // => 1, 2, 3 var date = new Date; function lz2(it){ return it > 9 ? it : '0' + it; } var format = [date.getDate(), date.getMonth() + 1, date.getFullYear()].map(lz2).join('.') + ' ' + [date.getHours(), date.getMinutes(), date.getSeconds()].map(lz2).join(':'); console.log(format); // => '18.11.2014 06:07:25 ' Date#toLocaleString, Date#toLocaleDateString, Date#toLocaleTimeStringby adding 2 arguments to them: localization and format options. Using them, a string close to the format mentioned above can be obtained as follows : new Date().toLocaleString('ru-RU', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric', hour: 'numeric', minute: '2-digit', second: '2-digit' }); // => ', 18 2014 ., 6:07:25' Intl. Here the whole module of working with the date is a few dozen lines.Date#formatand its UTC version Date#formatUTC( sandbox ): new Date().format('W, D MM Y ., h:mm:ss', 'ru'); // => ', 18 2014 ., 6:07:25' new Date().formatUTC('W, D MM Y ., h:mm:ss', 'ru'); // => ', 18 2014 ., 0:07:25' s | | 0-59 ss | , 2 | 00-59 m | | 0-59 mm | , 2 | 00-59 h | | 0-23 hh | , 2 | 00-23 D | | 1-31 DD | , 2 | 01-31 W | , | N | | 1-12 NN | , 2 | 01-12 M | , | MM | , | Y | , | 2014 YY | , 2 | 14 ru) and English ( en) locales. A locale is specified either by the method core.localeor by the second argument of the methods Date#formatand Date#formatUTC( sandbox ): new Date().format('W, D MM Y', 'ru'); // => ', 18 2014' new Date().format('W, D MM Y'); // => 'Tuesday, 18 November 2014' core.locale('ru'); new Date().format('W, D MM Y'); // => ', 18 2014' core.addLocale, but due to the possibility of building the library without extending native objects, the universal locale module will look like this: (typeof core != 'undefined' ? core : require('core-js/library')).addLocale('ru', { weekdays: ',,,,,,', months: ':|,:|,:|,:|,:|,:|,:|,:|,:|,:|,:|,:|' }); new Date().format('DD.NN.YY'); // => '18.11.14' new Date().format('hh:mm:ss'); // => '06:07:25' new Date().format('DD.NN.Y hh:mm:ss'); // => '18.11.2014 06:07:25' new Date().format('W, D MM Y '); // => ', 18 2014 ' new Date().format('D MM, h:mm'); // => '18 , 6:07' new Date().format('M Y'); // => ' 2014' Object.defineProperty / Object.defineProperties , ( , ) , .Object.assign , ECMAScript 6 Object.mixin , - . , -, super . , .Object.definethat works as described Object.mixin— copying the properties of the source object to the target with descriptors, but not overriding the parent, in the absence of a keyword superin ECMAScript 5. // : Object.defineProperty(target, 'c', { enumerable: true, configurable: true, get: function(){ return this.a + this.b; } }); // : Object.define(target, { get c(){ return this.a + this.b; } }); Object.create. It would be nice to add your own properties of the object during creation, but the second argumentObject.create, as well asObject.defineProperties, accepts an object that contains objects of property descriptors, which is terribly cumbersome.Object.make- an analogueObject.create, the second argument is a waiting object, not an object of descriptors, but a simple object from which own properties are copied into the object being created taking into account descriptors. // : var copy = Object.make(Object.getPrototypeOf(src), src); // : function Vector2D(x, y){ this.x = x; this.y = y; } Object.define(Vector2D.prototype, { get xy(){ return Math.hypot(this.x, this.y); } }); function Vector3D(x, y, z){ Vector2D.apply(this, arguments); this.z = z; } Vector3D.prototype = Object.make(Vector2D.prototype, { constructor: Vector3D, get xyz(){ return Math.hypot(this.x, this.y, this.z); } }); var vector = new Vector3D(9, 12, 20); console.log(vector.xy); // => 15 console.log(vector.xyz); // => 25 vector.y++; console.log(vector.xy); // => 15.811388300841896 console.log(vector.xyz); // => 25.495097567963924 Object.definePropertiesand, Object.createand to some extent, an alternative to ours Object.makeand Object.define. That's just too bulky.Array#contains , - MooTools , ) ECMAScript 7. . Array#indexOf , # SameValueZero «». , , — . : [1, 2, 3].includes(2); // => true [1, 2, 3].includes(4); // => false [1, 2, 3].includes(2, 2); // => false [NaN].indexOf(NaN); // => -1 [NaN].includes(NaN); // => true Array(1).indexOf(undefined); // => -1 Array(1).includes(undefined); // => true Array#turnis the fruit of my sick fantasy. Although, as it turned out, not unique - it is similar to the method_.transformfrom LoDash . This is an alternative to the methodArray#reducefor convolving an array into an arbitrary battery object (by default, a new array) without having to return the battery from the callback. The signature of the method and callback is similarArray#reduce. You can interrupt the collection by returning from the callbackfalse. Examples : // : [1, 2, 3, 4, 5].reduce(function(memo, it){ memo['key' + it] = !!(it % 2); return memo; }, {}); // => {key1: true, key2: false, key3: true, key4: false, key5: true} [1, 2, 3, 4, 5].turn(function(memo, it){ memo['key' + it] = !!(it % 2); }, {}); // => {key1: true, key2: false, key3: true, key4: false, key5: true} // filter + map + slice, : [1, 2, 3, 4, 5, 6, 7, 8, 9].map(function(it){ return it * it; }).filter(function(it){ return it % 2; }).slice(0, 2); // => [1, 9] [1, 2, 3, 4, 5, 6, 7, 8, 9].turn(function(memo, it){ it % 2 && memo.push(it * it); if(memo.length == 2)return false; }); // => [1, 9] for-of, simple generation of an array of a given length through #Array.from (and maybe # spread ), etc. So let's add an iterator of numbers, though not in such a primitive implementation. Examples : // : for(var i = 0; i < 3; i++)console.log(i); // => 0, 1, 2 // for-of : for(var i of 3)console.log(i); // => 0, 1, 2 // for-of, : $for(3).of(console.log); // => 0, 1, 2 // : // .map "" Array(10).map(Math.random); // => [undefined × 10] // ES5 , : Array.apply(undefined, Array(10)).map(Math.random); // => [0.9442228835541755, 0.8101077508181334, ...] // ES6 , : Array(10).fill(undefined).map(Math.random); // => [0.5587614295072854, 0.009569905698299408, ...] // Number Iterator: Array.from(10, Math.random); // => [0.9817775336559862, 0.02720663254149258, ...] Array.from(10); // => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] Array.from(10, function(it){ return this + it * it; }, .42); // => [0.42, 1.42, 4.42, 9.42, 16.42, 25.42, 36.42, 49.42, 64.42, 81.42] // Comprehensions: [for(i of 10)if(i % 2)i * i]; // => [1, 9, 25, 49, 81] Dict((for(i of 3)['key' + i, !(i % 2)])); // => {key0: true, key1: false, key2: true} $for(10).filter(function(i){ return i % 2; }).array(function(i){ return i * i; }); // => [1, 9, 25, 49, 81] Dict($for(3).map(function(i){ return ['key' + i, !(i % 2)]; })); // => {key0: true, key1: false, key2: true} Number.prototype- from the category of pleasant things. Just like in Sugar and MooTools, we move the methods from objectMathtoNumber.prototype. There is nothing special to say here - the context becomes the first argument of a mathematical function. Maybe it duplicates the existing, standardized functionality, but this is quite convenient :)Number#random. It returns a random number between the context number and the argument passed (the default is 0). 3..pow(3); // => 27 (-729).abs().sqrt(); // => 27 10..random(20); // => (10, 20), , 16.818793776910752 10..random(20).floor(); // => [10, 19], , 16 var array = [1, 2, 3, 4, 5]; array[array.length.random().floor()]; // => , , 4 escapeHTMLandunescapeHTMLthe prototype line. We will not break this tradition: '<script>doSomething();</script>'.escapeHTML(); // => '<script>doSomething();</script>' '<script>doSomething();</script>'.unescapeHTML(); // => '<script>doSomething();</script>' RegExp, somewhere a methodString.prototype. It has long been discussed the addition of such a method in ECMAScript. I hope we will wait for this, but for now we will implement the proposed option in our library: RegExp.escape(' -[]{}()*+?.,\\^$|'); // => ' \-\[\]\{\}\(\)\*\+\?\.\,\\\^\$\|' Source: https://habr.com/ru/post/216997/
All Articles