传统创建对象模板的方式

JavaScript 语言中,生成实例对象的传统方法是通过构造函数

  1. //JavaScript 语言中,生成实例对象的传统方法是通过构造函数
  2.  
  3. function Point(x, y) {
  4. this.x = x;
  5. this.y = y;
  6. }
  7.  
  8. Point.prototype.toString = function () {
  9. return '(' + this.x + ', ' + this.y + ')';
  10. };
  11.  
  12. var p = new Point(1, 2);
  13. console.log(p.toString()); //(1, 2)

ES6创建对象模板的方式Class

ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类

基本上,ES6 的class可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已

ES6 的类,完全可以看作构造函数的另一种写法

  1. //ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类
  2. class Point {
  3. constructor(x, y) { //构造方法,而this关键字则代表实例对象
  4. this.x = x;
  5. this.y = y;
  6. }
  7.  
  8. //toString方法。注意,定义“类”的方法的时候,前面不需要加上function这个关键字,直接把函数定义放进去了就可以了。
  9. // 另外,方法之间不需要逗号分隔,加了会报错
  10. toString() {
  11. return '(' + this.x + ', ' + this.y + ')';
  12. }
  13. }
  14.  
  15. const p = new Point(1,2); //类必须使用new调用,否则会报错。这是它跟普通构造函数的一个主要区别,后者不用new也可以执行
  16. console.log(p.toString()); //(1, 2)
  17.  
  18. console.log(typeof Point) //function,类的数据类型就是函数,类本身就指向构造函数
  19. console.log(Point === Point.prototype.constructor) // true

constructor 方法

constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加。

  1. //定义了一个空的类Point,JavaScript 引擎会自动为它添加一个空的constructor方法
  2. class Point {
  3. }
  4.  
  5. // 等同于
  6. class Point {
  7. constructor() {}
  8. }
  1. class Foo {
  2. constructor() { //constructor方法默认返回实例对象(即this),完全可以指定返回另外一个对象
  3. return Object.create(null); //constructor函数返回一个全新的对象,结果导致实例对象不是Foo类的实例
  4. }
  5. }
  6.  
  7. console.log(new Foo() instanceof Foo); // false

类的实例对象

根据类创建实例对象——类必须使用new调用,否则会报错。这是它跟普通构造函数的一个主要区别,后者不用new也可以执行

  1. class Point {
  2. constructor(x, y) {
  3. this.x = x;
  4. this.y = y;
  5. }
  6. toString() {
  7. return '(' + this.x + ', ' + this.y + ')';
  8. }
  9. }
  10.  
  11. const p = new Point(1,2); //类必须使用new调用,否则会报错。这是它跟普通构造函数的一个主要区别,后者不用new也可以执行
  12. console.log(p.toString()); //(1, 2)

实例对象属性与原型对象属性——实例的属性除非显式定义在其本身(即定义在this对象上),否则都是定义在原型上(即定义在class上)

  1. class Point {
  2. constructor(x, y) {
  3. this.x = x;
  4. this.y = y;
  5. }
  6. toString() {
  7. return '(' + this.x + ', ' + this.y + ')';
  8. }
  9. }
  10.  
  11. const p = new Point(1,2);
  12. console.log(p.toString()); //(1, 2)
  13.  
  14. //x和y都是实例对象point自身的属性(因为定义在this变量上)不是原型对象的,所以hasOwnProperty方法返回true
  15. console.log(Point.hasOwnProperty('x')) // true
  16. console.log(Point.hasOwnProperty('y'))// true
  17. console.log(Point.__proto__.hasOwnProperty('y'))// false
  18.  
  19. //toString是原型对象的属性(因为定义在Point类上),不是实例对象的,所以hasOwnProperty方法返回false
  20. console.log(Point.hasOwnProperty('toString')) // false
  21. console.log(Point.__proto__.hasOwnProperty('toString')) // true

