Last active
January 21, 2019 09:31
-
-
Save giscafer/30723edddae7970befcacd0847c84b48 to your computer and use it in GitHub Desktop.
原生js接口设计模式
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 类的封装和信息的隐藏 | |
* | |
* 本例子使用闭包来实现静态变量熟悉和方法,通过创建一个受保护的变量空间,可以实现公用、私用和特权成员,以及 | |
* 静态成员和常量。 | |
* | |
* 概念: | |
* 1、特权方法(privileged method) 指有权访问私有变量和私有函数的公有方法 | |
* 2、非特权方法(non-privileged method) 指无需(或没有权限)访问私有变量和私有函数的公有方法 | |
* | |
* 这么做的好处: | |
* | |
* 1、保护内部数据完整性,数据(变量)只能通过取值器getter和赋值器setter来访问,可以完全控制,避免数据处于无效状态; | |
* 2、对象的重构因此变得更轻松,因为用户不知道对象的内部细节,所以可以随心所欲的修改对象内部数据结构和算法,外部使用不会受到影响,也不会知道; | |
* 3、公开一些接口规定的方法,可弱化模块之间的耦合性,这是面向对象编程重要的原则之一; | |
* | |
* 弊端: | |
* | |
* 1、封装会带来复杂性,和作用域闭包打交道,调式困难; | |
*/ | |
var Book = (function () { | |
// 私有静态变量属性 | |
var numOfBooks = 0; | |
// 私有静态方法 | |
function checkIsbn() { | |
// …… | |
return true; | |
} | |
return function (isbn, title, author) { | |
// 私有属性 | |
var _isbn, _title, _author; | |
// privileged method 特权方法 | |
this.getIsbn = function () { | |
return _isbn; | |
}; | |
this.setIsbn = function (isbn) { | |
if (!checkIsbn(isbn)) throw new Error('Book:无效的ISBN'); | |
_isbn = isbn; | |
}; | |
this.getTitle = function () { | |
return _title; | |
}; | |
this.setTile = function (title) { | |
_title = title || 'No title'; | |
}; | |
this.setAuthor = function (author) { | |
_author = author || 'No title'; | |
} | |
this.getAuthor = function () { | |
return _author; | |
}; | |
// 静态变量自动技术,统计书的总数 | |
numOfBooks++; | |
// 设置值 | |
this.setIsbn(isbn); | |
this.setTile(title); | |
this.setAuthor(author); | |
} | |
})(); | |
// 公共静态方法(好处是所有实例对象,共享一个方法,该方法只会创建一次,也就是内存只会存放一个,如果每个实例都创建一次,很浪费资源) | |
Book.convertToTitleCase = function (inputString) { | |
// …… | |
} | |
// 任何不需要直接访问私有变量(如_isbn,_title)的方法,都可以放到prototype里边定义,里边定义的方法都称为非特权方法(non-privileged method) | |
Book.prototype = { | |
// non-privileged method | |
dispaly: function () { | |
console.log(this.getIsbn(), this.getTitle(), this.getAuthor()); | |
} | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 定义一个Person类 | |
* 其实JavaScript没有类之说(ES5之前无class关键词),JS的函数是一等对象,因为js太过于灵活(后端可能认为是奇葩),所以约定一些规范或者设计模式等。 | |
* JS也可以有类(如下Person), 类可以实现继承。当然,不会像Java或者其他静态语言一样,一个关键字extends就搞定了类的继承。 | |
* (了解ES6和ts的同学知道,类继承可以通过extends实现了,因为他们真正有类了,class) | |
* @param {*} name | |
*/ | |
function Person(name) { | |
this.name = name; | |
} | |
Person.prototype.getName = function () { | |
return this.name; | |
} | |
// 定义一个Author类,继承Person类 | |
function Author(name, books) { | |
Person.call(this, name); // 调用父类构造函数 | |
this.books = books; | |
} | |
Author.prototype = new Person(); // 将Author的原型链指向person的一个实例 | |
Author.prototype.constructor = Author; // 因为上一句会造成Author原本的constructor丢失,变成了Person,所以需要手动修改为Author,类的构造器就是类本身 | |
Author.prototype.getBooks = function () { | |
return this.books; | |
} | |
// test | |
var author1=new Author("东野奎君",["嫌疑人X的献身","白夜人"]); | |
var author2=new Author("村上春树",["挪威的深林"]); | |
console.log(author1.getName()); | |
console.log(author1.getBooks()); | |
console.log(author2.getBooks()); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 派生子类过程包装到继承inherit函数中 | |
* @param {*} C 子类 | |
* @param {*} P 父类 | |
* | |
* 很多著名的地图框架都采用类似的方式,如openlayers2.x和leaflet.js,Echarts2.x | |
* 如openlayers2.x的OpenLayers.Class的实现源码: | |
* https://github.com/openlayers/ol2/blob/master/lib/OpenLayers/BaseTypes/Class.js | |
* | |
*/ | |
function inherit(C, P) { | |
// 使用空函数F,避免超类P创建的实例较庞大,以及规避超类构造器中的可能存在的副作用。 | |
var F = function () { }; | |
F.prototype = P.prototype; | |
C.prototype = new F; | |
C.prototype.constructor = C; | |
// 提供超类属性,弱化父子类之间的耦合性,使得子类也可以之间调用超类的方法 | |
C.superclass = P.prototype; | |
// 确保父类的构造函数被正确设置 | |
if (P.prototype.constructor == Object.prototype.constructor) { | |
P.prototype.constructor = P; | |
} | |
} | |
function Person(name) { | |
this.name = name; | |
} | |
Person.prototype.getName = function () { | |
return this.name; | |
} | |
function Author(name, books) { | |
// 使用superclass来调用父类的构造器 | |
Author.superclass.constructor.apply(this, arguments); | |
this.books = books; | |
} | |
// 原型链继承,Author继承Person | |
inherit(Author, Person); | |
Author.prototype.getBooks = function () { | |
return this.books; | |
} | |
// test | |
var author1 = new Author("东野奎君", ["嫌疑人X的献身", "白夜人"]); | |
var author2 = new Author("村上春树", ["挪威的深林"]); | |
console.log(author1.getName()); | |
console.log(author1.getBooks()); | |
console.log(author2.getBooks()); | |
//===== 输出======// | |
// 东野奎君 | |
// [ '嫌疑人X的献身', '白夜人' ] | |
// [ '挪威的深林' ] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Interface 类 | |
* 检查某些类是否实现了指定的接口 | |
* @param {*} name | |
* @param {*} methods | |
*/ | |
var Interface = function (name, methods) { | |
if (arguments.length != 2) { | |
throw new Error('Interface contructor called width' + arguments.length + 'arguments,but expected exactly 2.'); | |
} | |
this.name = name; | |
this.methods = []; | |
for (var i = 0; len = methods.length; i++) { | |
if (typeof methods[i] !== 'string') { | |
throw new Error('Interface constructor expects method names to be passed in as a string'); | |
} | |
this.methods.push(methods[i]); | |
} | |
}; | |
// Static class method | |
Interface.ensureImplements = function (object) { | |
if (arguments.length < 2) { | |
throw new Error('Function Interface.ensureImplements called width' + arguments.length + 'arguments,but expected exactly 2.'); | |
} | |
for (var i = 1, len = arguments.length; i < len; i++) { | |
var interface = arguments[i]; | |
if (interface.constructor !== Interface) { | |
throw new Error('Function Interface.ensureImplements expects arguments two and above to be instances of Interface.'); | |
} | |
for (var j = 0, methodsLen = interface.methods.length; j < methodsLen; j++) { | |
var method = interface.methods[j]; | |
if (!object[method] || typeof object[method] !== 'function') { | |
throw new Error('Function Interface.ensureImplements:object does not implements the ' + interface.name + ' interface. Method ' + method + ' was not found'); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment