前言:好久以前读过一遍该书,近日得闲,重拾该书,详细研究一方,欢迎讨论指正。node
目录:web
一、精华正则表达式
二、语法编程
三、对象数组
四、函数浏览器
五、继承安全
六、数组服务器
七、正则表达式闭包
八、方法app
九、代码风格
十、优美的特性
附录A 毒瘤
附录B 糟粕
附录C JSLint
附录D 语法图
附录E JSON
正文:
第1章 精华
本书的目的就是要揭示JavaScript中的精华,让你们知道它是一门杰出的动态编程语言。
JavaScript是创建在一些很是优秀的想法和少数很是糟糕的想法之上。
那些优秀的想法包括函数、弱类型、动态对象和富有表现力的对象字面量表示法。
那些糟糕的想法包括基于全局变量的编程模型。
JavaScript的函数是基于词法做用域的顶级对象。
JavaScript是1门弱类型语言。
JavaScript有很是强大的对象字面量表示法。
JavaScript的继承是原型继承。
JavaScript依赖于全局变量来进行链接。
全部编译单元的全部顶级对象被撮合到一个被称为全局对象的命名空间中。
本文贯彻始终都会用到一个method方法去定义新方法:
Function.prototype.method = function (name, func){ this.prototype[name] = func; return this; }
第2章 语法
本章介绍JavaScript的精华部分的语法,并简要地概述其语言结构。
空白
空白可能表现为被格式化的字符或注释的形式。
如:
var that = this;
var 和 that 之间的空格不能移除,可是其余的空格能够移除。
JavaScript提供两种注释形式,一种是用/* */包围的块注释。一种是以//开头的行注释。注释应该优先被用来提升程序的可读性。其中,块注释遇到正则表达式的时候,对于被注释的代码来讲是不安全的。
标识符
标识符由一个字符开头,其后可选择性地加上一个或多个字母、数字或下划线。标识符不能使用保留字。
JavaScript不容许使用保留字来命名变量或参数,同时,不容许在对象字面量中,或者用点运算符提取对象属性时,使用保留字做为对象的属性名。
数字
JavaScript只有一个数字类型。它在内部被表示为64位的浮点数,和Java的double数字类型同样。
它没有分离出整数类型,因此:
1 === 1.0 // true 100 === 1e2 // true
NaN是一个数值,它表示不能产生正常结果的运算结果。NaN不等于任何值,包括它自己。能够用isNaN(number)检测NaN。
Infinity表示全部大于1.79769313486231570e+308的值。
数字拥有方法。JavaScript有一个对象Math,它包含一套做用于数字的方法。例如,能够用Math.floor(number)方法把一个数字转换成一个整数。
字符串
字符串字面量能够被包在一对单引号或双引号中,它可能包含0个或多个字符。
\(反斜线符号)是转义字符。JavaScript在被建立的时候,Unicode是一个16位的字符集,因此JavaScript中的全部字符都是16位的。
JavaScript没有字符类型。要表示一个字符,只需建立一个包含一个字符的字符串便可。
转义字符用来把那些正常状况下不被容许的字符插入到字符串中,好比反斜线、引号和控制字符。\u约定用来指定数字字符编码。
"A" === "\u0041" // true
字符串有一个length属性。例如,"seven".length是5。
字符串是不可变的。
语句
在web浏览器中,每一个<script>标签提供一个被编译且当即执行的编译单元。由于缺乏连接器,JavaScript把它们抛到一个公共的全局命名空间中。
当var语句被用在函数内部时,它定义的是这个函数的私有变量。
语句一般按照从上到下的顺序被执行。JavaScript能够经过条件语句(if 和 switch),循环语句(while\for\do)、强制跳转语句(break\return\throw)和函数调用来改变执行序列。
代码块是包在一对花括号中的一组语句。JavaScript中的代码块不会建立新的做用域,所以变量应该被定义在函数的头部,而不是在代码块中。
下面的值被当作假(6个):
false
null
undefined
空字符串''
数字0
数字NaN
其它全部的值都被当作真,包括true、字符串"false",以及全部的对象。
case从句防止继续执行下一个case,case从句后应该加一个强制跳转语句,break。
for...in语句:
会枚举一个对象的全部属性名(或键名)
var x; var mycars = {}; mycars["name"] = "BMW"; mycars["color"] = "green"; Object.prototype.size = "6meter"; for (x in mycars){ console.log(mycars[x]); //BMW green 6meter
}
一般须要检查object.hasOwnProperty(variable)来肯定这个属性是不是该对象的成员,仍是来自于原型链。
for (x in mycars){ if(mycars.hasOwnProperty(x)){ console.log(mycars[x]); //BMW green
}
}
return语句会致使从函数提早返回。它也能够指定要返回的值,若是没有指定返回表达式,那么返回值是undefined。
typeof运算符产生的值有number、string、boolean、undefined、function、object。
若是运算数是一个数组或null,那么typeof的结果是object,实际上是错的。
函数调用运算符是跟随在函数名后面的一对圆括号。
字面量
对象字面量是一种能够方便地按指定规格建立新对象的表示法。属性名能够是标识符或字符串。
这些名字被当作字面量名而不是变量名来对待,因此对象的属性名在编译时才能知道。
函数
函数字面量定义了函数值,它能够有一个可选的名字,用于递归地调用本身。它能够指定一个参数列表,这些参数就像变量同样,在调用时由传递的实际参数初始化。
函数的主体包括变量定义和语句。
第3章 对象
JavaScript的简单数据类型(5种)包括数字、字符串、布尔值(true和false)、null和undefined。其余全部的值都是对象。
JavaScript中的对象是可变的键控集合。
数组是对象,函数是对象,正则表达式是对象,对象也是对象。
对象是属性的容器,其中每一个属性都拥有名字和值。属性的名字是能够包括空字符串在内的任意字符串。属性值能够是除undefined值以外的任何值。
var obj = { "": 'aa' ; } obj[""]; // aa
JavaScript里的对象是无类型的。它对新属性的名字和属性的值没有限制。对象适合用于聚集和管理数据。对象能够包含其余对象,因此他们能够很容易地表示成树状或图形结构。
JavaScript包含一种原型链的特性,容许对象继承另外一个对象的属性。正确地使用它,能减小对象初始化时消耗的时间和内存。
对象字面量
一个对象字面量就是包围在一对花括号中的零个或多个"名/值"对。对象字面量能够出如今任何容许表达式出现的地方。
var empty_object = {}; var stooge = { "first-name": "Jerome", "last-name": "Howard" }
在对象字面量中,若是属性名是一个合法的JavaScript标识符且不是保留字,则并不强求要用引号括住属性名。如:"first-name"是必须的,first_name是可选的。用逗号来分隔多个"名/值"对。
属性的值能够从包括另外一个对象字面量在内的任意表达式中得到。对象是能够嵌套的。
var flight = { airline: "Oceanic", number: 815, arrival: { IATA: "LAZ", time: "2004-09-23 10:42", city: "Los Angeles" } }
检索
要检索对象里面的值,能够采用在[]后缀中括住一个字符串表达式的方式。
若是字符串表达式是一个字符串字面量,并且它是一个合法的JavaScript标识符且不是保留字,那么也能够用.表示法代替。优先考虑使用.表示法,由于它更紧凑且可读性更好。
stooge["first-time"]; // "Jerome" flight.departure.IATA; // "LAZ"
若是你尝试检索一个并不存在的成员属性的值,将返回undefined。
stooge["middle-name"]; //undefined flight.status; // undefined stooge["FIRST-NAME"]; //undefined
||运算符能够用来填充默认值:
var middle = stooge["middle-name"] || "(none)" var status = flight.status || "unknown";
尝试从undefined的成员属性中取值将会致使TypeError异常。这时能够经过&&运算符来避免错误。
var stooge = { first_name: 'bally' } stooge.last_name; // undefined stooge.last_name.name; // throw "TypeError" stooge.last_name && stooge.last_name.name; // undefined
更新
对象里的值能够经过赋值语句来更新。若是属性名已经存在于对象里,那么这个属性的值就会被替换。
stooge['first-name'] = 'Jome';
若是对象以前没有拥有那个属性名,那么该属性就会被扩充到对象中。
stooge['middle-name'] = 'Lester'; stooge.nickname = 'Curly'; flight.equipment = { model: 'Boeing 777' }; flight.status = 'overdue';
引用
对象经过引用来传递,它们永远不会被复制。
var stooge = { first_name: 'bally' } var x = stooge; // undefined x.nickname = 'Curly' // "Curly" stooge.nickname; "Curly" var a = {}, b = {}, c = {}; a = b = c = {}; //a、b和c都引用同一个空对象
原型
每一个对象都链接到一个原型对象,而且它能够从中继承属性。全部经过对象字面量建立的对象都链接到Object.prototype,它是JavaScript中的标配对象。
当建立一个对象时,能够选择某个对象做为它的原型。
给Object增长一个create方法。这个方法建立一个使用原对象做为其原型的新对象。
if(typeof Object.create !== 'function'){ Object.create = function (o){ var F = function () {}; F.prototype = o; return new F(); } } var another_stooge = Object.create(stooge); another_stooge.nickname; // 'Curly'
原型链接只有在检索值时才被用到,若是咱们尝试去获取对象的某个属性值,但该对象没有此属性名,那么JavaScript会试着从原型对象中获取属性值。若是该原型对象也没有此属性,再从它的原型中寻找,依次类推,直到该过程最后到达终点Object.prototype。
原型关系是一种动态的关系。若是咱们添加一个新的属性到原型中,该属性会当即对全部基于该原型建立的对象可见。
stooge.profession = 'actor'; another_stooge.profession; // 'actor'
反射
检查对象并肯定对象有什么属性。
typeof flight.number; // 'number' typeof flight.status; // 'string' typeof flight.arrival; // 'object' typeof flight.manifest; // 'undefined'
请注意原型链中的任何属性都会产生值。
typeof flight.toString; // 'function' typeof flight.constructor; // 'function'
使用hasOwnProperty方法,若是对象拥有独有的属性,将返回true。hasOwnProperty方法不会检查原型链。
flight.hasOwnProperty('number'); // true flight.hasOwnProperty('constructor'); // false
枚举
for...in语句可用来遍历一个对象中的全部属性名。该枚举过程将会列出全部的属性,包括函数和你可能不关心的原型中的属性。
最经常使用的过滤方法就是hasOwnProperty方法,以及使用typeof来排成函数。
var Stooge = function(){} Stooge.prototype.nick_name = 'nick'; Stooge.prototype.getName = function(){ console.log(this.nick_name) } for(name in another_stooge){ if(typeof another_stooge[name] !== 'function'){ //过滤函数 console.log(name) } }
删除
delete运算符能够用来删除对象的属性。若是对象包含该属性,那么该属性就会被删除。它不会触及原型链中的任何对象。
删除对象的属性可能会让原型链中的属性透现出来。
another_stooge.nickname; // 'Moe'; delete another_stooge.nickname; another_stooge.nickname; // 'Curly'
减小全局变量污染
JavaScript能够很随意地定义全局变量来容纳你的应用的全部资源。遗憾的是,全局变量削弱了程序的灵活性,应该避免使用。
最小化使用全局变量的方法之一是为你的应用只建立一个惟一的全局变量。
var MYAPP = {};
此时,该变量变成了你的应用的容器:
MYAPP.stooge = { "first-name": "Joe", "last-name": "Howard" } MYAPP.flight = { airline: "Oceanic", number: 815, arrival: { IATA: "LAX", time: "2004-09-23 10:42", city: "Los Angeles" } }
只要把全局性的资源都归入一个名称空间之下,你的程序与其余应用程序、组件、类库之间发生冲突的可能性就会显著下降。很明显,MYAPP.stooge指向的是顶层结构。
第4章 函数
JavaScript设计地最出色的就是它的函数的实现。函数用于指定对象的行为。
函数对象
JavaScript中的函数就是对象。
对象字面量产生的对象链接到Object.prototype。
函数对象链接到Function.prototype(该原型对象自己链接到Object.prototype)
每一个函数在建立时都会附加两个隐藏属性:函数的上下文和实现函数行为的代码。
每一个函数对象在建立时都配有一个prototype属性。它的值是一个拥有constructor属性且值为该函数的对象。
constructor指向该对象的构造函数。
function abc(){} abc.constructor; //function Function() { [native code] } var bbc = new abc(); bbc.constructor; //function abc(){}
由于函数是对象,因此他们能够像任何其余的值同样被使用。函数能够保存在变量、对象和数组中。
函数能够被当作参数传递给其余函数,函数也能够再返回函数。并且,由于函数是对象,因此函数也能够拥有方法。
函数的不同凡响在于他们能够被调用。
函数字面量
函数对象经过函数字面量来建立:
//建立一个名为add的变量,并用来把两个数字相加的函数赋值给它 var add = function (a, b){ return a + b; }
函数字面量包括4个部分:
第1部分:保留字function。
第2部分:函数名,能够被省略(匿名函数)。
第3部分:包围在圆括号中的一组参数,在该函数被调用时,初始化为实际提供的值。
第4部分:包围在花括号中的一组语句。这些语句是函数的主体,在函数被调用时执行。
经过函数字面量建立的函数对象包含一个连到外部上下文的链接。这被称为闭包。
调用
调用一个函数会暂停当前函数的执行,传递控制权和参数给新函数。
除了声明时定义的形式参数,每一个函数还接收两个附加的参数:this和arguments。
在JavaScript中一共有4种调用模式:
方法调用模式、函数调用模式、构造器调用模式、apply调用模式。
若是实际参数值过多,超出的参数值会被忽略。
若是实际参数值过少,超出的参数值会被替换为undefined。
对参数值不会进行类型检查:任何类型的值均可以被传递给任何参数。
方法调用模式
当一个函数被保存为对象的一个属性时,咱们称它为一个方法。当一个方法被调用时,this被绑定到该对象。
//建立myObject对象,它有一个value属性和一个increment方法 //increment方法接受一个可选的参数,若是参数不是数字,默认使用1 var myObject = { value: 0, increment: function(inc){ this.value += typeof inc === 'number' ? inc : 1; } } myObject.increment(); myObject.value; // 1 myObject.increment(2); myObject.value; // 2
方法可使用this访问本身所属的对象,因此它能从对象中取值或对对象进行修改。
经过this可取得它们所属对象的上下文的方法称为公共方法。
函数调用模式
当一个函数并不是一个对象的属性时,那么它就是被当作一个函数来调用的。
以此模式调用函数时,this被绑定到全局对象。
var myObject = { value: 0, increment: function(inc){ this.value += typeof inc === 'number' ? inc : 1; } } myObject.double = function(){ var that = this; var helper = function(){ that.value = add(that.value, that.value); //this指向全局对象 } helper(); } function add(a, b){ return a + b; } myObject.increment(2) myObject.double(); myObject.value;
构造器调用模式
JavaScript是一门基于原型继承的语言。这意味着对象能够直接从其余对象继承属性。该语言是无类型的。
若是在一个函数前面带上new来调用,那么背地里将会建立一个链接到该函数的prototype成员的新对象,同时this会被绑定到那个新对象上。
var Quo = function(string){ this.status = string; } Quo.prototype.get_status = function(){ return this.status; } var myQuo = new Quo('confused'); myQuo.get_status(); // "confused"
一个函数,若是建立的目的就是但愿结合new前缀来调用,那它就被称为构造器函数。
不推荐使用这种形式的构造器函数。
Apply调用模式
apply方法让咱们构建一个参数数组传递给调用函数。容许咱们选择this的值。
apply方法接收两个参数。第1个是要绑定给this的值,第2个就是一个参数数组。
var array = [3,4]; function add(a,b){ return a + b; } var sum = add.apply(null, array); sum; // 7 //构造一个包含status成员的对象 var statusObject = { status: 'A-OK' } var status = Quo.prototype.get_status.apply(statusObject);
参数
当函数被调用时,会获得一个免费配送的参数,那就是arguments数组。
函数能够经过此参数访问全部它被调用时传递给它的参数列表,包括那些没有被分配给函数声明时定义的形式参数的多余参数。这使得编写一个无需指定参数个数的函数成为可能。
var sum = function(){ var i, sum = 0; for(i = 0; i < arguments.length; i++){ sum += arguments[i] } return sum; } sum(2,4,6,8); // 20
返回
当一个函数被调用时,它从第一个语句开始执行,并在遇到关闭函数体的}时结束。而后函数把控制权交还给调用该函数的程序。
return语句可用来使函数提早返回。当return语句被执行时,函数当即返回而再也不执行余下的语句。
一个函数老是会返回一个值。若是没有指定返回值,则返回undefined。
若是函数调用时在前面加上了new前缀,且返回值不是一个对象,则返回this(该新对象)。
异常
var add = function(a, b){ if(typeof a !== 'number' || typeof b !== 'number'){ throw { name : 'TypeError', message: 'add needs numbers' } } return a + b; } var try_it = function(){ try { add('seven'); } catch(e){ console.log(e.name + ": " + e.message) } } try_it(); // TypeError: add needs numbers
一个try语句只会有一个捕获全部异常的catch代码块。若是你的处理手段取决于异常的类型,那么异常处理器必须检查异常对象的name属性来肯定异常的类型。
扩充类型的功能
给Function.prototype增长方法来使得该方法对全部函数可用:
Function.prototype.method = function(name, func){ this.prototype[name] = func; return this; }
经过给Function.prototype增长了一个method方法,下次给对象增长方法的时候就没必要键入prototype了。
经过给Number.prototype增长一个integer方法来取整:
Number.method('integer', function(){ return Math[this < 0 ? 'ceil' : 'floor'](this); }); (-10 / 3).integer();
增长一个移除字符串首尾空白的方法:
String.method('trim', function(){ return this.replace(/^\s+|\s+$/g, ''); //\s匹配任何不可见字符,包括空格、制表符、换页符等 })
由于JavaScript原型继承的动态本质,新的方法马上被赋予到全部的对象实例上。
基本类型的原型是公共结构,因此在类库混用时务必当心。一个保险的作法就是只在肯定没有该方法时才添加它。
Function.prototype.method = function(name, func){ if(!this.prototype[name]){ this.prototype[name] = func; } return this; }
递归
递归函数能够很是高效地操做树形结构,好比浏览器端的文档对象模型(DOM),每次递归调用时处理指定的树的一小段。
//定义walk_the_DOM函数,它从某个指定的节点开始,按HTML源码中的顺序访问该树中的每一个节点 //它会调用一个函数,并依次传递每一个节点给它 //walk_the_DOM调用自身去处理每个子节点 var walk_the_DOM = function walk(node, func){ func(node); node = node.firstChild; while(node){ walk(node, func); node = node.nextSibling; } } //定义一个getElementsByAttribute函数它以一个属性名称字符串和一个可选的匹配值做为参数 //它调用walk_the_DOM,传递一个用来查找节点属性名的函数做为参数 //匹配的节点会累加到一个结果数组中 var getElementsByAttribute = function (att, value) { var results = []; walk_the_DOM(document.body, function(node){ var actual = node.nodeType === 1 && node.getAttribute(att); if(typeof actual === 'string' && (actual === value || typeof value !== 'string')){ results.push(node); } }) } getElementsByAttribute('class','body')
做用域
JavaScript不支持块级做用域,支持函数做用域。
闭包
做用域的好处是内部函数能够访问定义它们的外部函数的参数和变量(除了this和arguments)。
一个更有趣的情形是内部函数拥有比它的外部函数更长的生命周期
构造一个myObject对象,拥有一个value属性和一个increment方法,假定咱们但愿保护该值不会被非法更改。
经过调用一个函数的形式去初始化myObject,该函数会返回一个对象字面量。
函数里定义了一个value变量。该变量对increment和getValue来讲是可用的,但函数的做用域使得它对其余的程序来讲老是不可见的。
var myObject = (function(){ var value = 0; return { increment: function(inc){ value += typeof inc === 'number' ? inc : 1; }, getValue: function(){ return value; } } }())
status是私有属性,定义另外一种形式的Quo函数:
var quo = function(status){ return { get_status: function(){ return status; } } } //构造一个quo实例 var myQuo = quo('amazed'); myQuo.get_status();
备注:这个quo函数被设计成无须在前面加上new来调用,因此名字也没有大写。
来看一个更有趣的例子:
//定义一个函数,它设置一个DOM节点为黄色,而后把它渐变为白色 var fade = function(node){ var level = 1; var step = function(){ var hex = level.toString(16); node.style.backgroundColor = '#FFFF' + hex + hex; if(level < 15){ level += 1; setTimeout(step, 100); } } setTimeout(step, 100); } fade(document.body);
理解内部函数访问外部函数的实际变量而无须复制是很重要的。
var add_the_handlers = function(nodes){ var i; for(i = 0; i < nodes.length; i++){ nodes[i].onclick = function(e){ alert(i) } } }
add_the_handlers函数的本意是想传递给每一个事件处理器一个惟一值,但它未能达到目的,由于事件处理器函数绑定了变量i自己,而不是函数在构造时的变量i的值。
var add_the_handlers = function(nodes){ var helper = function(i){ return function(e){ alert(i) } } var i; for(i = 0;i < nodes.length; i++){ nodes[i].onclick = helper(i); } }
避免在循环中建立函数,在循环以外建立一个辅助函数,让这个辅助函数再返回一个绑定了当前i值的函数。
回调
发起异步请求,提供一个当服务器的响应到达时随即触发的回调函数。
异步函数当即返回,这样客户端就不会被阻塞。
request = prepare_the_request(); send_request_asynchronously(request, function(response){ display(resposne); })
模块
咱们可使用函数和闭包来构造模块。
模块是一个提供接口却隐藏状态与实现的函数或对象。
经过使用函数产生模块,几乎能够彻底摒弃全局变量的使用。
Function.prototype.method = function(name, func){ if(!this.prototype[name]){ this.prototype[name] = func; } return this; } String.method('deentityfy', function(){ var entity = { quot: '"', lt: '<', gt: '>' }; return function(){ return this.replace(/&([^&;]+);/g, function(a,b){ var r = entity[b]; return typeof r === 'string' ? r : a; } ) } }()) '<">'.deentityfy();
模块模式的通常形式是:一个定义了私有变量和函数的函数,利用闭包建立能够访问私有变量和函数的特权函数,最后返回这个特权函数,或者把它们保存到一个能够访问到的地方。
使用模块模式能够彻底摒弃全局变量的使用,它促进了信息隐藏和其余优秀的设计实践。
对于应用程序的封装,或者构造其余单例对象,模块模式很是有效。
模块模式能够用来产生安全的对象,假定咱们想要构造一个用来产生序列号的对象:
var serial_maker = function(){ //返回一个用来产生惟一字符串的对象 //惟一字符串由两部分组成,前缀+序列号 //该对象包含一个设置前缀的方法,一个设置序列号的方法 // 和一个产生惟一字符串的gensym方法 var prefix = ''; var seq = 0; return { set_prefix: function(p){ prefix = String(p); }, set_seq: function(s){ seq = s; }, gensym: function(){ var result = prefix + seq; seq += 1; return result; } } } var seqer = serial_maker(); seqer.set_prefix('Q'); seqer.set_seq(1000); var unique = seqer.gensym();
seqer包含的方法没有用到this或that,所以没有办法损害seqer。
级联
有一些方法没有返回值,若是让这些方法返回this而不是undefined,就能够启用级联。
如:
getElement('myBoxDiv') .move(350,150) .width(100) .height(100)
柯里化
函数也是值,从而咱们能够用更有趣的方式操做函数值。柯里化容许咱们把函数与传递给它的函数相结合,,产生一个新的函数。
Function.method('curry', function(){ var slice = Array.prototype.slice, args = slice.apply(arguments), that = this; return function(){ return that.apply(null, args.concat(slice.apply(arguments))) } }) function add(a,b){ return a + b; } var add1 = add.curry(1); add1(6); // 7
第5章
JavaScript是一门基于原型的语言,这意味着对象直接从其余对象继承。
伪类
JavaScript不直接让对象从其余对象继承,反而插入了一个多余的间接层:经过构造器函数产生对象。
当一个函数对象被建立时,Function构造器产生的函数对象会运行相似这样的一些代码:
this.prototype = {constructor: this};
新函数对象被赋予一个prototype属性,它的值是一个包含constructor属性且属性值为该函数的对象。
这个prototype对象是存放继承特征的地方。由于JavaScript语言没有提供一种方法去肯定哪一个杉树是打算用来作构造器的,因此每一个函数都会获得一个prototype对象。
采用构造器调用模式,用new前缀去调用一个函数,函数执行的方式会被修改。
若是new运算符是一个方法而不是一个运算符,可能会像这样执行:
Function.method('new',function(){ //建立一个新对象,它继承自构造器函数的原型对象 var that = Object.create(this.prototype); //调用构造器函数,绑定this到新对象上 var other = this.apply(that, arguments); //若是它的返回值不是一个对象,就返回该对象 return (typeof other === 'object' && other) || that; })
定义一个构造器并扩充它的原型:
var Mammal = function(name){ this.name = name; } Mammal.prototype.get_name = function(){ return this.name; } Mammal.prototype.says = function(){ return this.sayings || ''; }
如今能够构造一个实例:
var myMammal = new Mammal('Herb the Mammal'); var name = myMammal.get_name();
构造另外一个伪类来继承Mamal,这是经过定义它的constructor函数并替换它的prototype为一个Mammal的实例。
var Cat = function(name){ this.name = name; this.saying = 'meow'; } //替换Cat.prototype为一个新的Mammal实例 Cat.prototype = new Mammal(); //扩充新原型对象,增长purr和get_name方法 Cat.prototype.purr = function(n){ var i, s = ''; for(i = 0; i < n; i++){ if(s){ s += '-'; } s += 'r'; } return s; } Cat.prototype.get_name = function(){ return this.says() + ' ' + this.name + ' ' + this.says(); } var myCat = new Cat('Herny'); var says = myCat.says(); var purr = myCat.purr(5); var name = myCat.get_name();
伪类模式本意是想向面向对象靠拢,但它看起来格格不入。咱们能够隐藏一些丑陋的细节,经过使用method方法来定义一个inherits方法实现。
Function.method('inherits', function(Parent){ this.prototype = new Parent(); return this; })
这里,inherits和method方法都返回this,能够采用级联的形式编程。
如今能够只用一行语句构造咱们的cat对象。
var Cat = function(name){ this.name = name; this.saying = 'memo' } .inherits(Mammal) .method('purr', function(n){ var i, s = ''; for(i = 0; i < n; i++) if(s){ s += '-'; } s += 'r'; } return s; }) .method('get_name',function(){ return this.says() + ' ' + this.name + ' ' + this.says(); })
原型
先用对象字面量去构造一个有用的对象:
var myMammal = { name: 'Herb the Mammal', get_name: function(){ return this.name; }, says: function(){ return this.saying || ''; } }
一旦有了一个想要的对象,就能够利用Object.create方法构造出更多的实例。
var myCat = Object.create(myMammal); myCat.get_name
函数化
应用模块模式来保护私有变量。
咱们从构造一个生成对象开始,以小写字母开头来命名它,由于它并不须要使用new前缀。
该函数包括4个步骤:
一、建立一个新对象
二、有选择地定义私有实例变量和方法
三、给这个新对象扩充方法
四、返回那个新对象
var constructor = function(spec , my){ var that,其余的私有变量; my = my || {}; 把共享的变量和函数添加到my中 that = 一个新对象 添加给that特权方法 return that; }
把这个模式应用到mammal中。
var mamal = funtion(spec){ var that = {}; that.get_name = function(){ return spec.name; }; that.says = function(){ return spec.saying || ''; } return that; } var myMammal = mammal({name: 'Herb'})
函数化模式还给咱们提供了一个处理父类的方法。
咱们会构造一个superior方法,它取得一个方法名并返回调用那个方法的函数。该函数会调用原来的方法,尽管属性已经变化了。
Object.method('superior', function(){ var that = this,method = that[name]; return function(){ return method.apply(that, arguments); } })
第6章
数组字面量
var empty= []; var numbers = [ 'zero','one' ]
对象字面量:
var numbers_object = { '0': 'zero', '1': 'one' }
明显的差别:
numbers继承自Array.prototype
numbers_object继承自Object.prototype
长度
每一个数组都有一个length属性。
若是用大于或等于当前length的数字做为下标来存储一个元素,那么length值会被增大,不会数组越界。
删除
因为JavaScript的数组其实就是对象,因此delete运算符能够用来从数组中移除元素。
delete numbers[2].//杀出以后就是undefined
JavaScript数组有一个splice方法。它能够对数组作个手术,删除一些元素并将它们替换为其余的元素。
numbers.splice(2,1); //第1个参数是数组中的1个序号,第2个参数是要删除的元素个数,任何额外的参数会在序号那个点的位置被插入到数组中。
枚举
for能够避免for..in带来的问题
var i; for(i = 0; i < myArray.length; i++){ myArray[i] }
容易混淆的地方
当属性名是小而连续的整数时,应该使用数组,不然,使用对象。
判断一个对象是不是数组:
var is_array = function(value){ return Object.prototype.toString.apply(value) === '[object Array]' }
方法
JavaScript提供了一套可用的方法,是被存储在Array.prototype中的函数。
给array增长一个方法,容许咱们对数组进行计算。
Array.method('reduce', function(f,value){ var i; for(i = 0; i < this.length; i++){ value = f(this[i], value) } return value; }) //建立一个数组 var data = [4,8,15,16]; //定义两个简单的函数 var add = fucntion(a, b){ return a+b; } var mult = function(a, b){ return a * b; } var sum = data.reduce(add, 0); var product = data.reduce(mult, 1);
由于数组自己就是对象,因此能够直接给一个单独的数组添加方法:
//给data数组添加一个total方法 data.total = function(){ return this.reduce(add, 0) } total = data.total;
指定初始值
Array.dim(a,b)建立a个b元素
Array.dim = function(dimension, initial){ var a = [],i; for(i = 0; i < dimension; i++){ a[i] = initial; } return a; } var myArray = Array.dim(10,0);
构造一个矩阵
Array.matrix = function(m, n, initial){ var a, i, j, mat = []; for(i = 0;i < m; i++){ a = []; for(j = 0;j < n; j++){ a[j] = initial; } mat[i] = a; } return mat; }
构造一个单位矩阵
Array.identity = function(n){ var i,mat = Array.matrix(n,n,0); for(i = 0;i < n; i++){ mat[i][i] = 1; } return mat; } myMatrix = Array.identity(4); myMatrix[3][3]; // 1
第7章 正则表达式