《understanding ESMAScript 6》之类 ( introducting javascript classes)

类 introducing javascript classes

es5 中的类结构 class-like structures in ecmascript5

// 创建一个构造函数
function PersonType(name) {
  this.name = name;
}

// 在构造函数的原型上添加方法
PersonType.prototype.sayName = function() {
  console.log(this.name);
};

// 创建一个PersonType的实例person
let person = new PersonType("kk");

// 沿着原型链向上查找,直到找到sayName方法
person.sayName(); // 输出 kk

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

类声明 class declarations

一个基本的类声明 a basic class declaration
class PersonClass {
  // 构造函数
  constructor(name) {
    this.name = name;
  }

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

let person = new PersonClass("kk");

person.sayName(); // 输出 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
  1. 类声明和函数不同,他们是不会被提升的,当执行流作用到类声明之前类会存在于暂存性死区中。
  2. 类声明的代码自动运行在严格模式下,同时没有任何办法可以手动的切换到非严格模式下。
  3. 类的所有方法都是不可枚举的。
  4. 不能再类的内部重写类名。

所以,PersonClass 声明等同如下未使用类语法的代码:

// 完全等效于PersonClass
let PersonType2 = function() {
  "use strict";
  const PersonType2 = function(name) {
    // 确定方法是被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() {
      // 检查new.target以确保类是由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 expressions

类和函数都有两种存在形式:声明和表达式。

一个基本的类表达式 basic class expression
// anonymous class expressions
let PersonClass = class {
  constructor(name) {
    this.name = name;
  }
  // ....
};

console.log(PersonClass.name); // ''
具名表达式 named class expressions
let PersonClass = class PersonClass1 {
  constructor(name) {
    this.name = name;
  }
  // 相当于
  // const PersonClass1 = function (name) {
  //   if(typeof new.target == 'undefined'){
  //     throw new Error('err');
  //   }
  //   this.name = name;
  // }
  // return PersonClass1;
};

再来看一种现象:

console.log(typeof PersonClass1); // "undefined"
/**
 * 原因: PersonClass1只能在类的内部使用,外部是访问不到的
 */

总结:在该匿名类表达式中,PersonClass.name 是个空的字符串。如果使用了类声明,那么 PersonClass.name 的值为 “PersonClass”。

注:译者在这里测试发现 Edge,Chrome 及 Opera 的匿名表达式都会返回类名,只有 FireFox 返回空字符串

一等公民,一等类

在编程中,如果某些东西能够作为值使用,那么他被成为一等公民。js 中的函数就是一等公民。又叫做一等函数。

// 使用 1. 作为参数

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

obj.sayHi(); // 'hi'
// 2. 立即执行
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

如果不使用类的写法,感受一下 完全等效于 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;
})();

动态命名 computed member names

// 例子1  动态命名方法
let className = "sayname";

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

let person = new PersonClass("jj");
person.sayName(); // 'jj'
// 例子2 动态命名属性
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 methods

该方法返回了一个经过硬编码的迭代器,当你想要一个具有集合性质的对象并能轻松迭代包含值得时候,生成器方法相当有用。

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);
}
// 依次输出1 2 3
设置类的默认迭代器

通过给类的 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);
}

// 输出:
// 1
// 2
// 3

静态成员 static members

es5 中的实现:

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

// 静态方法 create
PersonType.create = function(name) {
  return new PersonType(name);
};

// 实例方法 sayName
PersonType.prototype.sayName = function() {
  console.log(this.name);
};

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

es6 中的实现:

class PersonClass {
  // 等效于 PersonType 构造函数
  constructor(name) {
    this.name = name;
  }

  // 等效于 PersonType.prototype.sayName
  sayName() {
    console.log(this.name);
  }

  // 等效于 PersonType.create
  static create(name) {
    return new PersonClass(name);
  }
}

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

静态成员不能被实例访问。你必须通过类本身来使用它们。

类的继承

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

es6 中的实现:

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

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

class Square extends Rectangle {
  constructor(length) {
    // 等效于 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
派生类使用父类的静态方法
class A {
  constructor(width, height) {
    this.width = width;
    this.height = height;
  }
  getArea() {
    return this.width * this.height;
  }

  // 静态成员 静态方法
  static create(width, height) {
    return width * height;
  }
}

class B extends A {
  constructor(length) {
    // 使用super和指定的参数来调用A的构造函数
    super(length, length);
  }

  getArea() {
    // 调用父类中的方法 使用super.方法名
    return super.getArea();
  }
}
let b = new B(3);
b.getArea(); // 9

// 派生类可以使用父类的静态方法
B.create(2, 3); // 6

注意: 只有派生类本身可以使用他的静态方法,派生类的实例不可以使用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值