《JavaScript设计模式》Stoyan Stefanov-读书笔记

第一章  简介

一、三种类型模式

  设计模式、编码模式、反模式

二、JavaScript基本概念

1、面向对象

  五种基本类型:数字、字符串、布尔、undefine、null

  函数也是对象,也有属性和方法

  对象有两种类型:1、原生的 ECMAScript  2、主机的,在主机环境中定义,如浏览器

  原生包括:内置对象和用户自定义对象

  主机包括:window对象和所有DOM对象

2、原型

  原型是个对象,每个都会自动获取一个Prototypes属性,该属性指向一个新的空对象

  

3、JSLint

  javascript是一门解析语言没有静态编译时检查,JSLint是检查JavaScript代码质量的工具

第二章  基本技巧

  本章讨论一些高质量代码的核心方法、模式和习惯,如避免全局变量、使用单个变量var的声明、循环中缓存长度变量length和符合代码的约定.

一、编写可维护代码

1、减少全局变量

  全局对象可以在函数外部使用this访问

  mylobal = "hello";    // 全局变量

  console.log(mylobal);

  console.log(window.mylobal);

  console.log(window["mylobal"]);

  全局变量的问题:1、第三方Javascript库  2、广告合作伙伴的脚本 

  释放变量:使用var 创建的变量不能删除,不使用var创建的隐含全局变量可以删除,隐含变量不是真正的变量,而是全局对象的属性,可以通过delete删除

  // 定义三个全局变量

  var global_var = 1;

  global_noar = 2;

  (function(){

    global_fromfunc = 3;

  })

  delete  global_var;  // false

  delete  global_noar;  // true

  delete  global_fromfunc;   // true

2、单一的var模式 

  用一个var在函数顶部进行变量的声明

  var a = 0,

      b = 1;

3、for循环

  当遍历时存储长度,

  var oLi = document.getElementsByTagName("li");

  for(var i=0, len=oLi.length; i< len; i++){    ...   } 

4、不在增加内置的原型

  就是别在内置的构造函数内增加新功能,

5、switch模式

  提高switch的可读性和健壮性

  var inspect = 0, result = "";

  swith(inspect){

    case 0:

      result = "zero"; break;

    case 1:

      result = "one"; break;

    default:

      retult = "unknown";

  }

  如果不写break语句会接着向下执行

6 、parseInt()的数值约定

  parseInt()从一个字符串中获取数值,第二个参数为进制,但如果字符串为0开头就必须写进制,如"08",parseInt("08", 10);否则在ie下会输出

  另一种写法可以:

  +"08";  或者   Number("08")    

第三章  字面量和构造函数

一、对象字面量

  // 定义一个空对象

  var dog = {};

  // 向对象加一个属性和方法

  dog.name = "benji";

  dog.getName = function(){

    return dog.name;

  }

二、构造函灵敏

1 var Person = function(name){
2     this.name = name;
3     this.say = function(){
4         return this.name;
5     }
6 }

  构造函数原型prototyp的作用

  将say方法添加到this中,如果在任何时间调用new Person()时都会在内存中创建一个新的函数,会使效率低下,因为创建多个person实例时也会创建多个say方法,解决方法是将他们添加到原型中prototype

第四章  函数

  函数的两个特点:1、函数是一类对象     2、函数可以提供一个作用域

1、全局作用域:function foo(){}

2、局部作用域:

function local(){

  // 局部作用域

  function bar(){};

  return bar;

}

3、函数表达式  var bar = function(){  ...  }

4、匿名函数  function(){ ... };  没有名称,在自动函数或赋给一个变量时使用

5、即时执行函数  (function(){ ...  })();  

  即时函数的参数:

  (function(who, when){

    console.log("i met" + who);

  })("joe black", new Date())

  即时函数的返回值:

  var result = (function(){

    return 2 + 3;

  })();

第五章  对象创建模式

一、命名空间模式

  命名空间有助于减少全局变量的数量,并且避免合名冲突或过长的名字前缀

 1 <script>
 2     // 不安全代码,比如两个js文件中都有这会MYAPP的创建,这样第二个会覆盖掉第一个对象
 3     var MYAPP = {};
 4     
 5     // 更好的代码风格
 6     if(typeof MYAPP === "undefined"){
 7         var MYAPP = {};
 8     }
 9     
