JavaScript设计模式(一)

  什么是设计模式呢? 就是指对于类似的问题,我们可以用大致相同的思想、方法去解决之,而这种通用的思想、方法就是设计模式。学习设计模式可以帮助我们在遇到问题时迅速地搜索出一种清晰的思路来实现之。

第一部分: 面向对象的JavaScript

  1. JavaScript是动态类型语言。

  静态类型语言即强迫规定程序员在使用某个变量时先定义它的类型。而动态类型语言是在程序运行的时候,才会具有某个类型,不需要严格定义。显然,静态类型语言要求更严格,而动态类型语言却无法保证变量的类型。JavaScript就是这样的动态类型语言,它的好处是我们可以花更多的时间关注在逻辑上,而不是变量的定义上,代码会更加简洁。  静、动我们就可以理解为变量类型的静与动

  

  2. 面向接口编程和鸭子模型

  JavaScript王国需要100只鸭子来组成合唱团,但最后只能找到99只, 而正巧发现一只鸡的叫声也是嘎嘎嘎,于是我们就把鸡也拉近了鸭子合唱团。  这就是说,我们只关注对象的行为,而不关注对象本身。

  基于这种思想,比如一个对象若有push和pop方法,就可以把它当作栈来使用等等,这种思想的编程就是面向接口编程。

  

  3.多态

   多态的实际含义是: 同一个操作作用在不同的对象上面,可以产生不同的解释和不同的执行结果。 如下所示:

var makeSound = function(animal) {
    if (animal instanceof Duck) {
        console.log("嘎嘎嘎");
    }
    if (animal instanceof Chicken) {
        console.log("咯咯咯");
    }
}

var Duck = function() {};
var Chicken = function() {};

makeSound(new Duck());
makeSound(new Chicken());

  可以看出makeSound函数对于不同的输入就有不同的输出,这就是多态

  但是如果再添加一只狗呢?  我们不仅要在创建一个狗的构造函数,还要改变makeSound函数,即这样的可扩展性是十分糟糕的。

  解决方法: 多态背后最重要的思想是将“做什么”和“谁去做以及怎么样去做”分离开来,也就是将不变的事物和可变的事物分离开来。

  如下所示:

var makeSound = function(animal) {
    animal.sound(); // 做什么
}

var Duck = function() {};  // 谁去做
Duck.prototype.sound = function() {
    console.log("嘎嘎嘎"); // 怎么做
};

var Chicken = function() {};
Chicken.prototype.sound = function() {
    console.log("咯咯咯");
};

var Dog = function() {};
Dog.prototype.sound = function() {
    console.log("汪汪汪");
};

makeSound(new Duck());
makeSound(new Chicken());

 可以看出在上面的例子中不变的部分就是animal.sound(),我们将之分离出来(做什么)。 然后再将谁去做,怎么做分离出来,这样函数的可扩展性就非常好了。

  4. 封装

  封装的思想在于隐藏内部的实现、 提高代码的可重用性、 封装变化

  

 5.原型模式

  在以类为中心的面向对象变成语言中,类和对象的关系可以想象成铸模和铸件的关系,对象总是从类中创建。

  但是在原型编程的思想中,类不是必须的,对象未必必须从类中创建而来,一个对象是通过克隆另外一个对象而得到的。

第二部分:this、 call、 apply

  我在《JavaScript函数之美~》中详尽的介绍了this的用法。这里还是要提及一些重点。

  我们知道:this总是指向一个对象,也许是window对象,也许是调用它所在的方法的对象,也有可能是新创建的一个对象,具体执行的对象是运行时基于函数的执行环境动态绑定的,而非函数被声明时的环境。

  情况一:作为对象的方法调用,this指向的是该对象。

  情况二:作为普通函数调用,而非对象的方法,this指向的全局对象window。

  情况三:构造器调用,那么就指向这个新创建的函数。

  情况四:Function.prototype.call或Function.prototype.apply调用。

  

  注意点一: 在一个对象中,如果存在回调函数,我们还想this存在,该怎么办?

    可以把 this 在对象的环境中赋值给 that,然后使用that,这时就有正确的指向了。

  注意点二: 构造器调用的过程中,如果最后返回了对象,那么this就会指向这个返回的对象。如下:

var MyClass = function() {
    this.name = "zzw";
    return {
        name: "htt"
    };
}
var person = new MyClass();
console.log(person.name); // htt

  如果说这里构造函数并没有返回一个对象,那么最终的结果一定是zzw,但是如果返回了对象,那么这个this指向的一定是这个返回的对象。

  注意点三: docuemnt.getElementById()方法需要用到this

        var getId = function(id) {
            return document.getElementById(id);
        }
        getId("div").style.color = "red";

  这样我们就可以成功获取到id为div的元素。但是:

        var getId = document.getElementById;
        getId("div").style.color = "red";

  这样就会报错。

  这时因为用getId来引用document.getElementById()之后,再调用getId,此时就变成额普通函数调用,内部的this就指向了window而不是document。(许多引擎的document.getElementById方法的内部实现需要用到this)。

  

  在JavaScript版本的设计模式中,call和apply方法都是很常用的,能熟练应用这两个方法是我们真正成为一名JavaScript程序员的重要一步。

  Function.prototype.apply 和 Function.prototype.call 显然都是被一个函数(Function)调用的。 

  其中apply接受两个参数,第一个参数指定了函数体内this对象的指向(之前说过,this总是指向一个对象),第二个参数是一个到右下标的集合,这个集合可以是数组,也可以是类数组,apply把这个集合中的元素作为参数传递给被调用的函数。  而call也接收两个参数,同样的,第一个参数指定了函数体内的this对象的指向,第二个参数是这个函数需要接受的参数(没有call和apply函数也要接收参数啊!)。举例如下:

        var myObject = {
            c: 66
        };

        var anotherObject = {
            c: 88
        }

        var c = 233;

        function outputNum(a, b) {
            var c = 10;
            console.log([a, b, this.c]);
        }

        function outputNumSecond(a, b) {
            c = 10;
            console.log([a, b, this.c]);
        }
        outputNum(1, 2); // [1, 2, 233]
        outputNum.apply(null, [1,2]); // [1, 2, 233]
        outputNum.apply(window, [1,2]); // [1, 2, 233]
        outputNum.call(null, 1, 2); // [1, 2, 233]
        outputNum.call(window, 1, 2); // [1, 2, 233]
        outputNum.apply(myObject, [1, 2]); // [1, 2, 66]
        outputNum.apply(anotherObject, [1, 2]); // [1, 2, 88]
        outputNumSecond.apply(null, [1, 2]); // [1, 2, 10]

  如果第一个参数是null,那么this就会指向默认的宿主对象,在浏览器中就是window。 但是在严格模式下, 函数体内的this还是为null。

  

  常见错误

    

时间: 2024-10-14 12:29:38

JavaScript设计模式(一)的相关文章

JavaScript设计模式 - 迭代器模式

