美文网首页
JavaScript面向对象编程

JavaScript面向对象编程

作者: 杨志聪 | 来源:发表于2023-10-02 14:21 被阅读0次

创建对象

  1. 基本方式
let tom  = {};
tom.name = 'Tom';
tom.sayHi = function() {
    console.log(this.name, 'say hi.');
}
tom.sayHi();
  1. 使用构造函数
function Human(name) {
    this.name = name;
    this.sayHi = function() {
        console.log(this.name, 'say hi.');
    }
}
let tom = new Human("Tom");
tom.sayHi();

上面代码中,Human就是一个构造函数。构造函数一般首字母要大写,用来和普通函数作区分。其实构造函数和普通函数没有任何区别。构造函数通过new关键字来创建对象:

let tom = new Human("Tom");

new关键字做的事情,是创建一个空的对象{},然后将该对象传入到Human函数中进行初始化,然后返回该对象。

  1. 使用class

ES6引入了class关键字,如果你了解像java、C++这样的语言,一定对class很熟悉。

class Human {
    constructor(name) {
        this.name = name;
    }
    sayHi() {
        console.log(this.name, 'say hi.');
    }
}
let tom = new Human('Tom');
tom.sayHi();

添加方法

当我们用构造函数的方式添加方法时,有两种写法:
写法1:

function Shape {
    this.copy = function() {
        console.log('copy...');
    }
}

写法2:

function Shape() {}
Shape.prototype.copy = function() {
    console.log('copy...');
}

两种都可以,但是写法1每次创建一个新的对象,都要创建一个新的方法,而写法2是所有对象共享同一个方法。如果是需要创建大量对象的场景,写法2有更好的性能。

静态属性和静态方法

JavaScript使用static关键字声明静态属性和静态方法:

class Human {
   static level = 0;
   static upgrade() {
        this.level += 1;
   }
}
Human.upgrade();
console.log(Human.level);

私有属性和私有方法

方法1:通过Symbol实现私有属性和私有方法

const _name = Symbol();
const _sayHi = Symbol();
class Human {
   constructor(name) {
    this[_name] = name;
    this[_sayHi] = function() {
        console.log(this[_name], 'say hi.');
    }
   }
}
let tom = new Human('Tom');

ES6新增了Symbol作为原始数据类型(像number、boolean、null、undefined、string这些都是原始数据类型)。Symbol() 函数每次调用会创建一个新的唯一值。

方法2:通过WeakMap实现私有属性和私有方法

const nameMap = new WeakMap();
const sayHiMap = new WeakMap();
class Human {
   constructor(name) {
    nameMap.set(this, name);
    sayHiMap.set(this, () => {
        console.log(nameMap.get(this), 'say hi.');
    })
   }
}
let tom = new Human('Tom');

WeakMap将属性或者方法保存在对象之外,所以对外部是真正的不可见。使用WeakMap不会影响对象的垃圾回收,因为WeakMap不会持有该对象。

getter和setter

const ageMap = new WeakMap();
class Human {
   constructor(age) {
    ageMap.set(this, age);
   }
   set age(age) {
    if (typeof age !== 'number' || age < 0 || age > 150) throw '您输入的age不对劲!';
    ageMap.set(this, age);
   }
   get age() {
    return ageMap.get(this);
   }
}
let tom = new Human(18);
console.log(tom.age);

继承

在JavaScript中,所有的对象,都有一些共有的方法。

let obj = {};
console.log(obj.toString());

上面代码中的toString方法就是所有JavaScript共有的方法,它是怎么来的呢?在JavaScript中,每个对象都有一个隐藏的属性,这个属性是一个对象,我们称之为prototype(原型)。由于原型也是一个对象,所以原型也有它自己的原型!依次类推,形成一个原型的链条。一个对象中有什么属性和方法,是由它的原型链条决定的,因为当你访问一个对象的方法或者属性时,首先会在当前对象上找,如果找不到,就会顺着他的原型链条找。

获取一个对象的原型的方式:

