ES5中使用构造函数定义类
ES6之前定义一个类,都是通过定义构造函数实现:
1 2 3 4 5 6 7 8 9
| function Rectangle(x,y){ this.x = x; this.y = y; } Rectangle.prototype.area = function(){ return x * y; } console.log(new Rectangle(1, 2));
|
为了和普通函数区分开来,构造函数首字母一般大写。
如果把构造函数当做普通函数调用了,没有new指令分配内存,函数中的this
就会指向全局变量:
1 2 3 4
| var r = Rectangle(1, 2); console.log(r); console.log(window.x) console.log(window.y)
|
1. 防止构造函数被当成普通函数调用
有两种方式可以防止构造函数被当成普通函数调用。
在构造函数中使用严格模式,构造函数第一行加上use strict
1 2 3 4 5 6 7 8
| function Rectangle(x,y) { 'use strict' this.x = x; this.y = y; }
Rectangle();
|
在构造函数中判断是否用了new
指令,如果发现没有new
,则内部创建一个对象返回
1 2 3 4 5 6 7 8 9
| function Rectangle(x,y) { if(!this instanceof Rectangle) { return new Rectangle(x,y); } this.x = x; this.y = y; }
|
2. 构造函数中的return
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| function Rectangle(x,y) { this.x = x; this.y = y; return {}; } console.log(new Rectangle(1,2));
function Rectangle(x,y) { this.x = x; this.y = y; return null; } console.log(new Rectangle(1,2));
function Rectangle(x,y) { this.x = x; this.y = y; return undefined; } console.log(new Rectangle(1,2));
function Rectangle(x,y) { this.x = x; this.y = y; return 10; } console.log(new Rectangle(1,2));
function Rectangle(x,y) { this.x = x; this.y = y; return 'Rectangle'; } console.log(new Rectangle(1,2));
|
如果构造函数中返回的是对象类型,那么new
指令会返回这个对象;如果是基本数据类型、null
或undefined
则返回new出来的对象。
3. new指令的执行原理
执行new指令,具体可以分为以下几步:
- 创建空对象initObj
- 将initObj的原型指向构造函数的prototype
- 将initObj绑定到构造函数的this变量中
- 开始执行构造函数
用代码表示上面的流程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| function _new_( _constructor_, ...params) { var initObj = Object.create(_constructor_.prototype); var result = _constructor_.apply(initObj, params); return (typeof result === 'object' && result != null) ? result : initObj; } console.log(_new_(Rectangle,1,2));
function _new_( _constructor_, ...params) { var initObj = {}; initObj.__proto__ = _constructor_.prototype; var result = _constructor_.apply(initObj, params); return (typeof result === 'object' && result != null) ? result : initObj; } console.log(_new_(Rectangle,1,2));
|
测试prototype:
1 2 3 4 5 6 7 8 9 10
| function Rectangle(x,y){ 'use strict' this.x = x; this.y = y; } var r = new Rectangle(1,2); r.__proto__ == Rectangle.prototype; r.__proto__ == Object.prototype; var obj = new Object(); obj.__proto__ == Object.prototype;
|
ES6中使用class定义类
1 2 3 4 5 6 7
| class Rectangle { constructor(height, width) { this.height = height; this.width = width; } } console.log(new Rectangle());
|
1. class定义的类没有变量提升
1 2 3 4 5 6 7 8 9 10
| var p = new Rectangle();
class Rectangle {}
new Rectangle();
function Rectangle() {}
|
2. 匿名类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| var Rectangle = class { constructor(height, width) { this.height = height; this.width = width; } }; console.log(new Rectangle(1,2));
var Rectangle = class Rectangle { constructor(height, width) { this.height = height; this.width = width; } }; console.log(new Rectangle(1,2));
|
3. 一个类不允许有多个构造函数
1 2 3 4 5 6 7 8 9 10 11 12 13
| class Rectangle { constructor() { this.height = 1; this.width = 2; } constructor(height, width) { this.height = height; this.width = width; } }
|
可以使用默认参数的方式提供默认值:
1 2 3 4 5 6 7
| class Rectangle { constructor(height=1, width=2) { this.height = height; this.width = width; } }
|
4. 类中定义方法
1 2 3 4 5 6 7 8 9 10
| class Rectangle { constructor(height, width) { this.height = height; this.width = width; } area(){ return this.width * this.height; } }
|
5. 定义getter/setter方法
1 2 3 4 5 6 7 8 9 10 11 12 13
| class Rectangle { constructor(height, width) { this.height = height; this.width = width; } get area(){ return this.width * this.height; } } var r = new Rectangle(1,2); console.log(r.area);
|
6. 定义类静态方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| class Point { constructor(x, y) { this.x = x; this.y = y; }
move(deltaX,deltaY){ this.x = this.x + deltaX; this.y = this.y + deltaY; }
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));
Point.prototype.move = function(deltaX,deltaY){ this.x = this.x + deltaX; this.y = this.y + deltaY; } Point.distance = function() { const dx = a.x - b.x; const dy = a.y - b.y;
return Math.hypot(dx, dy); }
|
7. 类的继承
和原型链继承本质上是一样的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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();
|
8. 使用super
关键字调用父类方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| class Animal { constructor(name) { this.name = name; }
speak() { console.log(this.name + ' makes a noise.'); } }
class Dog extends Animal { speak() { super.speak(); console.log(this.name + ' barks.'); } }
var d = new Dog('Mitzie'); d.speak();
|
参考:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get
http://javascript.ruanyifeng.com/oop/basic.html
本作品采用 知识共享署名 4.0 国际许可协议 进行许可。
转载时请注明原文链接:https://blog.hufeifei.cn/2018/03/WebFront/ES6%E8%AF%AD%E6%B3%95%E5%AD%A6%E4%B9%A0-OOP%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%BC%96%E7%A8%8B/