通过__proto__属性为‘类’添加方法

  1. class Point {
  2. constructor(x, y) {
  3. this.x = x;
  4. this.y = y;
  5. }
  6. toString() {
  7. return '(' + this.x + ', ' + this.y + ')';
  8. }
  9. }
  10. var p1 = new Point(2,3);
  11. var p2 = new Point(3,2);
  12.  
  13. //p1和p2都是Point的实例,它们的原型都是Point.prototype,所以__proto__属性是相等的
  14. console.log(p1.__proto__ === p2.__proto__) //true
  15.  
  16. //可以通过实例的__proto__属性为“类”添加方法
  17. p1.__proto__.printName = function () {
  18. return 'Oops'
  19. };
  20. console.log(p1.printName()) // "Oops"
  21. console.log(p2.printName()) // "Oops"
  22. var p3 = new Point(4,2);
  23. console.log(p3.printName()) // "Oops"
  24.  
  25. //p1的原型上添加了一个printName方法,由于p1的原型就是p2的原型,因此p2也可以调用这个方法。
  26. // 而且,此后新建的实例p3也可以调用这个方法。
  27. // 这意味着,使用实例的__proto__属性改写原型,必须相当谨慎,不推荐使用,因为这会改变“类”的原始定义,影响到所有实例

立即执行的类的实例

  1. let person = new class {
  2. constructor(name) {
  3. this.name = name;
  4. }
  5. sayName() {
  6. console.log(this.name);
  7. }
  8. }('张三');
  9.  
  10. person.sayName(); // "张三"

使用表达式的形式定义类

  1. const MyClass = class Me {
  2. getClassName() {
  3. return Me.name;
  4. }
  5. };
  6.  
  7. //使用表达式定义了一个类。需要注意的是,这个类的名字是MyClass而不是Me,Me只在 Class 的内部代码可用,指代当前类
  8.  
  9. const p = new MyClass();
  10. console.log(p.getClassName()); //Me,Me只在 Class 内部有定义。

变量提升——类不存在变量提升(hoist), ES6 不会把类的声明提升到代码头部。这种规定的原因与下文要提到的继承有关,必须保证子类在父类之后定义

  1. //Foo类使用在前,定义在后,这样会报错,
  2. // 因为 ES6 不会把类的声明提升到代码头部。这种规定的原因与下文要提到的继承有关,必须保证子类在父类之后定
  3. new Foo(); // ReferenceError: Foo is not defined
  4. class Foo {}

私有方法

私有方法是常见需求,但 ES6 不提供,只能通过变通方法模拟实现

私有方法模拟实现方式一——在命名上加以区别,方法前面的下划线,表示这是一个只限于内部使用的私有方法。但是,这种命名是不保险的,在类的外部,还是可以调用到这个方法

  1. class Widget {
  2. foo (baz) { // 公有方法
  3. this._bar(baz);
  4. }
  5. _bar(baz) { // 私有方法,_bar方法前面的下划线,表示这是一个只限于内部使用的私有方法
  6. return this.snaf = baz;
  7. }
  8. }
  9.  
  10. const w = new Widget();
  11. console.log(w._bar('fffffff')) //fffffff,这种命名是不保险的,在类的外部,还是可以调用到这个方法

私有方法模拟实现方式二————将私有方法移出模块,因为模块内部的所有方法都是对外可见的

  1. class Widget {
  2. foo (baz) {
  3. bar.call(this, baz);
  4. }
  5. }
  6.  
  7. function bar(baz) {
  8. return this.snaf = baz;
  9. }
  10. //foo是公有方法,内部调用了bar.call(this, baz)。这使得bar实际上成为了当前模块的私有方法
  11. const w = new Widget();
  12. console.log(w.foo('fffff'))

静态方法