function Shape(idx) {
    this.idx = idx;
}
let s = new Shape(1);
// 获取对象s的原型有三种方式:
// 方式1:
let p1 = s.__proto__;
// 方式2
let p2 = Shape.prototype;
// 方式3
let p3 = Object.getPrototypeOf(s);

当然这个原型链条是有终点的,这个终点我们可以称之为objectBaseobjectBase是JavaScript中所有的对象最终的原型。objectBase中包含JavaScript对象共有的方法,例如toString等。

let foo = {};
// 获取objectBase
let objectBase = foo.__proto__;
if (foo.toString === objectBase.toString) console.log('foo的toString方法来自于objectBase');

所以,要实现JavaScript中的继承功能,就是要设计一个原型链条。先看一下最简单的情况:

let obj = {};

原型链条是:

objectBase
|
obj

使用构造方式创建对象:

function Shape() {
}
let s = new Shape();

原型链条是:

objectBase
|
shapeBase
|
s

那么,如果要设计一个继承于Shape的Circle类型,要怎么设计呢?需要通过手动修改Circle的原型的原型!

function Shape(idx) {
    this.idx = idx;
}
function Circle(idx, radius) {
    Shape.call(this, idx);
    this.radius = radius;
}
Circle.prototype.__proto__ = Shape.prototype;
let c = new Circle(1,1);

原型链条是:

objectBase
|
shapeBase
|
circleBase
|
c

说实话,很麻烦!不过事实上没有人这么实现JavaScript的继承的,因为ES6新增了class关键字,现在实现继承是很容易的:

class A {}
class B extends A {}
class C extends B {}
let c = new C();

但是,这只不过是语法糖,JavaScript中并没有class的概念,最终还是靠原型链实现继承功能的。通过使用class关键字,可以让编译器帮我们完成原型链设计。

方法覆写(Method overriding)

JavaScript并不需要像其他面向对象的语言一样使用overiding之类的关键字:

class Shape {
    copy() {
        console.log('Shape copy...');
    }
}

class Circle extends Shape {
    copy() {
        super.copy();
        console.log("Circle copy...");
    }
}

let c = new Circle();
c.copy();

多态

有了上面的基础后,使用多态就很简单了:

class Shape {
    copy() {
        console.log('Shape copy...');
    }
}

class Circle extends Shape {
    copy() {
        console.log("Circle copy...");
    }
}

class Triangle extends Shape {
    copy() {
        console.log('Triangle copy...');
    }
}

let shapes = [
    new Circle(),
    new Triangle()
];

for (const shape of shapes) {
    shape.copy();
}

相关文章

  • 构造函数与 new 命令

    JavaScript 语言具有很强的面向对象编程能力,本章介绍 JavaScript 如何进行面向对象编程。 对象...

  • Javascript面向对象编程

    阮一峰文档备忘 Javascript 面向对象编程(一):介绍封装 Javascript 面向对象编程(二):介绍...

  • JS创建对象方案(一)

    5.1 JavaScript的面向对象 JavaScript其实支持多种编程范式的,包括函数式编程和面向对象编程:...

  • JavaScript学习笔记(一)

    Javascript面向对象 1. 面向对象编程介绍 1.1 两大编程思想 面向过程 & 面向对象 1.2 面向过...

  • JavaScript学习

    javascript面向对象 初学javascript,感觉javascript的面向对象编程还是很有意思的,在此...

  • javascript的面向对象

    javascript面向对象 初学javascript,感觉javascript的面向对象编程还是很有意思的,在此...

  • ajax

    1. 面向对象 javascript 具有面向过程,面向对象,函数式编程的特点 javascript 重要 原型/...

  • javascript 面向对象编程

    引自:阮一峰的博客Javascript面向对象编程(一):封装Javascript面向对象编程(二):构造函数的继...

  • javascript面向对象编程

    javascript面向对象编程一(封装) 通俗易懂绝对干货 JS面向对象编程

  • JavaScript学习笔记(五)

    主要源于廖雪峰老师的JavaScript教程 面向对象编程 1. 简介 JavaScript的面向对象编程和大多数...

网友评论

      本文标题:JavaScript面向对象编程

      本文链接:https://www.haomeiwen.com/subject/jterbdtx.html