第一章 JavaScript简介
- JavaScript组成:
- ECMAScript:语法基础
- DOM:文档对象模型
- BOM:浏览器对象模型
第二章 在HTML中使用JavaScript
- 使用
标签引用JavaScript脚本:
- 直接在页面嵌入
- 包含外部JavaScript
- 不设置defer/async属性的前提下,JavaScript文件顺序加载。
-
的位置:
- 标签:加载后呈现页面,有延迟
- 标签最后:页面呈现无延迟
- 在XHTML中,特殊字符需要使用转义符号\转义或者使用
引用JavaScript代码块。
第三章 基本概念
- 语法特性:
- 区分大小写
- 可以没有分号,不推荐
- 使用var定义的变量为局部变量,不使用var修饰的变量是全局变量。
- 松散数据类型,可以使用typeof操作符检测变量数据类型:
- 5种简单类型:
- undefined:声明未初始化的类型,只有本身一个值
- null:表示一个空对象指针,因此typeof null返回object,也只有本身一个值
- boolean:两个值true和false
- number:数字类型,isNaN函数用于判断是否可以转换为数字类型,parseInt/parseFloat函数用于数字类型转换
- string:可以使用单/双引号,效果一样。toString函数用于转换输出对象的字符串表示
- 1种复杂类型:
- object:数据与功能的集合,可以通过new创建对象实例。是所有其它类型的基础。
此外,typeof还能检测函数类型,返回值为function。
PS:typeof的返回值都是小写,大写的类型是基本类型的包装类型,用于类型实例的构造。
- object:数据与功能的集合,可以通过new创建对象实例。是所有其它类型的基础。
- Ojbect类型共有的属性和方法:
- constructor:构造函数,如new Object()
- hasOwnProperty(propertyName):是否含有某属性
- isPrototypeOf(object):参数对象是否是本对象的原型
- propertyIsEnumerable(propertyName):检查给定的属性能否用for-in枚举
- toLocalString/toString:对象的字符串表示
- valueOf:返回对象的字符串/数值或布尔值表示
- 操作符:与Java类似,有符号右移>>,无符号右移>>>。相等操作符:==,比较时会自动进行转换,例如:'55'==55结果是true;全相等操作符:===,值和类型必须都相等,例如:'5555'===555结果是false。此外,null==undefined结果为true,null===undefined结果为false。
- for-in语句:
for(property in expression)
{
statements;
}
- with语句:将代码的作用域设定在一个特定的对象中,不推荐使用
- 函数:
- 不需要指定返回值,会自动推导,也可以通过return返回,return后的语句不会执行
- 不care传递参数的数量/类型。可以使用参数名,也可以使用arguments[n]获取参数
- 没有函数重载,会覆盖。可以通过检查传入函数中参数的类型和数量做出不同的反应,模仿重载。
function functionName(arg0...)
{
statements;
}
第四章 变量、作用域和内存问题
- 5中基本数据类型是按值访问的。引用数据类型是保存在内存中的值,不能直接操作对象的内存空间,实际上操作的是对象的引用。因此,引用类型的值是按引用访问的。
- 引用类型的值,可以为其动态添加/改变/删除属性和方法。
- 函数参数是按值传递的,基本类型会创建局部变量,引用类型传递的是对象的引用,操作时会改变对象本身。
- 判断变量引用类型使用instanceof。
- 延长作用域的语句:
- try-catch语句的catch块
- with语句
- 全局执行环境是windows对象,每个函数拥有自己的执行环境。函数中的变量访问不能跨越自己的执行环境。
- 没有块级作用域:
if(true)
{
var color='blue';
}
alert(color);//提示blue
特别注意for循环中的迭代i,执行完毕后alert(i)是可以输出最终值的。
- 使用var声明的变量自动加入到函数内部的局部环境中,没有使用var声明,自动添加到全局环境中。
- JavaScript的垃圾处理方法:标记清除/引用计数。
- 引用数据类型不使用后,设置为null可以释放其应用。
第五章 引用类型
- Object类型的创建:
- new Object()
- 字面量表示法:var person={name:"Gunner"};属性name的双引号可加可不加。
- Object对象属性的访问可以使用.和[]进行访问,如person.name或者person['name']。
- Array类型表示数组,创建方式:
- new Array()/Array('red','blue')
- 字面量表示法:var color=['red','blue']
- Array常用函数:
- Array.isArray(value):检测是否数组类型
- join():使用不同的分隔符来构建数组字符串
- 栈方法:push()和pop()
- 队列方法:shift()移出第一个元素并返回其值,push()后端添加元素;unshift()前端添加,pop()后端移出元素
- 排序:reverse()和sort()
- concat():基于当前数组添加创建新数组
- slice():数组切片创建新数组
- splice():向数组内部插入项,可以实现删除splice(第一项位置,删除项数目)/插入splice(起始位置,删除项数目=0,插入项)/替换splice(起始位置,删除项数,插入的任意数量的项)功能。splice返回从原始数组删除的项。
- indexOf()/lastIndexOf():查找返回位置
- 迭代方法:
- every():每一项执行,全部为true结果为true
- filter():每一项执行,返回函数结果为true的元素组成的数组
- forEach():每一项执行,无返回值
- map():每一项执行,返回执行结果组成的数组
- some():每一项执行,任一个结果为true则返回true
- reduce():从数组第一项开始,逐个遍历到最后,聚合函数
- reduceRight():从数组最后一项开始,向前遍历到第一项
- Date类型:创建new Date()
- RegExp类型:正则表达式对象,语法:
var expression=/pattern/flags
,其中flags取值g全局/i不区分大小写,m多行模式。创建:
- var pattern1 = /[bc]at/i
- var pattern = new RegExp("[bc]at","i");由于表达式是字符串,所以需要双重转义,例如
\
在字符串中的转义为\\
,在正则表达式中的转义为\\\\
。
RegExp主要方法: - exec():为捕获组设计的,返回第一个匹配信息的数组或者null,返回包括index:匹配项在字符串中的位置和input:正则表达式的需要匹配的字符串
- test():匹配返回true,否则false
- Function类型:函数也是对象,函数调用:sum()
var sum=function(num1,num2){return num1+num2;}
等价于:
function sum(num1,num2){return num1+num2;}
- 函数内部属性:
- arguments:包含传入函数的所有函数和保存函数名的callee属性
- this:引用函数据以执行的环境对象
- 函数的属性和方法:
- length属性:函数希望接受参数的个数
- prototype属性:保存实例方法,如toString()/valueOf()等都保存在prototype的属性中
- apply()和call()方法:在特定的作用域中调用函数,实际是设置函数体内this对象的值。主要用于扩充函数的作用域。
- bind():创建一个函数的实例,其this值会被绑定到传给bind()函数的值。ECMAScript5中定义。
- 基本包装类型:Boolean/Number和String。引用类型与基本包装类型的主要区别是对象的生存期,使用new创建的引用类型的实例,一直保存在内存中,而基本包装类型的对象,只存在代码执行的瞬间,因此不能在运行时添加属性和方法,因为对象已经销毁。
var s1="some text";
var s2=s1.substring(2);
s1是基本类型字符串,但是却可以使用方法,原因是后台解析是自动执行了:
var s1=new String("some text");
var s2=s1.substring(2);
s1=null;//正是这句改变了包装类型的生存期
- Object/Array/String/Date/RegExp等都是内置对象(ECMAScript默认提供,不依赖宿主环境的对象),单体内置对象有Global和Math:
- Global:兜底对象,比如isNaN()/isFinite()/parseFloat()等都是Global对象的方法
- encodeURI()/decodeURI()对URI进行编码
- eval():完整的ECMAScript解析器
- Global对象的属性:Object/Array/Function/Boolean等都是Global对象的属性,表示的是响应对象的构造函数
- window对象:全局作用域中声明的所有变量和函数都是window对象。
- Math:数学方法诸如ceil/floor/round等
第六章 面向对象的程序设计
- JavaScript使用Object创建对象并为其添加属性/方法。
- ECMAScript的两种属性:
- 数据属性:存储数据,有四个描述其行为的特性
- [[Configurable]]:默认true,表示能通过delete删除属性而重新定义属性,能否修改属性的特性或者能否把属性修改为访问器属性。
- [[Enumerable]]:能否通过for-in循环返回属性,默认true
- [[Writable]]:能否修改属性的值,默认ture
- [[Value]]:属性值,默认undefined
不能直接定义,必须使用Object.defineProperty(对象,属性名,描述符)方法修改默认特特性。例如:
var person={};
Object.defineProperty(person,'name',{writable:false,value:'Nicholas'});
- 访问器属性:不包含数据值,包含getter和setter函数,特性如下:
- [[Configurable]:表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性或者能否把属性修改为数据属性默认true
- [[Enumerable]]:能否通过for-in遍历,默认true
- [[Get]]:读取调用,默认undefined
- [[Set]]:写入调用,默认undefined
不能直接定义,必须使用Object.defineProperty(对象,属性名,描述符)方法修改默认属性。例如:
var book={_year;2004,Edition;1};//_开头的属性约定表示只能通过访问器访问
Object.defineProperty(book,'year',
{
get:function(){return this._year;},set:function(newValue){this._year=newValue;}
}
);
- 通过函数Object.defineProperties(待添加或修改的对象,{多个属性内容})定义多个属性。
- 通过Object.getOwnPropertyDescriptor(对象,属性)获取指定属性的描述符。
- 创建对象new和字面量较繁琐,可以使用如下方法创建对象:
- 自定义工厂方法的函数:
function createPerson(name,age)
{
var o=new Object();
o.name=name;
o.age=age;
o.setName=function(){alert(name);};
return o;
}
- 构造函数模式:约定使用大写字母表示构造函数,创建方法可以是new Person()或者直接Person()。
function Person(name,age)
{
this.name=name;
this.age=age;
this.sayName=function(){alert(name);};
}
构造函数的特点是,不同实例会有多个方法function。因此,通常的做法是定义函数,然后在构造函数中传入函数的引用。
- 原型模式:通过函数的prototype属性,让所有对象实例共享它包含的属性和方法。
function Person()={}
Person.prototype.name='Gunner'';
Person.prototype.age=1;
Person.prototype.sayName=function(){alert(this.name)};
var person1=new Person();
person1.sayName();
或者可以省略多次重写prototype:
function Person(0{}
Person.prototype={
name:'Gunner',age:1,sayName;function(){alert(this.name)}
}
原型模式的缺点是数据的共享。对于基本类型数据没有问题,但是对于引用数据类型,会造成不同对象修改某一个后另一个对象的引用数据类型类型的值也做了修改。
- 组合使用构造函数和原型模式:构造函数用于定义实例属性,原型模式用于定义方法和共享的属性
funciton Person(name,age)
{
this.name=name;
this.age=age;
this.friends=['gunner','gonner'];
}
Person.prototype={
constructor:Person,//会自动添加
sayName:function(){alert(this.name);}
};
- 动态原型模式:把所有信息封装到构造函数中,在构造函数中初始化原型。
function Person(name,age)
{
this.name=name;
this.age=age;
if(typeof this.sayName !='function'){
Person.prototype.sayName=function(){alert(this.name)};
}
}
- 寄生构造函数模式:该函数的作用是封装创建对象的代码,然后返回该对象,很像典型的构造函数,但是不使用this对象
function Person(name,age)
{
var o=new Object();
o.name=name;
o.age=age;
o.sayName=function(){alert(this.name);};
return o;
}
- 稳妥构造函数模式。适合在某些安全执行环境下执行,这些环境可能要求不能使用new等限制。类似寄生构造函数模式,两点不同:1。创建新对象实例方法不引用this。2。不使用new操作符调用构造函数。
function Person(name,age)
{
var o=newObject(0;
o.setName=function(){alert(name);};
return o;
}
该模式下除了使用sayName()方法外,没有其它方法能够访问name值。
- 继承:ECMAScript没有函数签名,因此没有接口继承,只支持实现继承。实现继承的方式
- 原型链:基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。
function SuperType(){
this.property=true;
}
SuperType.prototype.getSuperValue=function(){
return this.property;
}
function SubType(){
this.subproperty=false;
}
//继承SuperType
SubType.prototype=new SuperType();
SubType.prototype.getSubValue=function(){
return this.subproperty;
}
var instance=new SubType();
alert(instance.getSuperValue());//true
给原型添加方法的代码一定要放到替换原型的语句之后。并且通过原型链实现继承时,不能使用对象字面量创建原型方法。即不能使用:
SubType.prototype=new SuperType();
SubType.prototype={
getSbuValue:function(){
return this.subproperty;
}
};
原型链的问题与使用原型创建对象的问题类似,即引用对象的共享问题。
- 借用构造函数(constructor stealing):在子类构造函数的内部调用超类型构造函数。
function SuperType(){
this.colors=['red','blue'];
}
function SubType(){
//继承SuperType
SuperType.call(this);
}
借用构造函数的问题是函数不能重用
- 组合继承:将原型链和借用构造函数的技术组合到一起,最常用的的方法。
function SuperType(name){
this.name=name;
this.colors=['red','blue'];
}
SuperType.prototype.sayName=function(){
alert(this.name);
}
function SubType(name,age){
//继承属性
SuperType.call(this,name);
this.age=age;
}
SubType.prototpype=new SuperType();
SubType.prototype.constructor=SubType;
SubType.prototype.sayAge=function(){
alert(this.age);
}
- 原型式继承: 浅复制,引用数据有共享问题。
function object(o){
function F(){}//临时构造函数
F.prototype=o;//将传入的对象作为这个构造函数的原型,实际是对象的浅复制
return new F();
}
ECMAScript5规范化了原型继承模式Object.create(用作新对象原型的对象,[新对象定义额外属性的对象])。第二个参数为空等同于调用object()。
var antherPerson=Object.create(person,{
name:{value:'Gunner'}
});
- 寄生式继承:创建一个仅用于封装继过程的函数,函数在内部以某种方式来增强对象。
function createAnother(original){
var clone=object(original);//通过调用函数创建新对象
clone.sayHi=function(){//以某种方式来增强对象
alert('Hi');
};
return clone;
}
- 寄生组合式继承:组合继承的缺点是会两次调用超类构造函数,并且子类会包含超类对象的全部实例属性。所谓寄生组合式继承,即通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。其背后的基本思路是:不必为了指定子类型的原型而调用超类型的构造函数,我们所需要的无非就是超类型原型的一个副本而已。本质上,就是使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型。
function inheritPrototype(subType, superType){
var prototype = object(superType.prototype); //创建对象
prototype.constructor = subType; //增强对象
subType.prototype = prototype; //指定对象
}
这个函数接收两个参数:子类型构造函数和超类型构造函数。在函数内部,第一步是创建超类型原型的一个副本。第二步是为创建的副本添加 constructor 属性,从而弥补因重写原型而失去的默认的 constructor 属性。最后一步,将新创建的对象(即副本)赋值给子类型的原型。完整代码如下:
function SuperType(name){
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){
alert(this.name);
};
function SubType(name, age){
SuperType.call(this, name);
this.age = age;
}
inheritPrototype(SubType, SuperType);
SubType.prototype.sayAge = function(){
alert(this.age);
};