10     // 或者使用
11     var MYAPP = MYAPP || {};
12 </script>

 

  检测代码需要太多的重复工作,如果想定义MYAPP.modeles.module就必须三次检查,每次都需要检查一次对象或属性,可以使用namespace()函数

 1 <script>
 2     MYAPP.namespace("MYAPP.modules.getName");
 3 
 4     // 相当于
 5     MYAPP.modules.getName = {}
 6 
 7 
 8     // 定义一个命名空间函数
 9     var MYAPP = MYAPP || {};
10     MYAPP.namespace = function(sName, newObj){
11         var parts = sName.split("."),
12             argLen = arguments.length,
13             parent = MYAPP;
14 
15         // 剥离最前面的全局变量
16         if(parts[0] == "MYAPP"){
17             parts = parts.slice(1);
18         }
19 
20         if(argLen == 2 && (typeof newObj === "object" || typeof newObj === "function")){
21             for(var i= 0,len = parts.length; i<len; i++){
22                 if(i == len-1){
23                     parent[parts[i]] = $.extend(newObj, parent[parts[i]]);  // 需要继承之前的对象
24                 }
25                 else{
26                     // 如果不存在就创建一个新的属性
27                     if(typeof parent[parts[i]] === "undefined"){
28                         parent[parts[i]] = {};
29                     }
30                 }
31                 parent = parent[parts[i]];
32             }
33         }
34         else{
35             for(var i= 0,len = parts.length; i<len; i++){
36                 // 如果不存在就创建一个新的属性
37                 if(typeof parent[parts[i]] === "undefined"){
38                     parent[parts[i]] = {};
39                 }
40                 parent = parent[parts[i]];
41             }
42         }
43 
44         return parent;
45     }
46 
47     // 1、创建一个命名空间,并初始化命名空间的对象
48     var ssq = MYAPP.namespace("MYAPP.storage.getStorage", {
49 
50         init: function(){
51             this.bindEvent();
52         },
53 
54         bindEvent: function(){
55             console.log("bind All");
56         }
57 
58     });
59 
60     // 调用
61     ssq.init();     // 结果 bind All
62 
63     // 2、创建一个命名空间,初始化时为函数,其实使用对象也完全可以做到,MYAPP.namespace("MYAPP.storage", {showFun: function(){ console.log("匿名函数"); }};
64     var dlt = MYAPP.namespace("MYAPP.storage.showFun", function(){
65         console.log("匿名函数");
66     });
67 
68     // 调用
69     dlt();
70 </script>

二、私有属性、方法

  js中没有私有成员的语法,可以通过闭包,或内部变量来实现这种功能。

 1 <script>
 2     function Gadget(){
 3         
 4         // 私有成员
 5         var name = "iPad";
 6         
 7         // 公有函数
 8         this.getName = function(){
 9             return name;
10         }
11     }
12     
13     var toy = new Gadget();
14     console.log(toy.name);      // undefined
15     console.log(toy.getName()); // iPad
16 </script>

三、特权方法

  就是通过公有方法来访问私有属性,这个方法可以叫做特权方法,上面的例子getName()就是个特权方法.

四、模块模式

 1 <script>
 2     var myObj = (function(){
 3         
 4         // 私有成员
 5         var name = "my, oh my";
 6         
 7         // 实现公有部分
 8         return {
 9             getName: function(){
10                 return name;
11             }
12         }
13     })();
14 </script>

