JavaScript设计模式阅读随笔——JavaScript面向对象
Posted realpzy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaScript设计模式阅读随笔——JavaScript面向对象相关的知识,希望对你有一定的参考价值。
javascript中的面向对象其实就相当于是将需求抽象成一个对象,这个对象中包含属性和方法,可以用来描述或处理某一类实物,这个对象就叫做类。
在Java中面向对象的三要素是:封装、继承、多态,在ES6以前虽然没有class、extend这样的关键字或方法,中同样可以实现这三要素。
创建一个类
在ES6之前创建一个类通可以用:声明一个函数将其保存在变量中,内部通过对this来添加属性和方法;也可以在类的原型上添加属性和方法。
可以看到当在控制台中创建一个Book的类,Book的实例化对象book包含两个属性 id 和 name,它的隐身原型__proto__中包含一个constructor属性,它又指向了Book这个原型对象。
如果在Book的原型上添加方法或属性:好比 Book。prototype.display = function (){ ... } 每个实例化对象都会通过原型链继承到这个方法,但这并非是她自有的方法,需要通过prototype访问。
如今ES6引入Class可以这么创建类,constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。
1 class Book { 2 constructor(id, name) { 3 this.id = id; 4 this.name = name; 5 } 6 }
封装
利用javascript的函数作用域可以创建类的私有变量和私有方法,通过this创建类的共有属性和共有方法。一下代码是我理解中javascript封装的精髓
1 var Book = function (id,name) { 2 // 安全检查 判断执行过程中是否是当前这个对象 3 // 避免误创建全局对象 4 if (this instanceof Book) { 5 this.name = name; 6 } else { 7 return new Book(id,name); 8 } 9 // 私有属性 10 var num = 1; 11 // 私有方法 12 function checkId() { 13 return num +‘个‘+ id; 14 } 15 // 公有属性和公有方法 16 this.id = id; 17 this.copy = function () {} 18 // 特权方法 19 this.setName = function (name){} 20 this.getName = function() { 21 // 可以访问公有方法和属性,也可以访问创建对象时的私有方法和属性 22 } 23 // 构造器 24 this.setName = function (name) { 25 // 在对象创建时,使用特权方法初始化实例对象 26 } 27 // 闭包 28 return checkId; 29 }
继承
继承就是对原有对象的封装,从中创建私有属性、公有属性等,对于每种属性和方法特点都不一样。可以被继承的方法属性无外乎两种,一种是在构造函数中,在对象实例化时复制一遍属性和方法;一种是在类的原型对象中,这类方法和属性可以被所有实例化对象共有。
类式继承:
类式继承可以理解为把父类的实力赋值给子类的原型,通过原型链来实现继承。
1 function SuperClass() { 2 this.books = [‘父类book‘]; 3 } 4 function SubClass() {} 5 Sub.prototype = new SuperClass(); 6 7 var instance1= new SubClass(); 8 console.log(instance1.books); //[‘父类book‘]
用这种方式新创建的对象不仅可以访问父类原型上的属性和方法,还能访问父类构造函数中赋值的属性和方法,子类的原型还可以访问父类原型上的方法.
不足之处是,如果某一个实例化对象修改了books这个属性,那么所有实例化的对象books值都会被污染。
构造函数继承:
构造函数式继承可以很好的解决类式继承中所暴露的问题。它的精髓在于使用call() 这个函数
function SuperClass(id) { this.books = [‘super‘]; this.id = id; } function SubClass(id) { SuperClass.call(this,id); } var instance1 = new SubClass(10); var instance2 = new SubClass(11); instance1.books.push(‘sub‘); console.log(instance1.books) // [‘super‘, ‘sub‘] console.log(instance2.books) // [‘super‘, ‘sub‘] console.log(instance1.id) // 10 console.log(instance2.id) // 11
可以看出 通过call()这个函数改变函数的作用环境,在子类中调用父类是将子类中的变量拿到父类中执行了一遍。这一类继承不涉及到原型,所以父类原型的方法就不能被子类继承。
组合继承:
组合继承就是结合了类式继承中原型继承,以及构造函数式继承中在子类构造函数环境中执行一次父类 这两点来继承的
function SuperClass(id) { this.books = [‘super‘]; this.id = id; } SuperClass.prototype.getId = function () { console.log(this.id); } function SubClass(id,name) { // 构造函数中继承父类 id属性 SuperClass.call(this,id); this.name = name; } // 类式继承原型继承父类 SubClass.prototype = new SuperClass(); // 新增子类原型方法 SubClass.prototype.getName = function () { console.log(this.name); } var instance1 = new SubClass(10,‘sub1‘); var instance2 = new SubClass(11,‘sub2‘); instance1.books.push(‘sub1-1‘); console.log(instance1.books) // [‘super‘, ‘sub1-1‘] console.log(instance2.books) // [‘super‘] instance1.getName() // 10 instance1.getTime() // sub2
寄生组合继承 :
function inheritObject(o) { // 过渡对象 function F() {} // 原型继承 F.prototype = o; // 返回一个原型继承了父类对象的实例 return new F(); } function inheritPrototype(subClass, superClass) { // 复制一份父类的原型副本保存在变量中 var p = inheritObject(superClass.prototype); //修正因为重写子类原型导致子类constructor属性被修改 p.constructor = subClass; //设置子类的原型 subClass.prototype = p; } function SuperClass(id) { this.books = [‘super‘]; this.id = id; } SuperClass.prototype.getId = function () { console.log(this.id); } function SubClass(id,name) { // 构造函数中继承父类 id属性 SuperClass.call(this,id); this.name = name; } inheritPrototype(SubClass, SuperClass) SubClass.prototype.getName = function () { console.log(this.name); } var instance1 = new SubClass(12,‘sub12‘); var instance2 = new SubClass(13,‘sub13‘);
最大的改变就是子类的原型被赋予了父类的原型的一个引用,此时子类想要添加原型方法必须通过prototype添加。
以上是ES6之前,在使用javascript继承时大多采用的方法,那么在ES6当中,我们可以采用关键字 简洁明了的达到继承的目的
1 class ColorPoint extends Point {
2 constructor(x, y, color) {
3 super(x, y); // 调用父类的constructor(x, y)
4 this.color = color;
5 }
6
7 toString() {
8 return this.color + ‘ ‘ + super.toString(); // 调用父类的toString()
9 }
10 }
在子类的构造函数中必须使用 super()来调用父类的构造函数,这是因为子类自己的this对象,必须先通过父类的构造函数构造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用super方法,子类就得不到this对象。因此只有在调用super以后,子类的this才能正常使用。
多态
多态可以理解为,在调用方法时,更具传递参数的数量、类型不同时,具有多种实现方式。 可以说根据接口不同,呈现不一样的结果。
对于ES6以前实现继承,多态,封装的原理,主要围绕原型、原型链调用来实现,虽说目前ES6可以很好的实现这些灵魂玩法,包括目前大势的TypeSScript更是把JS 引向了强类型语言的圈子,但万变不离其宗,了解原理,才能更好的运用。
以上是关于JavaScript设计模式阅读随笔——JavaScript面向对象的主要内容,如果未能解决你的问题,请参考以下文章