NodeJS对象模型
四、JS对象模型
JavaScript 是一种基于原型(Prototype
)的面向对象语言,而不是基于类对象的面向对象语言
C++、JAVA 有类Class
和实例intance
的概念,类是一类事物的抽象,而实例是类的实体
JS 是基于原型的语言,它只有原型对象的概念,原型对象就是一个模板,新的对象从这个模板构建从而获取最初的属性。任何对象都可以在运行时动态增加属性,而且任何对象都可以作为另一个对象的原型,这样后者就可以共享前者的属性。
定义类
字面声明方式定义
var obj = {
name: 'dengyou',
func: (x, y) => x+y,
}
console.log(obj.name)
console.log(obj.func)
console.log((obj.func)(10, 20))
输出结果:
dengyou
[Function: func]
30
ES6之前-构造器
- 定义一个函数(构造器对象),函数首字母大写
- 使用this定义属性
- 使用new关键字和构造器创建新对象
// 定义一个构造器
function Point(x, y) {
this.x = x;
this.y = y;
this.show = () => console.log(this, this.x, this.y)
console.log("Point ~~~~~~~~~~~~~")
}
// 创建对象
p1 = new Point(4, 5);
console.log(p1);
console.log(p1.x);
p1.show();
输出结果:
Point ~~~~~~~~~~~~~
Point { x: 4, y: 5, show: [Function] }
4
Point { x: 4, y: 5, show: [Function] } 4 5
- 继承
// 定义一个构造器
function Point(x, y) {
this.x = x;
this.y = y;
this.show = () => console.log(this, this.x, this.y)
console.log("Point ~~~~~~~~~~~~~")
}
// 继承
function Point3D(x,y,z) {
Point.call(this, x, y);
this.z = z;
this.show = () => console.log(this, this.x, this.y, this.z)
console.log("Point3D ~~~~~~~~~~")
}
p2 = new Point3D(4, 5, 6)
console.log(p2)
p2.show()
输出结果:
Point ~~~~~~~~~~~~~
Point3D ~~~~~~~~~~
Point3D { x: 4, y: 5, show: [Function], z: 6 }
Point3D { x: 4, y: 5, show: [Function], z: 6 } 4 5 6
class 关键字构建
从ES6开始,提供class关键字,使得创建对象更加简单,清晰
- 类定义使用关键字。创建的本质还是函数
- 一个类只能拥有一个名为
constructor
的构造方法 - 继承使用
extends
关键字 - 一个构造器可以使用
super
关键字来调用一个父类的构造函数 - 类没有私有属性
class Point {
constructor(x, y) /*构造器*/ {
this.x = x;
this.y = y;
}
show() /*方法*/ {
console.log(this, this.x, this.y)
}
}
let p1 = new Point(10, 11)
p1.show()
- 继承
// 定义一个基类
class Point {
constructor(x, y) /*构造器*/ {
this.x = x;
this.y = y;
}
show() /*方法*/ {
console.log(this, this.x, this.y)
}
}
// let p1 = new Point(10, 11)
// p1.show()
// 继承
class Point3D extends Point {
constructor(x,y,z) {
super(x,y);
this.z = z;
}
}
let p2 = new Point3D(10,11,12)
p2.show()
重写方法
- 重写子类的show方法, 如果要使用父类的方法,使用
super.method()
调用
class Point {
constructor(x, y) /*构造器*/ {
this.x = x;
this.y = y;
}
show() /*方法*/ {
console.log(this, this.x, this.y)
}
}
// let p1 = new Point(10, 11)
// p1.show()
// 继承
class Point3D extends Point {
constructor(x,y,z) {
super(x,y);
this.z = z;
}
show() /*重写*/ {
console.log(this, this.x, this.y, this.z)
}
}
let p2 = new Point3D(10,11,12)
p2.show()
使用箭头函数重写上面的方法
// 定义一个基类
class Point {
constructor(x, y) /*构造器*/ {
this.x = x;
this.y = y;
this.show = () => console.log('Point method'); //2
}
show() {console.log("Point function method")} //4
}
// 继承
class Point3D extends Point {
constructor(x,y,z) {
super(x,y);
this.z = z;
this.show = () => console.log('Point3D method'); // 1
}
show() {console.log("Point3D function method")} //3
}
let p2 = new Point3D(10,11,12)
// 优先使用类的属性,如果没有会查找父类的属性,如果还没找到会查找类的方法,最后查找父类的方法,优先级为1,2,3,4
p2.show()
对于方法重写而言,父类,子类使用同一种方式定义方法,子类覆盖父类;如果父类使用属性,子类使用方法,则优先使用父类的属性。一句话,属性优先
静态属性
静态属性目前还没有得到很好的支持
静态方法
在方法名前面加上static,就是静态方法了。
class Add {
constructor(x, y) {
this.x = x;
this.y = y;
}
static print() {
console.log(this, this.x)
}
}
Add.print() // [class Add] undefined
add = new Add(4,5)
add.constructor.print() // 实例不能直接访问静态方法,可以通过constructor访问
输出结果:
[class Add] undefined
[class Add] undefined
this 的坑
C++,JAVA,JS都有this,但是C++跟JAVA都是编译型语言,this是编译的时候就绑定了,而JS是动态语言,是在运行期进行绑定的,所以他们的表现各不相同,对于JS而言,调用方式的不同,this对应的对象也就不同了。
var school = {
name: 'dengyou',
getNameFunc: function() {
console.log(this.name);
console.log(this);
return function() {
console.log(this === global)
return this.name
}
}
}
ret = school.getNameFunc()
console.log("---------------")
console.log(ret())
输出结果:
dengyou
{ name: 'dengyou', getNameFunc: [Function: getNameFunc] }
---------------
true // 说明this当前是global,nodejs函数调用的global,浏览器中的全局对象是windows
undefined // 因为this为全局对象,所以没有name属性
对于上面的例子,调用方式的不同,this对应的对象也有所变化,这种变化是历史遗留问题,我们使用是,需要明确的让this必须是我们期望的对象,可以通过下面几种方式解决这个问题。
显示传入-that
通过主动传入对象,避免this的问题,这个我们不推荐使用
var school = {
name: 'dengyou',
getNameFunc: function() {
console.log(this.name);
console.log(this);
return function(that) /*传入that*/ {
console.log(this === global)
return that.name
}
}
}
ret = school.getNameFunc()
console.log("---------------")
console.log(ret(school))
输出结果:
dengyou
{ name: 'dengyou', getNameFunc: [Function: getNameFunc] }
---------------
true
dengyou
ES3 引入apply、call方法
apply、call方法都是函数对象的方法,第一参数都是传入对象引入的
var school = {
name: 'dengyou',
getNameFunc: function() {
console.log(this.name);
console.log(this);
return function() {
console.log(this === global)
return this.name
}
}
}
ret = school.getNameFunc()
console.log("---------------")
console.log(ret.call(school)) // 等价console.log(ret.apply(school))
- apply传入其他参数需要使用数组
- call传入其他参数需要使用可变参数收集
function Print() {
this.print = function(x, y) {console.log(x, y)}
}
p = new Print(1,2)
p.print(10,20)
p.print.call(p, 10,20)
p.print.apply(p, [10, 20])
输出结果:
10 20
10 20
10 20
ES5 引入bind方法-常用
bind方法来设置函数的this值
var school = {
name: 'dengyou',
getNameFunc: function() {
console.log(this.name);
console.log(this);
return function() {
console.log(this === global)
return this.name
}
}
}
ret = school.getNameFunc()
console.log("---------------")
console.log(ret.bind(school)/*绑定以后再调用*/())
ES6引入支持this的箭头函数-推荐
var school = {
name: 'dengyou',
getNameFunc: function() {
console.log(this.name);
console.log(this);
return() => { /*箭头函数*/
console.log(this === global)
return this.name
}
}
}
ret = school.getNameFunc()
console.log("---------------")
console.log(ret())
// 将上面的类改写为新的类
class School {
constructor() {
this.name = 'dengyou'
}
getNameFunc() {
console.log(this.name);
console.log(this, typeof(this));
return () => {
console.log(this === global)
return this.name
}
}
}
console.log(new School().getNameFunc()())
输出结果:
dengyou
School { name: 'dengyou' } object
false
dengyou
高阶对象、高阶类、或称 Mixin 模式
Mixin模式,混合模式,这是一种不用继承就可以复用的技术,主要是为了解决多重继承的问题,多继承的继承路径是一个问题
JS是基于对象的,类和对象都是对象模板
混合mixin,指的是将一个对象的全部或部分属性拷贝到另一个对象上去,可以将多个类或者对象混合成一个类或者对象
继承实现
先看一个继承的例子
class Serialization {
constructor() {
console.log('Serialization constructor~~~~~~~')
/* 要求具有属性 stringify ,如果没有则抛出异常*/
if (typeof(this.stringify) != 'function') {
throw new ReferenceError("should define stringify method")
}
}
}
class Point extends Serialization {
constructor(x, y) {
console.log("Point constructor~~~~~~~~~~ ")
super(); // 调用父的构造器
this.x = x;
this.y = y;
}
stringify() {
return `<Point x=${this.x},y=${this.y}>}`
}
}
class Point3D extends Point {
constructor(x,y,z) {
super(x,y);
this.z = z;
}
stringify() {
return `<Point3D x=${this.x},y=${this.y}, z=${this.z}>}`
}
}
let p = new Point(4, 5)
console.log(p.stringify())
console.log("+++++++++++++++++++++++++++")
let p3d = new Point3D(4,5,6)
console.log(p3d.stringify())
输出结果:
Point constructor~~~~~~~~~~
Serialization constructor~~~~~~~
<Point x=4,y=5>}
+++++++++++++++++++++++++++
Point constructor~~~~~~~~~~
Serialization constructor~~~~~~~
<Point3D x=4,y=5, z=6>}
高阶对象的实现
const Serialization = Sup => class extends Sup {
constructor(...args) {
console.log('Serialization constructor~~~~~~~')
super(...args)
if (typeof(this.stringify) != 'function') {
throw new ReferenceError("should define stringify method")
}
}
}
class Point {
constructor(x, y) {
console.log("Point constructor~~~~~~~~~~ ")
this.x = x;
this.y = y;
}
}
class Point3D extends Serialization(Point) /*Serialization(Point)匿名箭头函数调用,返回一个新的类型*/ {
constructor(x,y,z) {
super(x,y);
this.z = z;
}
stringify() {
return `<Point3D x=${this.x},y=${this.y}, z=${this.z}>}`
}
}
let p3d = new Point3D(4,5,6)
console.log(p3d.stringify())
输出结果:
Serialization constructor~~~~~~~
Point constructor~~~~~~~~~~
<Point3D x=4,y=5, z=6>}