zoukankan      html  css  js  c++  java
  • 读书笔记:js设计模式

    面向过程编程,面向对象编程和函数式编程
    > 定义一个类
    方法1:
    function Anim(){

    }

    Anim.prototype.start = function(){ .. };
    Anim.prototype.stop = function(){ .. };

    或者
    方法2:
    function Anim(){ .. }
    Anim.prototype = {
    start: function(){.. },
    stop: function(){ .. },
    constructor: Anim
    }

    或者
    方法3:
    //从视觉上 整个类都在构造函数中定义
    function Anim(){
    ...
    if(!Anim.prototype.version){ //假设version是这个类 肯定具备的方法
    Anim.prototype.version = '1.0';
    Anim.prototype.start = function(){..};
    Anim.prototype.stop = function(){...};
    }
    }

    new Anim(); //第1次new的时候 完成对原型对象的定义

    或者
    方法4:
    把向函数原型对象添加方法的过程定义为函数,如:
    Function.prototype.method = function(name, fn){
    this.prototype[name] = fn;
    return this; //使可以链式调用
    }

    //与第1中方法没什么差别
    function Anim(){...}
    Anim.method('start', function(){..});
    Anim.method('stop', function(){..});

    > js的数据类型:原始类型 number string boolean null undefined, 引用类型 object ( array, function, regexp, math, number, string, boolean)
    原始数据类型按值传递; 引用类型按引用传递(即引用地址)
    数据类型转换:
    toString();
    parseInt();
    parseFloat();
    !!number
    注意 js的隐式类型转换

    > 函数是一等对象,可以存储在变量中,可以作为参数传递,可以作为函数返回值返回。
    匿名函数可以激发一个作用域,无影响全局环境,实现数据封装
    (function(){
    var foo = 10;
    var bar = 2;
    alert(foo * bar);
    })();

    var result = (function(foo, bar){
    return foo * bar
    })(20, 10);

    js具有函数作用域,js的作用域是词法作用域(函数运行在定义它的作用域中,而不是在调用它的作用域中)
    匿名函数最有趣的用途是创建闭包。
    用匿名函数创建闭包的例子:
    var baz;
    (function(){
    var foo = 10;
    var bar = 2;
    baz = function(){
    return foo * bar;
    }
    })();
    baz(); //20

    > js的对象都是易变的
    /* class Person */
    function Person(name, age){
    this.name = name;
    this.age = age;
    }
    Person.prototype = {
    getName: function(){ reutn this.name },
    getAge: function(){ return this.age },
    constructor: Person
    }

    /* instantiate the class */
    var alice = new Person('Alice', 19);

    /* modify the class */
    Person.prototype.geeting = function(){ return 'Hi,' + this.getName + '!'};

    /* modify the instance */
    alice.displayGeeting = function(){
    alert(this.geeting());
    }

    js中,任何东西都能够在运行时修改

    > 继承
    原型式继承 和 类式继承 两种继承范型

    在js中使用设计模式的好处:可维护性,性能,程序员间沟通;缺点:复杂性 性能(有的模式对性能有负面影响,有的能提高性能)

    -----------------------------------
    接口
    -----------------------------------
    接口:提供了一种说明一个对象应该具有什么方法的手段
    假设一批对象实现了相同的接口,那么即使他们彼此不相关都可以被同样对待
    如: 一批对象都实现了 comparable接口, 那么就可以调用 obj.compare(obj2);

    接口可以告诉程序员一个类实现了哪些方法,接口说明了一个类具有哪些特性和操作

    > js中的模仿接口的方法: 注释法,属性检查法,鸭式辨型

    1.注释法:
    /*
    interface Composite{
    function add(child);
    function remove(child);
    function getChild(index);
    }

    interface FormItem{
    function save();
    }
    */

    var CompositeForm = function(id, method, action){//implements Composite, FormItem
    ...
    };

    //implement the Composite interface
    CompositeForm.prototype.add = function(child){ .. };
    CompositeForm.prototype.remove = function(child){ .. };
    CompositeForm.prototype.getChild = function(index){ ..};

    //implement the FormItem interface
    CompositeForm.prototype.save = function(){...};

    依靠程序员自觉遵守接口约定,不影响执行性能;但是没有错误提示

    2.属性检查法:

    /*
    interface Composite{
    function add(child);
    function remove(child);
    function getChild(index);
    }

    interface FormItem{
    function save();
    }
    */

    var CompositeForm = function(id, method, action){
    this.implementsInterfaces = ['Composite', 'FormItem'];//自称实现了哪些接口
    ...
    }

    function addForm(formInstance){// 调用实例的一个方法
    if(!implements(formInstance,'Composite', 'FormItem')){//检查实例是否实现了指定的接口
    throw new Error('instance do not implement the required interfaces');
    }
    ...
    }

    //检查所需接口是否在实例对象自称实现的接口数组中能找到
    ~~ 检查传入的接口名,是否都在自称实现的接口数组中能找到
    function implements(object){
    for(var i=1; i<arguments.length; i++){ //遍历传入的接口
    var interfaceName = argument[i];
    var interfaceFound = false;
    for(var j = 0; j < object.implementsInterfaces.length; j++){
    if(object.implementsInterfaces[j] == interfaceName){
    interfaceFound = true;
    break;
    }
    }
    if(!interfaceFound) {return false;}
    }
    return true; //没有中途退出 即所有接口都能找到
    }

    3.鸭式辨型:走路嘎嘎叫的就是鸭子
    如果对象具有与接口定义的方法同名的所有方法,就认为它实现了这个接口

    /* class Interface */
    var Composite = new Interface('Composite',['add', 'remove', 'getChild']);
    var FormItem = new Interface('FormItem',['save']);

    //class CompositeForm
    var CompositeForm = function(id, method, action){
    ...
    }

    function addForm(formInstance){
    ensureImplements(formInstance, Composite, FormItem);
    ...
    }

    保证类真正实现了接口定义的方法,缺点:类没有声明自己实现了哪些接口

    比较好的方法是结合注释法 + 鸭式辨型 如:
    // Interfaces
    var Composite = new Interface('Composite',['add', 'remove', 'getChild']);
    var FormItem = new Interface('FormItem',['save']);

    //class CompositeForm
    var CompositeForm = function(id, method, action){ //implements Composite, FormItem
    ...
    }

    function addForm(formInstance){
    ensureImplements(formInstance, Composite, FormItem);
    ...
    }

    /** class Interface **/
    function Interface(name, methods){
    if(arguments.length !== 2){
    throw new Error('expect 2 arguments');
    }
    this.name = name;
    this.methods = [];
    if(methods.constructor !== Array){ throw new Error('expect an array for methods param')}
    for(var i =0, len=methods.length; i<len; i++){
    if(typeof methods[i] !=='string'){
    throw new Error('expect string value');
    }
    this.methods.push(methods[i]);
    }
    }

    /** ensureImplements method **/
    function ensureImplements(object){
    if(arguments.length < 2){ throw new Error('expect arguments at least 2'); }
    for(var i=1, len=arguments.length; i<len; i++){
    var interface = arguments[i];//遍历接口
    if(interface.constructor !== Interface){
    throw new Error('expect this argument to be Interface instance');
    }
    for(var j=0,mLen=interface.methods.length; j<mLen; j++){//遍历接口的方法
    var method = interface.methods[j];
    if(!object[method] || typeof object[method] !=='function'){
    //若object没有跟接口方法同名的方法 则抛出异常
    throw new Error('not the method:'+method);
    }
    }
    }
    }

    接口降低了对象间的耦合度,提高了代码的灵活性,不要求对象间有继承关系,只要它们实现了同样的接口,就可以同等对待。
    接口记载着api,可作为程序员交流的工具
    应用场景:对外部提供的服务api如搜索 地图 邮件等,创建一个Interface对象,对接收到每个对象做接口检查,如:
    var DynamicMap = new Interface('DynamicMap',['centerOnPoint','zoom','draw']);

    function displayRoute(mapInstance){//自己用到的实例方法做成接口,保证传入的实例确实有这些方法
    ensureImplements(mapInstance, DynamicMap);
    mapInstance.centerOnPoint(12,34);
    mapInstance.zoom(3);
    mapInstance.draw();
    ...
    }

    是否需要使用接口做检查,取决于项目的大小,和做检查的必要性。
    对传入对象做接口检查(是否实现某个接口)比做类检查(是否某个类),要有保证和灵活一点。

    依赖于接口的设计模式
    工厂模式、组合模式、装饰者模式、命令模式

    >> 封装和信息隐藏
    为对象创建私有成员是面向对象语言最基本和最有用的特性之一。封装是面向对象的基石。
    js中的封装是通过闭包来实现的。
    信息隐藏有助于减轻功能模块间的依赖度,只需要获得结果,不需要知道具体的内部实现。
    封装:对对象内部数据和实现细节的隐藏,许多面向对象语言用关键字声明 该属性或方法为隐藏的(私有的), JAVA中private
    ,js中用闭包

    封装:隐藏对象内部的数据和实现细节,只能用已定义的方法访问对象的数据。

    >> 创建对象的基本模式

    > 门户大开型对象 (对象的所有属性都是公共成员)
    var Book = function(isbn, title, author){
    if(isbn == undefined) throw new Error('isbn is required'); //没检查isbn是否正确,可能影响数据库检索和display图书信息
    //to => if(!this.checkIsbn(isbn)) throw new Error('isbn is invalid');
    this.isbn = isbn;
    this.title = title || 'no title specified';
    this.author = author || 'no author specified';
    }
    Book.prototype.display = function(){
    ... //生成显示图书信息的元素
    }
    Book.prototype.checkIsbn = function(){
    ...
    }

    ---------
    改为:只能通过getter,setter访问门户大开型对象的属性
    var Publication = new Interface('Publication',['getIsbn','setIsbn','getTitle','setTitle','getAuthor', 'setAuthor','display']);
    var Book = function(isbn, title, author){
    this.setIsbn(isbn);
    this.setTitle(title);
    this.setAuthor(author);
    }
    Book.prototype = {
    constructor: Book,
    getIsbn: function(){return this.isbn},
    setIsbn: function(isbn){ if(!this.checkIsbn(isbn) ){ throw new Error('invalid isbn')}else{ this.isbn = isbn } },
    getTitle: function(){ return this.title},
    setTitle:function(title){ return this.title = title || 'no specified title'},
    getAuthor: function(){ return this.author},
    setAuthor:function(Author){ return this.author = author || 'no specified Author'},
    display: function(){...},
    checkIsbn: function(){..}
    }
    但是
    book = new Book('0-234-234-034-9','something in life', 'kitty');
    book.isbn = '0-000-000-000-2'; //实例对象的属性还是可以任意修改的
    优点:简单,易于理解
    缺点:所有属性和方法公开 无法保护内部数据,getter,setter增加代码量

    > 用命名规范区别私有成员 ( 属性名加_前缀表明其为私有,如:_name = 'kitty book' )
    var Book = function(isbn, title, author){
    this.setIsbn(isbn);
    this.setTitle(title);
    this.setAuthor(author);
    }
    Book.prototype = {
    constructor: Book,
    getIsbn: function(){return this._isbn},
    setIsbn: function(isbn){ if(!this.checkIsbn(isbn) ){ throw new Error('invalid isbn')}else{ this._isbn = isbn } },
    getTitle: function(){ return this._title},
    setTitle:function(title){ return this._title = title || 'no specified title'},
    getAuthor: function(){ return this._author},
    setAuthor:function(Author){ return this._author = author || 'no specified Author'},
    display: function(){...},
    checkIsbn: function(){..}
    }

    > 作用域 嵌套函数和闭包
    js只有函数作用域,而且是词法作用域,函数是运行在定义它们的作用域,而不是调用它们的作用域的。
    返回一个内嵌函数是创建闭包的常用方法。

    var Book = function(newIsbn, newTitle, newAuthor){// implements Publication
    var isbn, title, author; //私有属性
    function checkIsbn(isbn){...} //私有方法
    this.getIsbn = function(){ return isbn }; //特权方法
    this.setIsbn = function(newIsbn){ if(checkIsbn(newIsbn)) isbn = newIsbn; };//特权方法
    this.getTitle = ..
    this.setTitle = ..
    this.getAuthor = ..
    this.setAuthor = ..

    //初始化对象私有数据
    this.setIsbn(newIsbn);
    this.setTitle(newTitle);
    this.setAuthor(newAuthor);
    }

    Book.prototype = {
    construcor: Book,
    display: function(){..} //公共方法,非特权方法
    }

  • 相关阅读:
    POJ 3411 Paid Roads(DFS)
    POJ 1699 Best Sequence(DFS)
    Codeforces Round #191 (Div. 2)
    Windows && Linux 搭建python开发环境
    zabbix 源码编译安装
    智能运维基础设施
    Redis
    ubuntu16.04 安装 mysql
    Python必须知道的基础语法
    ubuntu && CentOS && RedHat 离线安装docker
  • 原文地址:https://www.cnblogs.com/stephenykk/p/3890503.html
Copyright © 2011-2022 走看看