JavaScript深入理解-1

原始类型

ECMAScript 有 5 种原始类型(primitive type),即 Undefined、Null、Boolean、Number 和 String。

typeof 运算符

对变量或值调用 typeof 运算符将返回下列值之一:

undefined - 如果变量是 Undefined 类型的
boolean - 如果变量是 Boolean 类型的
number - 如果变量是 Number 类型的
string - 如果变量是 String 类型的
object - 如果变量是一种引用类型或 Null 类型的

Object对象

对象是由 new 运算符加上要实例化的对象的名字创建的。例如,下面的代码创建 Object 对象的实例:

1
var o = new Object();

对象类型有Funcation,Array,Date…
尽管括号不是必需的,但是为了避免混乱,最好使用括号。
ECMAScript 中的所有对象都由这个对象继承而来,Object 对象中的所有属性和方法都会出现在其他对象中,所以理解了 Object 对象,就可以更好地理解其他对象

Object 对象具有下列属性:

constructor
对创建对象的函数的引用(指针)。对于 Object 对象,该指针指向原始的 Object() 函数。
Prototype
对该对象的对象原型的引用。对于所有的对象,它默认返回 Object 对象的一个实例。

Object 对象还具有几个方法:

hasOwnProperty(property)
判断对象是否有某个特定的属性。必须用字符串指定该属性。(例如,o.hasOwnProperty(“name”))
IsPrototypeOf(object)
判断该对象是否为另一个对象的原型。
PropertyIsEnumerable
判断给定的属性是否可以用 for…in 语句进行枚举。
ToString()
返回对象的原始字符串表示。对于 Object 对象,ECMA-262 没有定义这个值,所以不同的 ECMAScript 实现具有不同的值。
ValueOf()
返回最适合该对象的原始值。对于许多对象,该方法返回的值都与 ToString() 的返回值相同。

Boolean 对象

1
2
var oFalseObject = new Boolean(false);
var bResult = oFalseObject && true; //输出 true

在 Boolean 表达式中,所有对象都会被自动转换为 true,所以 oFalseObject 的值是 true。然后 true 再与 true 进行 AND 操作,结果为 true。

Number 对象

1
2
var oNumberObject = new Number(68);
var iNumber = oNumberObject.valueOf();

toFixed() 方法

toFixed() 方法返回的是具有指定位数小数的数字的字符串表示

1
2
var oNumberObject = new Number(68);
alert(oNumberObject.toFixed(2)); //输出 "68.00"

toExponential() 方法

与格式化数字相关的另一个方法是 toExponential(),它返回的是用科学计数法表示的数字的字符串形式。

1
2
var oNumberObject = new Number(68);
alert(oNumberObject.toExponential(1)); //输出 "6.8e+1",它表示 6.8x10^1

toPrecision() 方法

toPrecision() 方法根据最有意义的形式来返回数字的预定形式或指数形式。它有一个参数,即用于表示数的数字总数(不包括指数)。

1
2
3
4
var oNumberObject = new Number(68);
alert(oNumberObject.toPrecision(1)); //输出 "7e+1",以另外的形式表示即 70
alert(oNumberObject.toPrecision(2)); //输出 "68"
alert(oNumberObject.toPrecision(3)); //输出 "68.0"

String 对象

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
var oStringObject = new String("hello world");
alert(oStringObject.valueOf() == oStringObject.toString()); //输出 "true"
/*length 属性*/
alert(oStringObject.length); //输出 "11"
/*charAt() 和 charCodeAt() 方法*/
alert(oStringObject.charAt(1)); //输出 "e"
alert(oStringObject.charCodeAt(1)); //输出 "101"
/*concat() 方法*/
var sResult = oStringObject.concat("world");
alert(sResult); //输出 "hello world"
alert(oStringObject); //输出 "hello "
/*indexOf() 和 lastIndexOf() 方法*/
alert(oStringObject.indexOf("o")); 输出 "4"
alert(oStringObject.lastIndexOf("o")); 输出 "7"
/*localeCompare() 方法*/
alert(oStringObject.localeCompare("brick")); //输出 "1"
alert(oStringObject.localeCompare("yellow")); //输出 "0"
alert(oStringObject.localeCompare("zoo")); //输出 "-1"
/*slice() 和 substring()*/
alert(oStringObject.slice("3")); //输出 "lo world"
alert(oStringObject.substring("3")); //输出 "lo world"
alert(oStringObject.slice("3", "7")); //输出 "lo w"
alert(oStringObject.substring("3", "7")); //输出 "lo w"
/*toLowerCase()、toLocaleLowerCase()、toUpperCase() 和 toLocaleUpperCase()*/
alert(oStringObject.toLocaleUpperCase()); //输出 "HELLO WORLD"
alert(oStringObject.toUpperCase()); //输出 "HELLO WORLD"
alert(oStringObject.toLocaleLowerCase()); //输出 "hello world"
alert(oStringObject.toLowerCase()); //输出 "hello world"
/*instanceof 运算符*/
alert(oStringObject instanceof String); //输出 "true"

