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>}

posted on 2023-07-20 13:56  kntvops  阅读(78)  评论(0编辑  收藏  举报

导航