The book "How JavaScript Works"

    imageMost programming languages ​​grew out of an ancient paradigm originated in Fortran's time. JavaScript guru Douglas Crockford uproots these dried roots, allowing us to think about the future of programming, moving to a new level of understanding the requirements for the Next Language.

    The author begins with the basics: names, numbers, logical values, characters, and other basic information. You will learn not only about the problems and difficulties of working with types in JavaScript, but also how to get around them. Then you will begin to familiarize yourself with data structures and functions to understand the mechanisms underlying them, and learn how to use higher-order functions and an object-oriented programming style without classes.

    Excerpt
    How code works without classes


    And you think that you are smart outside all classes and free.
    John Lennon

    One of the key ideas in developing object-oriented programming was a model for exchanging data between program parts. The name of the method and its arguments must be represented in the form of messages. A method call sends a message to the object. Each object is characterized by its own behavior, which is manifested when receiving specific messages. The sender believes that the recipient knows what to do with the message.

    One additional benefit is polymorphism. Each object that recognizes a particular message has the right to receive it. What happens next depends on the specialization of the object. And this is a very productive thought.

    Unfortunately, we began to be distracted by inheritance - a very effective scheme for reusing code. Its importance is associated with the ability to reduce labor costs when developing a program. Inheritance is built on a similar plan, with the exception of some nuances. We can say that some object or class of objects is similar to some other object or class of objects, but it has some important differences. In a simple situation, everything works great. It should be recalled that modern OOP began with Smalltalk, a programming language for children. As the situation becomes more complicated, inheritance becomes problematic. It gives rise to a strong cohesion of classes. Changing one class may cause failure in those classes that depend on it. Modules from classes are just useless.

    In addition, we observe increased attention to properties, and not to objects. Particular attention is paid to the methods of obtaining (get-methods) and assigning (set-methods) values ​​to each individual property, and in even less successful projects, the properties are open and can be changed without the knowledge of the object. It is possible to introduce a more successful project, where properties are hidden, and methods process transactions, not only dealing with property changes. But this approach is not often applied.

    In addition, there is too much type dependency. Types became a feature of Fortran and later languages, as they were convenient for the compiler creators. Since then, the mythology around types has grown, having acquired extravagant claims that types protect the program from errors. Despite devotion to types, mistakes did not leave everyday practice.

    Types are respected and praised for early detection of miscalculations at the compilation stage. The sooner an oversight is discovered, the lower the cost will be required to eliminate it. But with proper testing of the program, all these miscalculations are detected very quickly. Therefore, type identification errors are classified as low cost.

    Types are not to blame for the appearance of hard-to-detect and costly errors. Their fault is not in the occurrence of problems caused by such errors and requiring some tricks. Types can push us to use obscure, confusing, and dubious programming methods.

    Types are like a weight loss diet. Diet is not accused of returning and weight gain. She is also not considered the cause of suffering or the health problems she caused. Diets give hope that weight will return to a healthy norm and we will continue to eat junk food.

    Classical inheritance allows us to think that we create high-quality programs, while we make more errors and apply more and more inefficient inheritances. If you ignore the negative manifestations, the types appear to be a major victory. The benefits are obvious. But if you look at the types more closely, you will notice that the costs exceed the benefits.

    Constructor


    In chapter 13, we worked with factories - functions that return functions. Now we can do something similar with constructors - functions that return objects that contain functions.

    Let's start by creating counter_constructor, similar to the counter generator. It has two methods, up and down:

    function counter_constructor() {
          let counter = 0;
          function up() {
                counter += 1;
                return counter;
          }
          function down() {
                counter -= 1;
                return counter;
          }
          return Object.freeze({
                up,
                down
          });
    }

    The returned object is frozen. It cannot be damaged or damaged. The object has a state. The variable counter is a private property of the object. You can access it only through methods. And we do not need to use this.

    This is a very important circumstance. The object interface is exclusively methods. He has a very strong shell. We get the best encapsulation. There is no direct access to data. This is a very high-quality modular design.

    A constructor is a function that returns an object. The parameters and constructor variables become private properties of the object. It has no public properties consisting of data. Internal functions become object methods. They turn properties into closed ones. Methods that fall into a frozen object are open.

    Methods must implement transactions. Suppose, for example, that we have a person object. You may need to change the address of the person whose data is stored in it. To do this, you do not need a separate set of functions for changing each individual address element. We need one method that receives an object literal, capable of describing all parts of the address that need to be changed.

    One of the brilliant ideas in JavaScript is the object literal. This is a nice and expressive syntax for clustering information. By creating methods that consume and create data objects, you can reduce the number of methods, thereby increasing the integrity of the object.

    It turns out that we have two types of objects.

    • Hard objects contain only methods. These objects protect the integrity of the data contained in the closure. They provide us with polymorphism and encapsulation.
    • Soft data objects contain only data. They have no behavior. This is just a handy collection that functions can work with.

    It is believed that OOP began by adding procedures to records in the Kobol language, thereby ensuring some kind of behavior. I believe that the combination of data methods and properties was an important step forward, but should not be the last step.

    If the hard object must be converted to a string, the toJSON method must be enabled. Otherwise, JSON.stringify will see it as an empty object, ignoring methods and hidden data (see chapter 22).

    Constructor options


    Once I created a constructor that takes ten arguments. It was very difficult to use, since no one could remember the order of the arguments. Later it was noticed that no one was using the second argument, I wanted to remove it from the list of parameters, but that would break all the already developed code.

    If I were prudent, I would have a constructor that takes one object as a parameter. It is usually taken from an object literal, but can come from other sources, for example, from JSON content.

    This would provide many benefits.

    • Key lines give the code a documented look. The code is easier to read because it tells you what each argument to the caller is.
    • Arguments can be arranged in any order.
    • In the future, you can add new arguments without damaging the existing code.
    • Irrelevant parameters can be ignored.

    Most often, a parameter is used to initialize a private property. This is done as follows:

    function my_little_constructor(spec) {
          let {
               name, mana_cost, colors, type, supertypes, types, subtypes, text,
               flavor, power, toughness, loyalty, timeshifted, hand, life
          } = spec;

    This code creates and initializes 15 private variables using properties with the same names from spec. If spec does not have a corresponding property, a new variable is initialized, which is assigned the value undefined. This allows you to fill in all the missing values ​​with default values.

    Composition


    The vivid expressiveness and effectiveness of JavaScript allows you to create programs in the classical paradigm, although this language does not apply to the classical ones. JavaScript also allows for improvements. We can work with a functional composition. So, instead of adding something as an exception, you can get a little of this and that. The constructor has the following general appearance:

    function my_little_constructor(spec) {
          let {компонент} = spec;
          const повторно_используемый = other_constructor(spec);
          const метод = function () {
                // могут применяться spec, компонент, повторно_используемый, метод
          };
          return Object.freeze({
                метод,
                повторно_используемый.полезный
          });
    }

    Your constructor can call as many other constructors as needed to gain access to state management and the behavior they provide. You can even pass it the exact same spec object. By documenting the spec parameters, we list the properties needed by my_little_constructor and the properties needed by other constructors.

    Sometimes you can simply add the resulting methods to a frozen object. In other cases, we have new methods that invoke the received methods. This ensures that code is reused, similar to inheritance, but without strong cohesion. A function call is the original code reuse scheme, and nothing better has been invented.

    The size


    With this approach to constructing an object, more memory is involved than when using prototypes, since each hard object contains all the methods of the object, and the prototype object contains a link to the prototype containing the methods. Is the difference in memory consumption significant? Comparing the difference with the latest achievements in increasing the amount of memory, we can say: no. We are used to reading memory in kilobytes. And now we consider it in gigabytes. Against this background, the difference is not felt at all.

    The difference can be reduced by improving modularity. The emphasis on transactions, not properties, allows you to reduce the number of methods, and at the same time improve connectivity.

    The classical model is characterized by uniformity. Each object must be an instance of a class. JavaScript removes these restrictions. Not all objects need to comply with such strict rules.

    For example, I believe that it makes no sense for points to be necessarily rigid objects with methods. A point can be a simple container for two or three numbers. Points are passed to functions that are capable of projection, or interpolation, or something else that can be done with points. This can be much more productive than subclassing points to give them special behavior. Let the functions work.

    »More details on the book can be found on the publisher’s website
    » Contents
    » Excerpt

    For Khabrozhiteley a 25% discount on the coupon - JavaScript.

    Upon payment of the paper version of the book, an electronic book is sent by e-mail.

    Also popular now: