数据类型
原始数据类型
弱类型特征
- number
- string
- boolean
- null
- undefined
对象类型
- Function
- Array
- Date
- …
隐式转化
- ‘+’ 字符串拼接
包装类型
当基本数据类型以对象的形式访问的时候,会产生一个临时的包装对象,在访问结束以后会销毁1
2
3
4var str = "string";
str.length; //有效 值为6
str.b = 3;
str.b; //undefined 访问结束后 对象被销毁了
类型检测
typeof //函数对象和基本数据类型的判断
1
2
3function "function"
null "object"
undefined "undefined"instanceof //适用于对象 在不同iframe和window间检测时失效
- Object.prototype.toString() //内置对象和基元类型
- constructor //
- duck type //
block
没有块级作用域
严格模式
语法
1 | 'use strict' |
区别
- 不允许未声明变量赋值
- arguments变为参数的静态副本
- delete参数、函数名报错
- delete不可配置的属性报错
- 对象字面量重复属性名报错
- 禁止八进制字面量
- eval ,arguments变为关键字
- eval独立作用域
- 不允许使用with
- 一般函数调用时(不是对象方法的调用,也不使用apply/call/bind等修改this)this指向null,而不是全局对象
- 如使用apply/call,当传入null或者undefined时,this将指向null或者undefined而不是全局对象
- 试图修改不可写属性(writable == false),报错而不是忽略
- argument.caller,arguments.callee禁用
数组
大小范围:2^23-1
创建数组
1
2
3
4
5
6字面量:
var students = [{name: 'Bosn',age : 27},{name : 'Nunnly', age:3}]
值得注意的地方 var arr1 = [,,];//表示两个未定义的变量
new Array():
var arr = new Array(100);
var arr = new Array(1,2,3);数组操作
1
2
3
4
5
6头部操作:
unshift();//头部插入
shift();//头部删除
尾部操作:
push(); //尾部插入
pop(); //尾部删除数组迭代
1
2
3for(i in arr){}
for(i = 0; i < arr.length;i++)
arr[i];数组方法
ES5 = IE9及其以上1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17{} => Object.prototype;
[] => Array.prototype;
Array.prototype.join(); //将数组转化成字符串
Array.prototype.reverse();//将数组逆序
Array.prototype.sort(); //排序
Array.prototype.concat(); //数组合并
Array.prototype.slice(); //返回部分数组 -4 表示倒数第几个元素
Array.prototype.splice(); //数组拼接
Array.prototype.forEach(ES5); //遍历数组
Array.prototype.map(ES5); //数组映射 不修改原数组
Array.prototype.filter(ES5); //数组过滤
Array.prototype.every(ES5); //每一个元素都要满足一定的条件
Array.prototype.some(ES5); //某一个元素满足一定的条件
Array.prototype.reduce/reduceRight(ES5); //可以求和或者找最大最小值
Array.prototype.indexOf/lastIndexOf(ES5); //数组检索
Array.isArray(ES5);
Array.prototype.join.call(str,"-");
JavaScript 创建对象的七种模式
工厂模式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18这种模式抽象了创建具体对象的过程
考虑到在 ECMAScript 中无法创建类,开发人员就发明了一种函数,用函数来封装以特定接口创建对象的细节
function createPerson(name, age, job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
alert(this.name);
};
return o;
}
var person1 = createPerson("Nicholas", 29, "Software Engineer");
var person2 = createPerson("Greg", 27, "Doctor");
函数 createPerson() 能够根据接受的参数来构建一个包含所有必要信息的 Person 对象。
可以无数次地调用这个函数,而每次它都会返回一个包含三个属性一个方法的对象。
工厂模式虽然解决了创建多个相似对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型)。
随着 JavaScript的发展,又一个新模式出现了。构造函数模式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53ECMAScript 中的构造函数可用来创建特定类型的对象,例如Object 和 Array 这样的原生构造函数
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function(){
alert(this.name);
};
}
var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");
在这个例子中, Person() 函数取代了 createPerson() 函数。
我们注意到, Person() 中的代码除了与 createPerson() 中相同的部分外,还存在以下不同之处:
没有显式地创建对象;
直接将属性和方法赋给了 this 对象;
没有 return 语句。
要创建 Person 的新实例,必须使用 new 操作符。以这种方式调用构造函数实际上会经历以下 4个步骤:
(1) 创建一个新对象;
(2) 将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象) ;
(3) 执行构造函数中的代码(为这个新对象添加属性) ;
(4) 返回新对象。
在前面例子的最后, person1 和 person2 分别保存着 Person 的一个不同的实例。这两个对象都有一个 constructor (构造函数)属性,该属性指向 Person ,如下所示。
alert(person1.constructor == Person); //true
alert(person2.constructor == Person); //true
但是,提到检测对象类型,还是 instan-
ceof 操作符要更可靠一些。 我们在这个例子中创建的所有对象既是 Object 的实例, 同时也是 Person的实例,这一点通过 instanceof 操作符可以得到验证。
alert(person1 instanceof Object); //true
alert(person1 instanceof Person); //true
alert(person2 instanceof Object); //true
alert(person2 instanceof Person); //true
构造函数的问题
使用构造函数的主要问题,就是每个方法都要在每个实例上重新创建一遍
person1 和 person2 都有一个名为 sayName() 的方法,但那两个方法不是同一个 Function 的实例
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = new Function("alert(this.name)"); // 与声明函数在逻辑上是等价的
}
以下代码可以证明这一点。
alert(person1.sayName == person2.sayName); //false
因此,大可像下面这样,通过把函数定义转移到构造函数外部来解决这个问题。
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = sayName;
}
function sayName(){
alert(this.name);
}
var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");原型模式
1
2
3
4
5我们创建的每个函数都有一个 prototype (原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。
如果按照字面意思来理解,那么 prototype 就是通过调用构造函数而创建的那个对象实例的原型对象。
使用原型对象的好处是可以让所有对象实例共享它所包含的属性和方法。
换句话说,不必在构造函数中定义对象实例的信息,而是可以将这些信息直接添加到原型对象中。
但与构造函数模式不同的是,新对象的这些属性和方法是由所有实例共享的。组合使用构造函数模式和原型模式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24创建自定义类型的最常见方式,就是组合使用构造函数模式与原型模式。
构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。
结果,每个实例都会有自己的一份实例属性的副本,但同时又共享着对方法的引用,最大限度地节省了内存。
另外,这种混成模式还支持向构造函数传递参数;可谓是集两种模式之长。
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.friends = ["Shelby", "Court"];
}
Person.prototype = {
constructor : Person,
sayName : function(){
alert(this.name);
}
}
var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");
person1.friends.push("Van");
alert(person1.friends); //"Shelby,Count,Van"
alert(person2.friends); //"Shelby,Count"
alert(person1.friends === person2.friends); //false
alert(person1.sayName === person2.sayName); //true
是目前在 ECMAScript中使用最广泛、认同度最高的一种创建自定义类型的方法动态原型模式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17动态原型模式致力于解决这样一个问题,它把所有信息都封装在了构造函数中,而通过在构造函数中初始化原型(仅在必要的情况下) ,又保持了同时使用构造函数和原型的优点。换句话说,可以通过检查某个应该存在的方法是否有效,来决定是否需要初始化原型。
function Person(name, age, job){
//属性
this.name = name;
this.age = age;
this.job = job;
// 方法
if (typeof this.sayName != "function"){
Person.prototype.sayName = function(){
alert(this.name);
};
}
}
var friend = new Person("Nicholas", 29, "Software Engineer");
friend.sayName();
注意构造函数代码中加粗的部分。这里只在 sayName() 方法不存在的情况下,才会将它添加到原型中。
使用动态原型模式时,不能使用对象字面量重写原型。前面已经解释过了,如果在已经创建了实例的情况下重写原型,那么就会切断现有实例与新原型之间的联系。寄生构造函数模式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28通常,在前述的几种模式都不适用的情况下,可以使用寄生(parasitic)构造函数模式。这种模式的基本思想是创建一个函数,该函数的作用仅仅是封装创建对象的代码,然后再返回新创建的对象;但从表面上看,这个函数又很像是典型的构造函数。
function Person(name, age, job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
alert(this.name);
};
return o;
}
var friend = new Person("Nicholas", 29, "Software Engineer");
friend.sayName(); //"Nicholas"
除了使用 new 操作符并把使用的包装函数叫做构造函数之外, 这个模式跟工厂模式其实是一模一样的。
工厂模式为
function createPerson(name, age, job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
alert(this.name);
};
return o;
}
var person1 = createPerson("Nicholas", 29, "Software Engineer");
var person2 = createPerson("Greg", 27, "Doctor");
关于寄生构造函数模式,有一点需要说明:首先,返回的对象与构造函数或者与构造函数的原型属性之间没有关系;也就是说,构造函数返回的对象与在构造函数外部创建的对象没有什么不同。为此,不能依赖 instanceof 操作符来确定对象类型。 由于存在上述问题, 我们建议在可以使用其他模式的情况下,不要使用这种模式。稳妥构造函数模式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20所谓稳妥对象,指的是没有公共属性,而且其方法也不引用 this 的对象。
稳妥对象最适合在一些安全的环境中 (这些环境中会禁止使用 this 和 new ) , 或者在防止数据被其他应用程序 (如 Mashup程序)改动时使用。
稳妥构造函数遵循与寄生构造函数类似的模式,但有两点不同:
一是新创建对象的实例方法不引用 this ;
二是不使用 new 操作符调用构造函数。按照稳妥构造函数的要求,可以将前面的 Person 构造函数重写如下。
function Person(name, age, job){
//创建要返回的对象
var o = new Object();
//可以在这里定义私有变量和函数
//添加方法
o.sayName = function(){
alert(name);
};
//返回对象
return o;
}
var friend = Person("Nicholas", 29, "Software Engineer");
friend.sayName(); //"Nicholas"
注意, 在以这种模式创建的对象中, 除了使用 sayName() 方法之外, 没有其他办法访问 name 的值。
与寄生构造函数模式类似, 使用稳妥构造函数模式创建的对象与构造函数之间也没有什么关系,因此 instanceof 操作符对这种对象也没有意义。