arguments 对象

在函数代码中,使用特殊对象 arguments,开发者无需明确指出参数名,就能访问它们。

1
2
3
4
5
6
7
function howManyArgs() {
alert(arguments.length);
}
//依次显示 "2"、"0" 和 "1"。
howManyArgs("string", 45);
howManyArgs();
howManyArgs(12);

Function 对象(类)

Function 类可以表示开发者定义的任何函数。
用 Function 类直接创建函数的语法如下:

1
var function_name = new function(arg1, arg2, ..., argN, function_body)

在上面的形式中,每个 arg 都是一个参数,最后一个参数是函数主体(要执行的代码)。这些参数必须是字符串。

Function 对象的 length 属性

1
2
3
4
5
6
7
8
function doAdd(iNum) {
alert(iNum + 10);
}
function sayHi() {
alert("Hi");
}
alert(doAdd.length); //输出 "1"
alert(sayHi.length); //输出 "0"

函数 doAdd() 定义了一个参数,因此它的 length 是 1;sayHi() 没有定义参数,所以 length 是 0。

Function 对象的方法

Function 对象也有与所有对象共享的 valueOf() 方法和 toString() 方法。这两个方法返回的都是函数的源代码,在调试时尤其有用

1
2
3
4
function doAdd(iNum) {
alert(iNum + 10);
}
document.write(doAdd.toString());

面向对象

对象
基于代码的名词(人、地点或事物)的表示

每个对象都由类定义,可以把类看做对象的配方。类不仅要定义对象的接口(interface)(开发者访问的属性和方法),还要定义对象的内部工作(使属性和方法发挥作用的代码)。编译器和解释程序都根据类的说明构建对象。
实例
程序使用类创建对象时,生成的对象叫作类的实例(instance)。对类生成的对象的个数的唯一限制来自于运行代码的机器的物理内存。每个实例的行为相同,但实例处理一组独立的数据。由类创建对象实例的过程叫做实例化(instantiation)。

面向对象语言的要求

一种面向对象语言需要向开发者提供四种基本能力:

封装 - 把相关的信息(无论数据或方法)存储在对象中的能力
聚集 - 把一个对象存储在另一个对象内的能力
继承 - 由另一个类(或多个类)得来类的属性和方法的能力
多态 - 编写能以多种方法运行的函数或方法的能力

对象的构成

在 ECMAScript 中,对象由特性(attribute)构成,特性可以是原始值,也可以是引用值。如果特性存放的是函数,它将被看作对象的方法(method),否则该特性被看作对象的属性(property)。

声明和实例化

1
2
var oObject = new Object();
var oStringObject = new String();

对象废除

1
2
3
var oObject = new Object;
// do something with the object here
oObject = null;

关键字 this

this 的功能

关键字 this 总是指向调用该方法的对象

使用 this 的原因

为什么使用 this 呢?因为在实例化对象时,总是不能确定开发者会使用什么样的变量名。使用 this,即可在任何多个地方重用同一个函数。

1
2
3
4
5
6
7
8
9
10
11
function showColor() {
alert(this.color);
};
var oCar1 = new Object;
oCar1.color = "red";
oCar1.showColor = showColor;
var oCar2 = new Object;
oCar2.color = "blue";
oCar2.showColor = showColor;
oCar1.showColor(); //输出 "red"
oCar2.showColor(); //输出 "blue"

在上面的代码中,首先用 this 定义函数 showColor(),然后创建两个对象(oCar1 和 oCar2),一个对象的 color 属性被设置为 “red”,另一个对象的 color 属性被设置为 “blue”。两个对象都被赋予了属性 showColor,指向原始的 showColor () 函数(注意这里不存在命名问题,因为一个是全局函数,而另一个是对象的属性)。调用每个对象的 showColor(),oCar1 输出是 “red”,而 oCar2 的输出是 “blue”。这是因为调用 oCar1.showColor() 时,函数中的 this 关键字等于 oCar1。调用 oCar2.showColor() 时,函数中的 this 关键字等于 oCar2。
注意,引用对象的属性时,必须使用 this 关键
如果不用对象或 this 关键字引用变量,ECMAScript 就会把它看作局部变量或全局变量。然后该函数将查找名为 color 的局部或全局变量,但是不会找到。结果如何呢?该函数将在警告中显示 “null”。

定义类或对象

使用预定义对象只是面向对象语言的能力的一部分,它真正强大之处在于能够创建自己专用的类和对象。

工厂方式

1
2
3
4
5
6
7
8
9
10
11
12
function createCar() {
var oTempCar = new Object;
oTempCar.color = "blue";
oTempCar.doors = 4;
oTempCar.mpg = 25;
oTempCar.showColor = function() {
alert(this.color);
};
return oTempCar;
}
var oCar1 = createCar();
var oCar2 = createCar();

在这里,第一个例子中的所有代码都包含在 createCar() 函数中。此外,还有一行额外的代码,返回 car 对象(oTempCar)作为函数值。调用此函数,将创建新对象,并赋予它所有必要的属性,复制出一个我们在前面说明过的 car 对象。因此,通过这种方法,我们可以很容易地创建 car 对象的两个版本(oCar1 和 oCar2),它们的属性完全一样。

为函数传递参数

1
2
3
4
5
6
7
8
9
10
11
12
13
function createCar(sColor,iDoors,iMpg) {
var oTempCar = new Object;
oTempCar.color = sColor;
oTempCar.doors = iDoors;
oTempCar.mpg = iMpg;
oTempCar.showColor = function() {
alert(this.color);
};
return oTempCar;
}
var oCar1 = createCar("red",4,23);
var oCar2 = createCar("blue",3,25);
oCar1.showColor(); //输出 "red"

在工厂函数外定义对象的方法

前面的例子中,每次调用函数 createCar(),都要创建新函数 showColor(),意味着每个对象都有自己的 showColor() 版本。而事实上,每个对象都共享同一个函数。
有些开发者在工厂函数外定义对象的方法,然后通过属性指向该方法,从而避免这个问题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function showColor() {
alert(this.color);
}
function createCar(sColor,iDoors,iMpg) {
var oTempCar = new Object;
oTempCar.color = sColor;
oTempCar.doors = iDoors;
oTempCar.mpg = iMpg;
oTempCar.showColor = showColor;
return oTempCar;
}
var oCar1 = createCar("red",4,23);
var oCar2 = createCar("blue",3,25);
oCar1.showColor(); //输出 "red"
oCar2.showColor(); //输出 "blue"

在上面这段重写的代码中,在函数 createCar() 之前定义了函数 showColor()。在 createCar() 内部,赋予对象一个指向已经存在的 showColor() 函数的指针。从功能上讲,这样解决了重复创建函数对象的问题;但是从语义上讲,该函数不太像是对象的方法。

构造函数方式

创建构造函数就像创建工厂函数一样容易。第一步选择类名,即构造函数的名字。根据惯例,这个名字的首字母大写,以使它与首字母通常是小写的变量名分开。除了这点不同,构造函数看起来很像工厂函数。

1
2
3
4
5
6
7
8
9
10
function Car(sColor,iDoors,iMpg) {
this.color = sColor;
this.doors = iDoors;
this.mpg = iMpg;
this.showColor = function() {
alert(this.color);
};
}
var oCar1 = new Car("red",4,23);
var oCar2 = new Car("blue",3,25);