静态方法在方法名前面添加一个叫static的修饰符来修饰,那么这个方法就是静态方法,可以直接通过类名来调用,不需要通过创建实例对象来调用

  1. class Foo {
  2. static classMethod () {
  3. return 'hello static'
  4. }
  5. }
  6.  
  7. console.log(Foo.classMethod())// hello static

如果通过实例对象来调用静态方法会报错

  1. class Foo {
  2. static classMethod () {
  3. return 'hello static'
  4. }
  5. }
  6. var f = new Foo()
  7. console.log(f.classMethod())//报错:f.classMethod is not a function

Class的继承

Class 可以通过extends关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。

子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类没有自己的this对象,而是继承父类的this对象,然后对其进行加工。如果不调用super方法,子类就得不到this对象

ES6 的继承机制实质是先创造父类的实例对象this(所以必须先调用super方法),然后再用子类的构造函数修改this

如果子类没有定义constructor方法,这个方法会被默认添加,也就是说,不管有没有显式定义,任何一个子类都有constructor方法。

另一个需要注意的地方是,在子类的构造函数中,只有调用super之后,才可以使用this关键字,否则会报错。这是因为子类实例的构建,是基于对父类实例加工,只有super方法才能返回父类实例

  1. class Point {
  2. constructor(x, y) {
  3. this.x = x;
  4. this.y = y;
  5. }
  6. toString() {
  7. return '(' + this.x + ', ' + this.y + ')';
  8. }
  9. }
  10.  
  11. class ColorPoint extends Point {
  12. constructor(x, y, color) {
  13.  
  14. //this.color = color; //在调用super之前,this是不存在的,所以会报错
  15. super(x, y); // 调用父类的constructor(x, y),
  16. this.color = color;
  17. }
  18.  
  19. toString() {
  20. return this.color + ' ' + super.toString(); // 调用父类的toString()
  21. }
  22. }
  23.  
  24. const cp = new ColorPoint(1,2,'red');
  25. console.log(Object.getPrototypeOf(ColorPoint)); //[Function: Point]
  26. console.log(Object.getPrototypeOf(ColorPoint) === Point); //true

通过继承创建的实例对象所属的类的实例——实例对象cp同时是ColorPoint和Point两个类的实例

  1. class Point {
  2. constructor(x, y) {
  3. this.x = x;
  4. this.y = y;
  5. }
  6. toString() {
  7. return '(' + this.x + ', ' + this.y + ')';
  8. }
  9. }
  10.  
  11. class ColorPoint extends Point {
  12. constructor(x, y, color) {
  13.  
  14. //this.color = color; //在调用super之前,this是不存在的,所以会报错
  15. super(x, y); // 调用父类的constructor(x, y),
  16. this.color = color;
  17. }
  18.  
  19. toString() {
  20. return this.color + ' ' + super.toString(); // 调用父类的toString()
  21. }
  22. }
  23.  
  24. const cp = new ColorPoint(1,2,'red');
  25. console.log(cp instanceof Point); // true
  26. console.log(cp instanceof ColorPoint); // true

Object.getPrototypeOf()——Object.getPrototypeOf方法可以用来从子类上获取父类,可以使用这个方法判断,一个类是否继承了另一个类

  1. class Point {
  2. constructor(x, y) {
  3. this.x = x;
  4. this.y = y;
  5. }
  6. toString() {
  7. return '(' + this.x + ', ' + this.y + ')';
  8. }
  9. }
  10.  
  11. class ColorPoint extends Point {
  12. constructor(x, y, color) {
  13.  
  14. //this.color = color; //在调用super之前,this是不存在的,所以会报错
  15. super(x, y); // 调用父类的constructor(x, y),
  16. this.color = color;
  17. }
  18.  
  19. toString() {
  20. return this.color + ' ' + super.toString(); // 调用父类的toString()
  21. }
  22. }
  23.  
  24. const cp = new ColorPoint(1,2,'red');
  25. console.log(Object.getPrototypeOf(ColorPoint)); //[Function: Point]
  26. console.log(Object.getPrototypeOf(ColorPoint) === Point); //true

