第一章 简介
一、三种类型模式
设计模式、编码模式、反模式
二、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、临时构造函数