传统创建对象模板的方式

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

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

function Point(x, y) {
this.x = x;
this.y = y;
} Point.prototype.toString = function () {
return '(' + this.x + ', ' + this.y + ')';
}; var p = new Point(1, 2);
console.log(p.toString()); //(1, 2)

ES6创建对象模板的方式Class

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

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

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

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

constructor 方法

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

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

类的实例对象

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

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

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

class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
} const p = new Point(1,2);
console.log(p.toString()); //(1, 2) //x和y都是实例对象point自身的属性(因为定义在this变量上)不是原型对象的,所以hasOwnProperty方法返回true
console.log(Point.hasOwnProperty('x')) // true
console.log(Point.hasOwnProperty('y'))// true
console.log(Point.__proto__.hasOwnProperty('y'))// false //toString是原型对象的属性(因为定义在Point类上),不是实例对象的,所以hasOwnProperty方法返回false
console.log(Point.hasOwnProperty('toString')) // false
console.log(Point.__proto__.hasOwnProperty('toString')) // true

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

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

立即执行的类的实例

let person = new class {
constructor(name) {
this.name = name;
}
sayName() {
console.log(this.name);
}
}('张三'); person.sayName(); // "张三"

使用表达式的形式定义类

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

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

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

私有方法

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

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

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

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

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

静态方法

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

class Foo {
static classMethod () {
return 'hello static'
}
} console.log(Foo.classMethod())// hello static

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

class Foo {
static classMethod () {
return 'hello static'
}
}
var f = new Foo()
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方法才能返回父类实例

class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
} class ColorPoint extends Point {
constructor(x, y, color) { //this.color = color; //在调用super之前,this是不存在的,所以会报错
super(x, y); // 调用父类的constructor(x, y),
this.color = color;
} toString() {
return this.color + ' ' + super.toString(); // 调用父类的toString()
}
} const cp = new ColorPoint(1,2,'red');
console.log(Object.getPrototypeOf(ColorPoint)); //[Function: Point]
console.log(Object.getPrototypeOf(ColorPoint) === Point); //true

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

class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
} class ColorPoint extends Point {
constructor(x, y, color) { //this.color = color; //在调用super之前,this是不存在的,所以会报错
super(x, y); // 调用父类的constructor(x, y),
this.color = color;
} toString() {
return this.color + ' ' + super.toString(); // 调用父类的toString()
}
} const cp = new ColorPoint(1,2,'red');
console.log(cp instanceof Point); // true
console.log(cp instanceof ColorPoint); // true

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

class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
} class ColorPoint extends Point {
constructor(x, y, color) { //this.color = color; //在调用super之前,this是不存在的,所以会报错
super(x, y); // 调用父类的constructor(x, y),
this.color = color;
} toString() {
return this.color + ' ' + super.toString(); // 调用父类的toString()
}
} const cp = new ColorPoint(1,2,'red');
console.log(Object.getPrototypeOf(ColorPoint)); //[Function: Point]
console.log(Object.getPrototypeOf(ColorPoint) === Point); //true

super关键字

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

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

class Point {
constructor(x, y) {
this.x = x;
this.y = y;
console.log(new.target.name);
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
} class ColorPoint extends Point {
constructor(x, y, color) {
//子类的构造函数之中的super(),代表调用父类的构造函数。这是必须的,否则 JavaScript 引擎会报错
super(x, y); // 调用父类的constructor(x, y),
this.color = color;
} toString() {
//super(); //SyntaxError: 'super' keyword unexpected here
     // 作为函数时,super()只能用在子类的构造函数之中,用在其他地方就会报错
return this.color + ' ' + super.toString(); // 调用父类的toString()
}
} const cp = new ColorPoint(1,2,'red'); //ColorPoint,创建子类对象的时候,this指向的是子类
console.log(cp.toString()) // red (1, 2)

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

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

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

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

class Point {
constructor() {
this.x = 1;
}
toString() {
return '(' + this.x + ')';
}
}
Point.prototype.z = 200; class ColorPoint extends Point {
constructor() {
super();
this.x = 3
} toString() {
// 调用父类的toString(),通过super调用父类的方法时,super会绑定子类的this
return super.toString();
}
}
const cp = new ColorPoint();
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. echarts地图 禁止高亮显示(转载)

    转载来源: https://blog.csdn.net/john1337/article/details/54666759 案例一:在实际应用中希望(没有数据的省会)禁止高亮颜色,解决方案: mapC ...

  2. Android RecyclerView预览item

    参考: Android Tools Attributes listItem 和 Sample Data 的用法 笔记 tools:text TextView可以实现预览,不影响实际的效果 例如: to ...

  3. Mybatis框架基础支持层——解析器模块(2)

    解析器模块,核心类XPathParser /** * 封装了用于xml解析的类XPath.Document和EntityResolver */ public class XPathParser { / ...

  4. C# 利用ReportViewer生成报表

    本文主要是利用微软自带的控件ReportViewer进行报表设计的小例子,仅供学习分享使用,如有不足之处,还请指正. 涉及知识点: ReportViewer :位于Microsoft.Reportin ...

  5. Last Day in Autodesk

    今天是我的最后一天在Autodesk上海了,以后将不再折腾那么大的软件了,还是回到CG开发中捣鼓短小精悍的东西——我还将继续整理开源CG生产工具. Today is my last day in Au ...

  6. Markdown:常用语法

    1.标题 说明:一共可以6级标题,几级几个# 一级标题 #一级标题 2.代码 用前后扩上 Hello World! 3.代码块 用前后扩上 Hello World! 4.加粗 加粗了 **加粗了** ...

  7. python base64 decode incorrect padding错误解决方法

    个人觉得原因应该是不同的语言/base64库编码规则不太统一的问题. python中base64串的长度需为4的整数倍,故对长度不为4整数倍的base64串需要用"='补足 如下代码: da ...

  8. Linux下图形数据库Neo4j单机安装

    Neo4j数据库简介 Neo4j 是一个NoSQL的图形数据库(Graph Database).Neo4j使用图(graph)相关的概念来描述数据模型,把数据保存为图中的节点以及节点之间的关系.很多应 ...

  9. A problem has been detected and windows has been shut down to prevent damage to your computer.他么啥意思?看这里!【蓝屏】

    A problem has been detected and windows has been shut down to prevent damage to your computer.  检测到问 ...

  10. 最简单的java浏览器

    /** * Created by Admin on 2017/3/27. */ import java.awt.BorderLayout; import java.awt.Container; imp ...