super关键字

super作为函数调用时,代表父类的构造函数。ES6 要求,子类的构造函数必须执行一次super函数

super虽然代表了父类的构造函数,但是返回的是子类的实例,即super内部的this指的是子类,因此super()在这里相当于父类.prototype.constructor.call(this)。

  1. class Point {
  2. constructor(x, y) {
  3. this.x = x;
  4. this.y = y;
  5. console.log(new.target.name);
  6. }
  7. toString() {
  8. return '(' + this.x + ', ' + this.y + ')';
  9. }
  10. }
  11.  
  12. class ColorPoint extends Point {
  13. constructor(x, y, color) {
  14. //子类的构造函数之中的super(),代表调用父类的构造函数。这是必须的,否则 JavaScript 引擎会报错
  15. super(x, y); // 调用父类的constructor(x, y),
  16. this.color = color;
  17. }
  18.  
  19. toString() {
  20. //super(); //SyntaxError: 'super' keyword unexpected here
         // 作为函数时,super()只能用在子类的构造函数之中,用在其他地方就会报错
  21. return this.color + ' ' + super.toString(); // 调用父类的toString()
  22. }
  23. }
  24.  
  25. const cp = new ColorPoint(1,2,'red'); //ColorPoint,创建子类对象的时候,this指向的是子类
  26. console.log(cp.toString()) // red (1, 2)

super作为对象时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类

由于super指向父类的原型对象,所以定义在父类实例上的方法或属性,是无法通过super调用的

如果属性定义在父类的原型对象上,super就可以取到

通过super调用父类的方法时,super会绑定子类的this

  1. class Point {
  2. constructor() {
  3. this.x = 1;
  4. }
  5. toString() {
  6. return '(' + this.x + ')';
  7. }
  8. }
  9. Point.prototype.z = 200;
  10.  
  11. class ColorPoint extends Point {
  12. constructor() {
  13. super();
  14. this.x = 3
  15. }
  16.  
  17. toString() {
  18. // 调用父类的toString(),通过super调用父类的方法时,super会绑定子类的this
  19. return super.toString();
  20. }
  21. }
  22. const cp = new ColorPoint();
  23. console.log(cp.toString()) //(3)