1、构造函数的模块

 1 <script>
 2     var MYAPP = MYAPP || {};
 3 
 4     // 命名空间函数
 5     MYAPP.namespace = function(sName, newObj){
 6         var parts = sName.split("."),
 7                 argLen = arguments.length,
 8                 parent = MYAPP;
 9 
10         // 剥离最前面的全局变量
11         if(parts[0] == "MYAPP"){
12             parts = parts.slice(1);
13         }
14 
15         if(argLen == 2 && (typeof newObj === "object" || typeof newObj === "function")){
16             for(var i= 0,len = parts.length; i<len; i++){
17                 if(i == len-1){
18                     parent[parts[i]] = $.extend(newObj, parent[parts[i]]);  // 需要继承之前的对象
19                 }
20                 else{
21                     // 如果不存在就创建一个新的属性
22                     if(typeof parent[parts[i]] === "undefined"){
23                         parent[parts[i]] = {};
24                     }
25                 }
26                 parent = parent[parts[i]];
27             }
28         }
29         else{
30             for(var i= 0,len = parts.length; i<len; i++){
31                 // 如果不存在就创建一个新的属性
32                 if(typeof parent[parts[i]] === "undefined"){
33                     parent[parts[i]] = {};
34                 }
35                 parent = parent[parts[i]];
36             }
37         }
38 
39         return parent;
40     }
41 
42     MYAPP.namespace("MYAPP.util");      // 创建一个命名空间
43     MYAPP.util =  (function(){
44 
45         // 依赖
46         var oLib = MYAPP.lib;
47         
48         // 创建构造函数
49         function Constr(){
50             this.elements = "siguang";
51         }
52 
53         Constr.prototype.version = "2.0";
54         Constr.prototype.showMessage = function(){
55             console.log(this.version, this.elements);
56         }
57 
58         var oConstr = new Constr();
59 
60         return oConstr;
61     }());
62 
63     console.log(MYAPP);
64 
65     // 调用
66     MYAPP.util.showMessage();        //
67 </script>

2、将全局变量导入到模块中

1 <script>
2     MYAPP.utilities.module = (function(app, global){
3         // 引用全局对象
4         // 可以转成局部变量
5         // 全局应用程序命名空间对象
6     })(MYAPP, this)
7 </script>

第五章  对象创建

一、包装对象

  什么是包装对象

  var str = "ssssssss";   

  str.charAt(3);

  str.substring(2);

  一般方法是调用的是对象,而str是字符串,这就是用到了包装对象,基本类型都有自己的包装对象

  例如:字符串包装类型是String     数字是Number    布尔是Boolean,为什么str可以调用字符串的方法,看下面的例子

1 <script>
2     var str = new String("haha");   // 将字符串到String对象
3     
4     // String的对象的方法
5     String.prototype.charAt = function(n){
6         // ....
7     }
8 </script>

var str = "haha";    // 这句正常定义变量

str.charAt(2);      // 这里基本类型str会找到对应包装对象的方法,将属性及方法赋给基本类型,然后包装对象消失

第六章  代码复用模式

一、类式继承

  类式继承的几种写法:

1、默认模式 

 1 <script>
 2 
 3     // 父构造函数
 4     function Parent(name){
 5         this.name = name || "siguang";
 6         this.age = "30";
 7         this.setGame = function(){
 8             console.log(this.age);
 9         }
10     }
11 
12     // 向该原型添加功能
13     Parent.prototype.say = function(){
14         return this.name;
15     }
16 
17     // 空构造函数
18     function Child(name, age){
19         this.age = age;
20         Parent.call(this, name);        // 改变父类的属性,否则调用的就是父类的属性
21     }
22 
23     // 继承方法,封装了一个
24     inherit(Child, Parent);
25 
26     function inherit(C, P){
27         C.prototype = new P();    // 继承方法
28     }
29 
30     var oC = new Child("lulu", 28);   
31     console.log(oC.say());      // lulu
32     oC.setGame();               // 30
33 </script>

  类式继承继承父类的属性和方法,如果改变其属性需要将改变指针Parent.call(this, name);

  此模式的缺点:同时继承两个对象的属性,将子类传入age时this.age = age 输出的并不是预期的28而还是父类的30,这也就是第二种模式

2、共享原型

  修改了inherit方法

function inherit(C, P){
  C.prototype = P.prototype;    // 缺点:在继承链的某一个子对象或孙对象修改了原型,会影响到父对象和祖先对象.
}

3、临时构造函数

原文地址:https://www.cnblogs.com/couxiaozi1983/p/4083966.html