下面为您解释上面的代码与工厂方式的差别。首先在构造函数内没有创建对象,而是使用 this 关键字。使用 new 运算符构造函数时,在执行第一行代码前先创建一个对象,只有用 this 才能访问该对象。然后可以直接赋予 this 属性,默认情况下是构造函数的返回值(不必明确使用 return 运算符)。

原型方式

该方式利用了对象的 prototype 属性,可以把它看成创建新对象所依赖的原型。
这里,首先用空构造函数来设置类名。然后所有的属性和方法都被直接赋予 prototype 属性。我们重写了前面的例子

1
2
3
4
5
6
7
8
9
10
function Car() {
}
Car.prototype.color = "blue";
Car.prototype.doors = 4;
Car.prototype.mpg = 25;
Car.prototype.showColor = function() {
alert(this.color);
};
var oCar1 = new Car();
var oCar2 = new Car();

在这段代码中,首先定义构造函数(Car),其中无任何代码。接下来的几行代码,通过给 Car 的 prototype 属性添加属性去定义 Car 对象的属性。调用 new Car() 时,原型的所有属性都被立即赋予要创建的对象,意味着所有 Car 实例存放的都是指向 showColor() 函数的指针。从语义上讲,所有属性看起来都属于一个对象,因此解决了前面两种方式存在的问题。
目前使用最广泛的是混合的构造函数/原型方式。

混合的构造函数/原型方式

联合使用构造函数和原型方式,就可像用其他程序设计语言一样创建对象。这种概念非常简单,即用构造函数定义对象的所有非函数属性,用原型方式定义对象的函数属性(方法)。结果是,所有函数都只创建一次,而每个对象都具有自己的对象属性实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function Car(sColor,iDoors,iMpg) {
this.color = sColor;
this.doors = iDoors;
this.mpg = iMpg;
this.drivers = new Array("Mike","John");
}
Car.prototype.showColor = function() {
alert(this.color);
};
var oCar1 = new Car("red",4,23);
var oCar2 = new Car("blue",3,25);
oCar1.drivers.push("Bill");
alert(oCar1.drivers); //输出 "Mike,John,Bill"
alert(oCar2.drivers); //输出 "Mike,John"

原型链(prototype chaining)

继承这种形式在 ECMAScript 中原本是用于原型链的。上一章介绍了定义类的原型方式。原型链扩展了这种方式,以一种有趣的方式实现继承机制。
在上一章学过,prototype 对象是个模板,要实例化的对象都以这个模板为基础。总而言之,prototype 对象的任何属性和方法都被传递给那个类的所有实例。原型链利用这种功能来实现继承机制。

1
2
3
4
5
6
7
8
9
function ClassA() {
}
ClassA.prototype.color = "blue";
ClassA.prototype.sayColor = function () {
alert(this.color);
};
function ClassB() {
}
ClassB.prototype = new ClassA();

JavaScript深入浅出
JavaScript 教程
JavaScript 教程
理解prototype(原型)

文章目录
  1. 1. 原始类型
    1. 1.1. typeof 运算符
  2. 2. Object对象
    1. 2.1. Object 对象具有下列属性:
    2. 2.2. Object 对象还具有几个方法:
    3. 2.3. Boolean 对象
    4. 2.4. Number 对象
      1. 2.4.1. toFixed() 方法
      2. 2.4.2. toExponential() 方法
      3. 2.4.3. toPrecision() 方法
    5. 2.5. String 对象
  3. 3. arguments 对象
  4. 4. Function 对象(类)
    1. 4.1. Function 对象的 length 属性
    2. 4.2. Function 对象的方法
  5. 5. 面向对象
    1. 5.1. 面向对象语言的要求
    2. 5.2. 对象的构成
    3. 5.3. 声明和实例化
    4. 5.4. 对象废除
  6. 6. 关键字 this
    1. 6.1. this 的功能
    2. 6.2. 使用 this 的原因
  7. 7. 定义类或对象
    1. 7.1. 工厂方式
      1. 7.1.1. 为函数传递参数
      2. 7.1.2. 在工厂函数外定义对象的方法
    2. 7.2. 构造函数方式
    3. 7.3. 原型方式
    4. 7.4. 混合的构造函数/原型方式
  8. 8. 原型链(prototype chaining)
,