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.mixin
that 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
NaN
is equal NaN
, and, +0
and -0
different. In ECMAScript 6, they wanted to bring it out as operators is
and 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, '==='
NaN
is equal to NaN
, and, unlike the previous one, it -0
is 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.setPrototypeOf
them, it remains only to set the value by key __proto__
.
Object.setPrototypeOf
is completely missing, since it is impossible.Object.setPrototypeOf
does not work, if the prototype chain of the target is absent Object.prototype
or 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.from
and 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.call
without 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-in
non-returnedObject.keys
keys — 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; } })();
Symbol
we call, we generate a unique key string, for example "Symbol(description)_m.y9oth2pcqaypsyvi"
,, and by this key we Object.prototype
set the setter. When trying to set the value by the key string, to which our “symbol” is brought, the setter sets the enumerable: false
property 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.getOwnPropertySymbols
we 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
,Reflect
we don’t really need themodule. However, it has a methodReflect.ownKeys
that 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.for
searches 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.keyFor
returns 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, WeakMap
and WeakSet
. There are also typed arrays , but for now we can do without them.
Map
andSet
.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, Set
and WeakMap
. In the nightly assemblies appeared and WeakSet
. Map
and Set
take the object being iterated. Map
and Set
have 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
..add
and .set
do 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
Map
from the most popular ECMAScript 6 polyfile:
Map test: Map test: 506823.31 Map size: 100000
Object
that 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 Set
and 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
WeakMap
live 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 .
WeakSet
this 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.iterator
for-of
$for
that 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.
.next
that should return an object with fields.done
- whether the iterator traversal is completed, and.value
is 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.iterator
containing a method that returns an iterator for it. Accordingly, in order for an iterator to be iterable,Symbol.iterator
it 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.iterator
string 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, Set
andArguments
. In addition,Array, Map
andSet
have methods.keys, .values
and.entries
, which return iterators, respectively, by key, value, and key-value pair. Core.js adds data iterators and methods. Together with the cyclefor-of
it 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.race
to 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]
filter
and map
for 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$for
returns an iterator, extended by methodsof
, about which,filter, map
andarray
. Methodsfilter
andmap
return 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 methodarray
converts 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$for
accepts 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.
setTimeout
well 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-catch
does 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 reject
or through throw
.
.then
(the first argument is a function that will be executed upon successful completion, the second - if an error occurs).
Promise
that 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 forthen
with 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.nextTick
and the methods of the # setImmediate polyfil .
Promise.resolve
andPromise.reject
return a promise completed, respectively, successfully or with an error with the value passed to them. If hePromise.resolve
accepts 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.all
returns 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.race
is 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 delay
similar to the function in the previous example, returns a promise that is resolved after a specified time - goodbye setTimeout
. Perhaps delay
it would be nice to add to this library.
.length
and 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.
.concat
not 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'}
setTimeout
and 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- setTimeout
and setInterval
take 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.
setTimeout
with minimal delay, but it will turn out very slowly. The minimum setTimeout
specification 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.nextTick
postMessage
MessageChannel
script.onreadystatechange
setTimeout
with 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?
console
defined 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, Number
andObject
. 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 null
returns'object'
and an attempt to fix it in Harmony was not crowned with success. For a type,Object
typeof
returns either'object'
or'function'
depending on the presence of an internal method[[Call]]
. Total we get that the operatortypeof
returns exactly the data type only for primitives, and then not all.
null
just enough to compare it with him, then you Object
have to either write hundreds of code each time, or a helper. You can, of course, do this - Object(foo) === foo
but 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
instanceof
checks 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 propertyconstructor
cannot guarantee anything, andconstructor.name
not 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.classof
for classifying data , similar to the operator typeof!
from LiveScript ( example ).
Object.prototype.toString.call(foo).slice(8, -1)
a couple of problems:
null, undefined
and arguments
, this construct returns "Object"
. This is easily treated with additional checks."Object"
. Object#toString
ECMAScript 6.[[Class]]
. Object#toString
ES6, through checking special internal properties, returns the variable to Undefined, Null, Array, String, Arguments, Function, Error, Boolean, Number, Date
or 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. Map
It's far from being loved by everyone JSON
and not so versatile. Yes, and usually in the keys of the dictionary does not require anything except the lines.
Object.prototype
and dictionariesDict
Object.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] }
in
similarly checks its presence. Let's add / replace in
with a method hasOwnProperty
that 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
hasOwnProperty
untied 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.prototype
addition, 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-in
can 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-in
dictionaries under which is found Object.prototype
defective 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.
toString
and valueOf
. What does it threaten us with?
+Object.create(null)
, or the string,, '' + Object.create(null)
-TypeError
==
, with primitives -TypeError
Dict
Object.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 decision
Dict
Object.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 entries
or an object without an iterator, a sort of version #Array.from
for dictionaries.
Dict() instanceof Dict
will 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
.
delete
and 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, some
and 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
. Key
in the name it corresponds index
to 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.from
or 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']
Dict
they 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, .values
and .entries
in Object.prototype
unlikely - 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.keys
returning 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}
Dict
not 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'
.Dict
get 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#bind
combines partial application and context binding this
. The latter is needed far from always, and in this case the binding is this
not 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#bind
not 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#bind
we 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#by
similar 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.bar
2 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 ).
_.bindKey
fromLoDash (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#boundTo
from Eddy.js .
Object.prototype
. To risk, expanding the Object.prototype
method 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-in
in 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 Number
that 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#toLocaleTimeString
by 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#format
and 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.locale
or by the second argument of the methods Date#format
and 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.define
that 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 super
in 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.defineProperties
and, Object.create
and to some extent, an alternative to ours Object.make
and 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#turn
is the fruit of my sick fantasy. Although, as it turned out, not unique - it is similar to the method_.transform
from LoDash . This is an alternative to the methodArray#reduce
for 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 objectMath
toNumber.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
escapeHTML
andunescapeHTML
the 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/