在JS里面,它其实是没有类的概念的,咱们若是想要实现一个面向对象的一个编程,那么咱们只可以借助构造函数,可是构造函数有着自身的缺点,首先第一点它的写法不清晰,咱们须要继承的话就须要手动的去操做prototype;其次它面向对象的思想也不是很强烈,因此在ES6它封装了一层构造函数的语法糖,就叫Class,封装后有了一个更清晰的写法,并且更加像一个面向对象编程语言。编程
在ES5中:编程语言
// ES5 的构造函数 function Position(x, y) { this.x = x; this.y = y; } const position = new Position(1, 1);
在ES6中:编辑器
在ES6中,首先有一个class修饰符,class将告诉编辑器声明了一个名叫Position的class。 class是构造函数的语法糖,咱们怎么来看呢?咱们能够看一下这个构造函数它的prototype的constructor是不是自己,若是是自己那就说明class的行为跟构造函数是保持一致的。函数
class Position { constructor(x, y) { this.x = x; this.y = y; } } Position === Position.prototype.constructor; /* true */ const position = new Position(1, 1);
在ES5里面,好比说咱们在构造函数里面定义了一些方法,那么咱们在实例里面是有这一个方法的,可是ES6不同。在Class里面,全部的方法所有都定义到了prototype属性上面,也就是除了this.以外的其余都定义在了prototype属性上面去。ui
class Position { constructor(x, y) { this.x = x; this.y = y; } scan() { console.log(this.x, this.y); } } Position.prototype; /* { constructor: class Position scan: ƒ scan() __proto__: Object } */
内部的方法都到prototype上面去,可是这个prototype的方法倒是不可枚举的,也就是说它的枚举属性是false。 若是这个时候咱们想要获得这个枚举,想要知道它有哪些方法的话,咱们就须要使用到getOwnPropertyNames。this
class Position { constructor(x, y) { this.x = x; this.y = y; } scan() { console.log(this.x, this.y); } } Object.keys(Position.prototype); /* [] */ Object.getOwnPropertyNames(Position.prototype); /* ["constructor", "scan"] */
class Position { constructor(x, y) { this.x = x; this.y = y; } scan() { console.log(this.x, this.y); } } const position = new Position(1, 1); // 调用自身扫描 position.scan(); // 1 1
解构出来再去调用this指向就不一致了,会出问题报错:prototype
const { scan } = position; scan(); /* Cannot read property 'x' of undefined */
对于上面的this在解构后调用出现的指向问题出错咱们能够有两种解决办法: 第一种方法,借助于箭头函数: 箭头函数在定义时this就已经指定了,也就是它什么定义的和在哪里定义的,它的this就是什么。rest
class Position { constructor(x, y) { this.x = x; this.y = y; } // 将scan()函数改成一个箭头函数,this永远指向定义时的位置 scan = () => { console.log(this.x, this.y); } } const position = new Position(1, 1); const { scan } = position; scan(); /* 1 1 */
第二种方法,手动的bind:code
class Position { constructor(x, y) { this.x = x; this.y = y; this.scan = this.scan.bind(this); } scan() { console.log(this.x, this.y); } } const position = new Position(1, 1); const { scan } = position; scan(); /* 1 1 */
constructor 构造器,首先它是一个required,也就是它是一个必须的,若是咱们没有传入它,那么就会默认生成一个空的一个constructor。对象
new命令使用时调用一次constructor
class Position { constructor(x, y) { this.x = x; this.y = y; console.log('constructor'); } } const position = new Position(1, 1); /* constructor */
默认返回实例对象
class Position { constructor(x, y) { return Object.create({ name: 'Eric' }); } } const position = new Position(1, 1); 控制台输入:position // 获得一个空对象:{} position instanceof Position; /* false */
实例的属性除this以外所有定义在原型对象上
hasOwnProperty();
get是拦截它的取,set是拦截它的存,getter和setter必须是成对出现的,并且咱们设置时须要设置getter和setter应该是哪个,应该是当前的对象不存在的,不然就会报一个堆栈溢出,由于它会有一个循环调用的问题。
class Position { constructor(x, y) { this.x = x; this.y = y; } get z() { return this.x; } set z(value) { this.x = 2; } } const position = new Position(1, 1);
static字面意思理解就是静态,它不能被实例继承,也就是说在实例里面是不能调用静态方法的,它只能经过类自己来调用,这时这个this指向的是当前class自己。
class Position { constructor(x, y) { this.x = x; this.y = y; this.z = 100; } static scan() { console.log(this); } } const position = new Position(1, 1); position.scan(); /* position.scan is not a function */ Position.scan(); /* class Position { constructor(x, y) { this.x = x; this.y = y; this.z = 100; } static scan() { console.log(this); } } */
在以前的代码里面属性 x 和 y 都是定义在constructor内部的,这样其实是比较清晰的可是有一个提案:把属性所有放在构造函数的最顶层,咱们能够直接在构造函数的内部来定义一些变量,固然这个变量是不须要加修饰符的,这时候就默认加到了this的对象上面去。
class Count { x = 0; reduce() { this.x = this.x - 1; console.log(this.x); } increment() { this.x = this.x + 1; console.log(this.x); } } const count = new Count(); count.increment(); count.increment(); count.increment(); count.increment(); // 1 2 3 4
class Point { constructor(x, y) { this.x = x; this.y = y; } scan = () => { console.log(this.x, this.y); } } // SubPoint 继承了 Point class SubPoint extends Point { } const subPoint = new SubPoint(1, 1); subPoint; /* { scan: () => { console.log(this.x, this.y); } x: 1 y: 1 __proto__: Point } */
class Point { constructor(x, y) { this.x = x; this.y = y; } scan = () => { console.log(this.x, this.y); } } class SubPoint extends Point { constructor(...rest) { // super(...rest) } } const subPoint = new SubPoint(1, 1); /* Must call super constructor in derived class before accessing 'this' or returning from derived constructor */
class Point { constructor(x, y) { this.x = x; this.y = y; } scan = () => { console.log(this.x, this.y); } } class SubPoint extends Point { constructor(...rest) { super(...rest) } } const subPoint = new SubPoint(1, 1); Object.getPrototypeOf(subPoint); /* Point { constructor: class SubPoint __proto__: Object } */