understanding ESMAScript 6 (introducting javascript classes)

Keywords: Javascript Firefox Programming

Class introducing javascript classes

class-like structures in ecmascript5

// Create a constructor
function PersonType(name) {
  this.name = name;
}

// Adding methods to the prototype of constructor
PersonType.prototype.sayName = function() {
  console.log(this.name);
};

// Create an instance person of PersonType
let person = new PersonType("kk");

// Look up the prototype chain until you find the sayName method
person.sayName(); // Output kk

console.log(person instanceof personType); // true
console.log(person instanceof Object); // true

class declarations

a basic class declaration

class PersonClass {
  // Constructor
  constructor(name) {
    this.name = name;
  }

  sayName() {
    console.log(this.name);
  }
}

let person = new PersonClass("kk");

person.sayName(); // Output kk

console.log(person instanceof PersonClass); // true
console.log(person instanceof Object); // true

console.log(typeof PersonClass); // function
console.log(typeof PersonClass.prototype.sayName); // function

why use the class syntax with the class declaration

  1. Class declarations, unlike functions, are not promoted, and classes exist in temporary dead zones before execution streams are applied to class declarations.
  2. Class declaration code automatically runs in strict mode, and there is no way to manually switch to non-strict mode.
  3. All methods of a class are not enumerable.
  4. Class names cannot be overridden internally.

Therefore, the PersonClass declaration is equivalent to the following code that does not use class syntax:

// Fully equivalent to PersonClass
let PersonType2 = function() {
  "use strict";
  const PersonType2 = function(name) {
    // Determine that the method is called by new
    if (typeof new.target === "undefined") {
      throw new Error("Constructor must be called with new.");
    }
    this.name = name;
  };

  Object.defineProperties(PersonType2.prototype, "sayName", {
    value: function() {
      // Check new.target to ensure that the class is called by new.
      if (typeof new.target === "undefined") {
        throw new Error("Constructor must be called with new.");
      }
      console.log(this.name);
    },
    writable: true,
    configurable: true,
    enumerable: false
  });
  return PersonType2;
};

Class expression class expressions

Classes and functions exist in two forms: declarations and expressions.

A basic class expression

// anonymous class expressions
let PersonClass = class {
  constructor(name) {
    this.name = name;
  }
  // ....
};

console.log(PersonClass.name); // ''

Named expression named class expressions

let PersonClass = class PersonClass1 {
  constructor(name) {
    this.name = name;
  }
  // Amount to
  // const PersonClass1 = function (name) {
  //   if(typeof new.target == 'undefined'){
  //     throw new Error('err');
  //   }
  //   this.name = name;
  // }
  // return PersonClass1;
};

Let's look at another phenomenon:

console.log(typeof PersonClass1); // "undefined"
/**
 * Reason: PersonClass1 can only be used internally, but not externally.
 */

Summary: In this anonymous class expression, PersonClass.name is an empty string. If a class declaration is used, the value of PersonClass.name is "PersonClass".

Note: The translator tests here and finds that the anonymous expressions of Edge, Chrome and Opera all return class names, while FireFox only returns empty strings.

First Class Citizens, First Class Citizens

In programming, if something can be used as a value, he is made a first-class citizen. The function in js is first-class citizen. It is also called first-class function.

// Use 1. as a parameter

function createObj(classDef) {
  return new classDef();
}
let obj = createObj(
  class {
    sayHi() {
      console.log("hi");
    }
  }
);

obj.sayHi(); // 'hi'
// 2. Immediate implementation
let person = new class {
  constructor(name) {
    this.name = name;
  }
  sayName() {
    console.log(this === person); // true

    console.log(this.name);
  }
}("kk");

person.sayName(); // 'kk'

accessor properties

class CreateHtmlElement {
  constructor(ele) {
    this.ele = ele;
  }
  get html() {
    return this.ele.innerHtml;
  }
  set html(value) {
    this.ele.innerHtml = value;
  }
}

let descriptor = Object.getOwnPropertyDescriptor(
  CreateHtmlElement.prototype,
  "html"
);

console.log("get" in descriptor); // true
console.log("set" in descriptor); // true
console.log(descriptor.enumerable); // false

If you don't use class notation, feel that it's exactly the same as CreateHtmlElement

let CreateHtmlElement = (function() {
  "use strict";
  const CreateHtmlElement = function(ele) {
    if (new.target == "undefined") {
      throw new Error("err!");
    }
    this.ele = ele;
  };
  Object.getOwnPropertyDescriptor(CreateHtmlElement.prototype, "html", {
    writable: true,
    configurable: true,
    enumerable: false,
    get: function() {
      return this.ele.innerHtml;
    },
    set: function(value) {
      this.ele.innerHtml = value;
    }
  });
  return CreateHtmlElement;
})();

Dynamic naming computed member names

// Example 1 Dynamic naming method
let className = "sayname";

class PersonClass {
  constructor(name) {
    this.name = name;
  }
  [className]() {
    console.log(this.name);
  }
}

let person = new PersonClass("jj");
person.sayName(); // 'jj'
// Example 2 Dynamic naming attributes
let animalName = "animalName";
class Animal {
  constructor(animalKind) {
    this.animalKind = animalKind;
  }

  set [animalName](name) {
    this.animalKind.animalName = name;
  }

  get [animalName]() {
    return this.animalKind.animalName;
  }
}

let dog = new Animal("dog");
dog.set("huahua");
dog.get(); // 'huahua'

Generator method generator methods

This method returns a hard-coded iterator, which is quite useful when you want an object of set nature and can easily iterate over its inclusion value.

class MyClass {
  *createIterator() {
    yield 1;
    yield 2;
    yield 3;
  }
}

let myclass = new MyClass();

let iterator = myclass.createIterator();

for (const i of iterator) {
  console.log(i);
}
// Output 123 in turn

Set the default iterator for the class

Define the default iterator of the class by defining a generator method for the class's Symbol.iterator.

class Collection {
  constructor() {
    this.items = [];
  }

  *[Symbol.iterator]() {
    yield* this.items.values();
  }
}

var collection = new Collection();
collection.items.push(1);
collection.items.push(2);
collection.items.push(3);

for (let x of collection) {
  console.log(x);
}

// Output:
// 1
// 2
// 3

static members

Implementation in es5:

function PersonType(name) {
  this.name = name;
}

// Static method create
PersonType.create = function(name) {
  return new PersonType(name);
};

// Example method sayName
PersonType.prototype.sayName = function() {
  console.log(this.name);
};

var person = PersonType.create("Nicholas");

The implementation of es6:

class PersonClass {
  // Equivalent to PersonType constructor
  constructor(name) {
    this.name = name;
  }

  // Equivalent to PersonType.prototype.sayName
  sayName() {
    console.log(this.name);
  }

  // Equivalent to PersonType.create
  static create(name) {
    return new PersonClass(name);
  }
}

let person = PersonClass.create("Nicholas");

Static members cannot be accessed by instances. You have to use them through the classes themselves.

Class inheritance

Implementation in es5:

function Rectangle(length, width) {
  this.length = length;
  this.width = width;
}

Rectangle.prototype.getArea = function() {
  return this.length * this.width;
};

function Square(length) {
  Rectangle.call(this, length, length);
}

Square.prototype = Object.create(Rectangle.prototype, {
  constructor: {
    value: Square,
    enumerable: true,
    writable: true,
    configurable: true
  }
});

var square = new Square(3);

console.log(square.getArea()); // 9
console.log(square instanceof Square); // true
console.log(square instanceof Rectangle); // true

The implementation of es6:

class Rectangle {
  constructor(length, width) {
    this.length = length;
    this.width = width;
  }

  getArea() {
    return this.length * this.width;
  }
}

class Square extends Rectangle {
  constructor(length) {
    // Equivalent to Rectangle.call(this, length, length)
    super(length, length);
  }
}

var square = new Square(3);

console.log(square.getArea()); // 9
console.log(square instanceof Square); // true
console.log(square instanceof Rectangle); // true

Derived classes use static methods of parent classes

class A {
  constructor(width, height) {
    this.width = width;
    this.height = height;
  }
  getArea() {
    return this.width * this.height;
  }

  // Static member static method
  static create(width, height) {
    return width * height;
  }
}

class B extends A {
  constructor(length) {
    // Call the constructor of A with super and specified parameters
    super(length, length);
  }

  getArea() {
    // Calling methods in the parent class uses the super. method name
    return super.getArea();
  }
}
let b = new B(3);
b.getArea(); // 9

// Derived classes can use static methods of parent classes
B.create(2, 3); // 6

Note: Only the derived class itself can use its static method, and instances of the derived class cannot use it.

Posted by jrough on Thu, 27 Dec 2018 08:33:07 -0800