迭代器模式是指提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示. 迭代器模式可以把迭代的过程从业务逻辑中分离出来,在使用迭代器模式之后,即使不关心对象的内部构造,也可以按顺序访问其中的每个元素 许多浏览器都支持 Javascript 的 Array.prototype.forEach 迭代器可以分为 内部迭代器 和 外部迭代器 一.jQuery 中的迭代器 1 $.each( [1,2,3,4], function (i, n) { 2 console.log( "当

javascript设计模式详解之命令模式

每种设计模式的出现都是为了弥补语言在某方面的不足,解决特定环境下的问题.思想是相通的.只不过不同的设计语言有其特定的实现.对javascript这种动态语言来说,弱类型的特性,与生俱来的多态性,导致某些设计模式不自觉的我们都在使用.只不过没有对应起来罢了.本文就力求以精简的语言去介绍下设计模式这个高大上的概念.相信会在看完某个设计模式之后有原来如此的感慨. 一.基本概念与使用场景: 基本概念: 将请求封装成对象,分离命令接受者和发起者之间的耦合. 命令执行之前在执行对象中传入接受者.主要目的相互

javascript设计模式

javascript设计模式 阅读目录 什么是设计模式 单体模式: 工厂模式: 单例模式 观察者模式(发布订阅模式) 策略模式 模板模式 代理模式 外观模式 设计模式太多了,貌似有23种,其实我们在平时的工作中没有必要特意去用什么样的设计模式,或者你在不经意间就已经用了设计模式当中的一种.本文旨在总结平时相对来说用的比较多的设计模式. 回到顶部 什么是设计模式 百度百科: 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结. 使用设计模式是

javascript设计模式实践之迭代器--具有百叶窗切换图片效果的JQuery插件(一)

类似于幻灯片的切换效果,有时需要在网页中完成一些图片的自动切换效果,比如广告,宣传,产品介绍之类的,那么单纯的切就没意思了,需要在切换的时候通过一些效果使得切换生动些. 比较常用之一的就是窗帘切换了. 先贴上完成的效果. 实现原理不复杂,在动的一条一条的称之为“窗帘条”或者是“strip”,每一个strip都是一个div,类似于雪碧图的方式将其背景图的位置设置为strip的可视位置,然后用jquery的animate让他们按照一定规律动起来就完成窗帘切换效果了. 为了使用方便,将这个功能作为jq

Javascript设计模式系列三

继承,一个类或对象继承另一个类或对象的三种方法.类式继承.原型式继承.掺元类. 一.类式继承,原型链.Extend函数. <script type="text/javascript"> ////定义Person类 var Person = function (name) { this.name = name; }; Person.prototype.getName = function () { return this.name; }; ////原型链 function A

Javascript设计模式系列二

创建对象的基本模式,一.门户大开型,二.采用下划线来表示属性和方法的私用性,三.使用闭包来创建私用的成员. 一.门户大开型.只能提供公用成员.所有属性和方法都公开的.可访问的.这些共用属性都要使用this关键字来创建. <script type="text/javascript"> ////定义Dog对象 var Dog = function (name, age) { this.name = name; this.age = age; } Dog.prototype.Sa

《Javascript 设计模式》-读书笔记

第七章   设计模式类别 一.设计模式有三种类别 1.创建型模式:专注于处理对象机制.包括:构造器.工厂.抽象.原型.单例.生成器 2.结构型模式:用于找出在不同对象之间建立关系密切的简单方法. 包括:装饰者.外观.享元.适配器.代理 3.行为模式:用于改善或简化系统中不同对象之间的通信.包括:迭代器.中介者.观察者.访问者 第八章   设计模式分类 <Javascript 设计模式>-读书笔记,布布扣,bubuko.com

【读书笔记】读《JavaScript设计模式》之观察者模式

一.定义 在事件驱动的环境中,比如浏览器这种持续寻求用户关注的环境中,观察者模式(又名发布者-订阅者(publisher-subscripber)模式)是一种管理人与其任务之间的关系(确切地讲,是对象及其行为和状态之间的关系)的得力工具.用JavaScript的话来说,这种模式的实质就是你可以对程序中某个对象的状态进行观察,并且在其发生改变时能够得到通知. 二.例子 我们需要一个发布者的构造函数,它为该实例定义了一个类型为数组的属性,用来保存订阅者的引用. function Publisher(

方法的链式调用【参考javascript设计模式第6章】

对应经常使用jquery的朋友,方法的链式调用应该是已经很属性了,书上有模拟出一个很简单的类库代码, 见代码如下: Function.prototype.method = function(name,fn){ this.prototype[name] = fn; return this; }; (function(){ function _$(els){ ........ } /*Events  addEvent getEvent*/ _$.method("addEvent",func

模块化JavaScript设计模式(一)

在可扩展JavaScript的世界里,如果我们说一个应用程序是模块化(modular)的,那么通常意味着它是由一系列存储于模块中的高度解耦,不同的功能片段组成.在可能的情况下,通过一处依赖性,松耦合可以使应用程序的可维护性更加简单. 如果有效地实现了这点,就很容易地了解一部分如何影响另一个部分. 异步模块定义(AMD)的整体目标是提供模块化的JavaScript解决方案,以便开发者使用.诞生于使用XHR+eval的Dojo开发经验,这种格式的支持者希望能够避免未来的任何解决方案受到过去解决方案缺