ES6的class
ES6中引入的 JavaScript 类(class)实质上是 JavaScript 现有的基于原型的继承的语法糖。类语法并不会为JavaScript引入新的面向对象的继承模型。
在es5中我们是使用构造函数实例化出来一个对象,那么构造函数与普通的函数有什么区别呢?其实没有区别,一般构造函数的函数名称用首字母大写来加以区分。但是es5的这种方式给人的感觉还是不够严谨,于是在es6中就有了class,就是把es5中的function换成了class,有这个单词就代表是个构造函数,然后呢对象还是new出来的,这一点并没有变化。
以下代码定义了一个Point类,他里面有个constructor方法,这就是构造方法;而this关键字则代表实例对象。 Point类除了构造方法,还定义了一个toString方法,定义类的方法的时候,前面不需要加function这个关键字,直接将函数定义放进去就行了 ,另外,方法之间不需要逗号分隔;
//定义类 class Point { constructor (x, y) { this.x =x; this.y =y; } toString () { return `( ${this.x}, ${this.y} )`; } toValue () { return this.x+this.y; } } var p = new Point(1,2); p.toString(); //"(1,2)" p.toValue(); //3
类的使用
class Coder{ name(val){ console.log(val); } } let shuang= new Coder; shuang.name('shuang'); // shuang
类的传参
在es6中的对象的属性都是写在constructor里面,方法都是在原型身上。
以下代码用constructor约定了两个参数,然后用add()方法把参数相加,这个地方跟以前不太一样,所以需要多注意一下。
class Coder{ name(val){ console.log(val); return val; } constructor(a,b){ this.a=a; this.b=b; } add(){ return this.a+this.b; } } let shuang=new Coder(1,2); console.log(shuang.add());
class的继承
class的继承就是用extends
以下代码声明一个shuang的新类,用extends继承了Coder,调用里面的name方法,发现也是可以输出的。
class shuang extends Coder{ } let shuang=new shuang; shuang.name('Angel爽');
提升
函数声明和类声明之间的一个重要区别是函数声明会提升,类声明不会。
首先需要声明一个类,然后在访问它,否则像下面的代码会抛出一个ReferenceError:
let p = new Rectangle(); // ReferenceError class Rectangle {}
类表达式
一个类表达式是定义一个类的另一种方式。类表达式可以是被命名的或匿名的。赋予一个命名类表达式的名称是类的主体的本地名称。类表达式也同样受到类声明中提到的提升问题的限制。类表达式也不会变量提升。
/* 匿名类 */ let Rectangle = class { constructor(height, width) { this.height = height; this.width = width; } }; /* 命名的类 */ let Rectangle = class Rectangle { constructor(height, width) { this.height = height; this.width = width; } };
constructor
constructor方法是类的默认方法,通过new 命令生成对象实例时,自动调用该方法,一个类有且只有一个constructor方法,如果没有显示定义,一个空的constructor方法会被默认添加;如果类包含多个constructor
的方法,则将抛出 一个SyntaxError。
原型方法
class Rectangle { // constructor constructor(height, width) { this.height = height; this.width = width; } // Getter get area() { return this.calcArea() } // Method calcArea() { return this.height * this.width; } } const square = new Rectangle(10, 10); console.log(square.area); // 100
静态方法
static关键字用来定义一个类的一个静态方法。调用静态方法不需要实例化该类,也不能通过一个类实例调用静态方法。
class Point { constructor(x, y) { this.x = x; this.y = y; } static distance(a, b) { const dx = a.x - b.x; const dy = a.y - b.y; return Math.hypot(dx, dy); } } const p1 = new Point(5, 5); const p2 = new Point(10, 10); console.log(Point.distance(p1, p2));
当一个对象调用静态或原型方法时,如果该对象没有“this”值(或“this”作为布尔,字符串,数字,未定义或null) ,那么“this”值在被调用的函数内部将为 undefined
。不会发生自动包装。即使我们以非严格模式编写代码,它的行为也是一样的,因为所有的函数、方法、构造函数、getters或setters都在严格模式下执行。因此如果我们没有指定this的值,this值将为undefined。
class Animal { speak() { return this; } static eat() { return this; } } let obj = new Animal(); obj.speak(); // Animal {} let speak = obj.speak; speak(); // undefined Animal.eat() // class Animal let eat = Animal.eat; eat(); // undefined
如果我们使用传统的基于函数的类来编写上述代码,那么基于调用该函数的“this”值将发生自动装箱。
function Animal() { } Animal.prototype.speak = function() { return this; } Animal.eat = function() { return this; } let obj = new Animal(); let speak = obj.speak; speak(); // global object let eat = Animal.eat; eat(); // global object
extends
extends关键字在类声明或类表达式中用于创建一个类作为另一个类的一个子类:
class Animal { constructor(name) { this.name = name; } speak() { console.log(this.name + ' makes a noise.'); } } class Dog extends Animal { speak() { console.log(this.name + ' barks.'); } } var d = new Dog('Mitzie'); d.speak(); // 'Mitzie barks.'
super
super既能当作方法调用也能当对象使用:
当做方法调用时只有是在子类构造函数constructor方法内;
当做属性使用时,在普通方法中,指向父类的原型对象;
在静态方法中,指向父类:
class Parent { static myMethod(msg) { console.log('static', msg); } myMethod(msg) { console.log('instance', msg); } } class Child extends Parent { static myMethod(msg) { super.myMethod(msg); } myMethod(msg) { super.myMethod(msg); } } Child.myMethod(1); // static 1 var child = new Child(); child.myMethod(2); // instance 2
在子类的静态方法中通过 super 调用父类的方法时,方法内部的 this 指向当前的子类,而不是子类的实例:
class A { constructor() { this.x = 1; } static print() { console.log(this.x); } } class B extends A { constructor() { super(); this.x = 2; } static m() { super.print(); } } B.x = 3; B.m() // 3
调用子类构造函数中的super方法可以继承父类构造函数中东西:
class A { constructor() { this.show(); } } class B extends A { constructor() { super(); } show(){ console.log('实例'); } static show(){ console.log('子类'); } } new B() //输出 '实例' ,new B 时触发了 B 的构造函数,所以触发了 super 方法,即触发了父类 A 的构造函数,此时的 this.show 的 this 指的是子类
在构造函数中super指向父类的原型对象:
class A { p() { return 2; } } class B extends A { constructor() { super(); console.log(super.p()); // 2 此时的super指向父类原型对象,即 A.prototype } } let b = new B(); //2
由于在普通方法中的 super 指向父类的原型对象,所以如果父类上的方法或属性是定义在实例上的,就无法通过 super 调用的:
class A { constructor() { //在构造函数上定义的属性和方法相当于定义在父类实例上的,而不是原型对象上 this.p = 2; } } class B extends A { get m() { return super.p; } } let b = new B(); console.log(b.m) // undefined
class A { constructor() { this.x = 1; } print() { console.log(this.x); } } class B extends A { constructor() { super(); this.x = 2; super.y = 123; // 如果通过super对某个属性赋值,这时super就是this,赋值的属性会变成子类实例的属性。 } m() { super.print(); // 这里调用父类时,父类里的this指向是当前子类 } } let b = new B(); b.m() // 2 console.log(b.y); //123
如果一个类里有一个属性,这个类的构造函数中也有一个同名属性,它的实例上会取哪个值呢?
class Point{ constructor(){ this.x = 1; } x = 2 } var p = new Point(); console.log(p.x); // 1 取构造函数中的值
以上代码的原型链:
class Point{ constructor(){ this.x = 1; } x = 2 } var p = new Point(); console.log(p.__proto__); // 指向p的构造函数的prototype,这个prototype包括它的构造函数本身和它的__proto__
原文:https://www.cnblogs.com/hsgg/p/8108927.html
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Classes