ES5-ES6-ES7_class类的更多相关文章

  1. [js高手之路] es6系列教程 - new.target属性与es5改造es6的类语法

    es5的构造函数前面如果不用new调用,this指向window,对象的属性就得不到值了,所以以前我们都要在构造函数中通过判断this是否使用了new关键字来确保普通的函数调用方式都能让对象复制到属性 ...

  2. Atitit js版本es5 es6新特性

    Atitit js版本es5 es6新特性 Es5( es5 其实就是adobe action script的标准化)1 es6新特性1 Es5( es5 其实就是adobe action scrip ...

  3. 简述ES5 ES6

    很久前的某一天,一位大神问我,你知道ES6相对于ES5有什么改进吗? 我一脸懵逼的反问,那个啥,啥是ES5.ES6啊. 不得不承认与大神之间的差距,回来深思了这个问题,结合以前的知识,算是有了点眉目. ...

  4. Es6 的类(class)

    首先根据es5的类(原型对象)的基本点做参照. 序号 基本点 es5 >es6 1 实例属性(方法) √ √ 2 原型属性(方法) 或 公共属性(方法) √ √ 3 es5的私有变量 或 私有属 ...

  5. Atitit js es5 es6新特性 attilax总结

    Atitit js es5 es6新特性 attilax总结 1.1. JavaScript发展时间轴:1 1.2. 以下是ES6排名前十的最佳特性列表(排名不分先后):1 1.3. Es6 支持情况 ...

  6. ES6入门——类的概念

    1.Class的基本用法 概述 JavaScript语言的传统方式是通过构造函数,定义并生成新对象.这种写法和传统的面向对象语言差异很大,下面是一个例子: function Point(x, y) { ...

  7. JavaScript es6 class类的理解。

    本着互联网的分享精神,在本篇文章我将会把我对JavaScript  es6 class类的理解分享给大家. JavaScript 类主要是 JavaScript 现有的基于原型的继承的语法糖. 类语法 ...

  8. 【转】React Native中ES5 ES6写法对照

    很多React Native的初学者都被ES6的问题迷惑:各路大神都建议我们直接学习ES6的语法(class Foo extends React.Component),然而网上搜到的很多教程和例子都是 ...

  9. ES6 | class类的基本语法总结

    类和模块的内部,默认就是严格模式,所以不需要使用use strict指定运行模式.只要你的代码写在类或模块之中,就只有严格模式可用. 考虑到未来所有的代码,其实都是运行在模块之中,所以 ES6 实际上 ...

  10. es6 --- class 类的继承使用

    传统的javascript中只有对象,没有类的概念.它是基于原型的面向对象语言.原型对象特点就是将自身的属性共享给新对象.这样的写法相对于其它传统面向对象语言来讲,很有一种独树一帜的感脚!非常容易让人 ...

随机推荐

  1. 写一个ORM框架的第一步(Apache Commons DbUtils)

    新一次的内部提升开始了,如果您想写一个框架从Apache Commons DbUtils开始学习是一种不错的选择,我们先学习应用这个小“框架”再把源代码理解,然后写一个属于自己的ORM框架不是梦. 一 ...

  2. svn迁移后本地地址变更及externals无效的问题

    1.软件: visual SVN Server 2.具体方法: 在打开本地原来SVN check  out的根目录,点右键,tortoiseSVN --> relocate 弹出的对话框中修改s ...

  3. 关于EF中出现FOREIGNKEY约束可能会导致循环或多重级联路径的问题

    ef中,我们创建外键的时候需要注意,否则会出现标题所示问题. 例:有项目表,项目收藏表,用户表 项目表有如下字段:ProjectId,InputPersonId等 项目收藏表有如下字段:Project ...

  4. [Linux] Nginx响应压缩gzip

    压缩和解压缩 .本节介绍如何配置响应的压缩或解压缩以及发送压缩文件. gzip on; .NGINX仅使用MIME类型text / html压缩响应 gzip_types text/plain app ...

  5. 我是这样搞懂一个神奇的BUG

    摘要: 通过分析用户的行为,才想得到为什么会出现这种情况! 前两天在BearyChat收到这样的一个报警消息: 409 ?Conflict ? 平时很少遇到这样的错误,貌似很严重的样子,吓得我赶紧查看 ...

  6. jfinal中excel表格导出

    今天工作中遇到了excel表格导出的操作,还好有写好的模板,不然我也是焦头烂额,下面记录一下excel表格导出的操作步骤,同时用来给正在学习jfinal的小伙伴一些参考和学习. 首先我们需要把表格查询 ...

  7. K8S 部署 ingress-nginx (二) 部署后端为 tomcat

    在上面已经部署了 ingress-nginx, https://www.cnblogs.com/klvchen/p/9903480.html 创建 service 和 pods cd vi tomca ...

  8. Linux 安装 jdk8

    切换目录 cd /usr 创建目录 mkdir java cd java 下载 jdk rz 或者 ftp 都行,只要能成功上传 解压 tar zxvf jdk-8u181-linux-x64.tar ...

  9. 17.Odoo产品分析 (二) – 商业板块(10) – 电子商务(1)

    查看Odoo产品分析系列--目录 安装电子商务模块 1. 主页 点击"商店"菜单:  2. 添加商品 在odoo中,你不用进入"销售"模块,再进入产品列表添加产 ...

  10. android开发——Android开发中的47个小知识

    1.判断sd卡是否存在  boolean sdCardExist = Environment.getExternalStorageState().equals(android.os.Environme ...