ECMAScript 基础

随着ECMA的努力,JavaScript语言在各浏览器之间越来越统一,这也对Javascript使用带来很多便利,不过以浏览器作为宿主环境的Javascript的更多细节不常被剖开来学习,其中ECMAScript也是其中一个常被使用但又不知道细节的一块,最近在看JavaScript的内容,所以顺便整理一下ECMAScript方面的知识。
JavaScript 的核心包含三块:
ECMAScript 描述了该语言的语法和基本对象;
DOM 描述了处理网页内容的方法和接口;
BOM 描述了与浏览器进行交互的方法和接口。

其中ECMAScript和DOM 都有统一标准,只有BOM是各浏览器之间独立和区别比较大的,而BOM中包括:Window 对象、Navigator 对象、Screen 对象、History 对象、Location 对象。使用到这些对象的地方都需要考虑浏览器兼容问题。

ECMAScript 语法特点

1、区分大小写
2、变量是弱类型的
3、每行结尾的分号可有可无
4、括号表示代码块

ECMAScript 变量

声明变量:弱类型,不一定要初始化,可以存放不同类型的值,变量声明不是必须的(随用随定义)。

var test = "hi"; // 常规方法
var test1 = "hi", test2 = "hello"; // 多个变量,不过觉得这样方法声明的变量不方便阅读,少用为妙
var test; // 不一定要初始化

著名的变量命名规则:
Camel 标记法 :首字母是小写的,接下来的字母都以大写字符开头。
eg: var myTestValue = 0;
Pascal 标记法:首字母是大写的,接下来的字母都以大写字符开头。
eg:var MyTestValue = 0;
匈牙利类型标记法:在以 Pascal 标记法命名的变量前附加一个小写字母(或小写字母序列),说明该变量的类型。例如,i 表示整数,s 表示字符串,如下所示“
eg: var iMyTestValue = 0;

其中推荐的做法就是使用“匈牙利类型标记法”来命名变量,对弱类型语言来说,管理起来比较清晰。

这里写图片描述

ECMAScript 原始值和引用值

原始值:存储在栈(stack)中的简单数据段,也就是说,它们的值直接存储在变量访问的位置。
引用值:存储在堆(heap)中的对象,也就是说,存储在变量处的值是一个指针(point),指向存储对象的内存处。
为变量赋值时,ECMAScript 的解释程序必须判断该值是原始类型,还是引用类型。要实现这一点,解释程序则需尝试判断该值是否为 ECMAScript 的原始类型之一,即 Undefined、Null、Boolean、Number 和 String 型。由于这些原始类型占据的空间是固定的,所以可将他们存储在较小的内存区域 - 栈中。

ECMAScript 原始类型

ECMAScript 有 5 种原始类型(primitive type),即 Undefined、Null、Boolean、Number 和 String。
1、typeof 运算符返回值
undefined - 如果变量是 Undefined 类型的
boolean - 如果变量是 Boolean 类型的
number - 如果变量是 Number 类型的
string - 如果变量是 String 类型的
object - 如果变量是一种引用类型或 Null 类型的

2、Undefined 类型:当声明的变量未初始化时,该变量的默认值是 undefined;当函数无明确返回值时,返回的也是值 “undefined”。但是值 undefined 并不同于未定义的值,要注意却分。

如:
alert(typeof oTemp2); // 程序正常,切输出 undefined
alert(oTemp2 == undefined);  // 程序会报错,因为 == 符号只能对已定义的变量进行计算,不过除了typeof 之外的其他运算符都只能用于已声明的变量上。

3、Null 类型:只有一个专用值 null,即它的字面量。如果函数或方法要返回的是对象,那么找不到该对象时,返回的通常是 null。值 undefined 实际上是从值 null 派生来的,因此 ECMAScript 把它们定义为相等的。
eg: alert(null == undefined); //输出 “true”

4、Boolean 类型:它有两个值 true 和 false (即两个 Boolean 字面量)。 false 不等于 0。

5、Number 类型:既可以表示 32 位的整数,还可以表示 64 位的浮点数。
其中几个特殊值也定义为Number类型,前两个是 Number.MAX_VALUE 和 Number.MIN_VALUE,它们定义了 Number 值集合的外边界。当计算生成的数大于 Number.MAX_VALUE 时,它将被赋予值 Number.POSITIVE_INFINITY,意味着不再有数字值。同样,生成的数值小于 Number.MIN_VALUE 的计算也会被赋予值 Number.NEGATIVE_INFINITY,也意味着不再有数字值。如果计算返回的是无穷大值,那么生成的结果不能再用于其他计算。可以对任何数调用 isFinite() 方法,以确保该数不是无穷大。最后一个特殊值是 NaN,表示非数(Not a Number)。NaN 的另一个奇特之处在于,它与自身不相等,这意味着下面的代码将返回 false:
alert(NaN == NaN); //输出 “false”
用isNaN() 函数来判断值是否为NaN。

6、String 类型:字符串字面量是由双引号(”)或单引号(’)声明的。

7、特俗字符
这里写图片描述

ECMAScript 类型转换

1、转换成字符串
ECMAScript 的 Boolean 值、数字和字符串的原始值是伪对象,这意味着它们实际上具有属性和方法,都有 toString() 方法,直接用字符串表示字面量。Number 类型的 toString() 方法比较特殊,它有两种模式,即默认模式和基模式。采用默认模式,toString() 方法只是用相应的字符串输出数字值(无论是整数、浮点数还是科学计数法)。采用 Number 类型的 toString() 方法的基模式,可以用不同的基输出数字,例如二进制的基是 2,八进制的基是 8,十六进制的基是 16。

var iNum = 10;
alert(iNum.toString(2));    //输出 "1010"
alert(iNum.toString(8));    //输出 "12"
alert(iNum.toString(16));   //输出 "A"

2、转换成数字
ECMAScript 提供了两种把非数字的原始值转换成数字的方法,即 parseInt() 和 parseFloat()。只有对 String 类型调用这些方法,它们才能正确运行;对其他类型返回的都是 NaN。parseInt() 方法还有基模式,可以把二进制、八进制、十六进制或其他任何进制的字符串转换成整数。

var iNum1 = parseInt("12345red");   //返回 12345
var iNum1 = parseInt("0xA");    //返回 10
var iNum1 = parseInt("56.9");   //返回 56
var iNum1 = parseInt("red");    //返回 NaN

parseFloat() 方法与 parseInt() 方法的处理方式相似,不过,对于这个方法来说,第一个出现的小数点是有效字符。如果有两个小数点,第二个小数点将被看作无效的。parseFloat() 会把这个小数点之前的字符转换成数字。这意味着字符串 “11.22.33” 将被解析成 11.22。
3、强制类型转换
ECMAScript 中可用的 3 种强制类型转换如下:
Boolean(value) - 把给定的值转换成 Boolean 型;
Number(value) - 把给定的值转换成数字(可以是整数或浮点数);
String(value) - 把给定的值转换成字符串;

ECMAScript 引用类型

引用类型通常叫做类(class),也就是说,遇到引用值,所处理的就是对象。
1、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() 的返回值相同。

2、Boolean 对象
尽量不要使用,直接使用字面量

3、Number 对象
几个处理数值的专用方法:
toFixed() 方法返回的是具有指定位数小数的数字的字符串表示。

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

toExponential() 方法,返回的是用科学计数法表示的数字的字符串形式。
与 Boolean 对象相似,Number 对象也很重要,不过应该少用这种对象,以避免潜在的问题。只要可能,都使用数字的原始表示法。

4、String 对象
length 属性
charAt() 和 charCodeAt() 方法
concat() 方法
indexOf() 和 lastIndexOf() 方法。 // 与charAt方法是有区别的,如果没有找不到子串,则返回 -1,而charAt会报错。
localeCompare() 方法:如果 String 对象按照字母顺序排在参数中的字符串之前,返回负数。如果 String 对象等于参数中的字符串,返回 0;如果 String 对象按照字母顺序排在参数中的字符串之后,返回正数。
slice() 和 substring():两个函数在参数为正数时是一样的功能,可是slice接收负数时代表倒数获取字符串,substring是取整个字符串。

ECMAScript 一元运算符

delete:delete 运算符删除对以前自定义的对象属性或方法的引用,仅限自定义的属性或方法。
void:void 运算符对任何值返回 undefined。
前增量/前减量运算符: –i
后增量/后减量运算符:i–
一元加法和一元减法:这个方法跟java的一元加减法是不一样的,注意。

ECMAScript 位运算符

位运算 NOT : 位运算 NOT 由否定号(~)表示。反转
位运算 AND:位运算 AND 由和号(&)表示,直接对数字的二进制形式进行运算。
位运算 OR:位运算 OR 由符号(|)表示,也是直接对数字的二进制形式进行运算。
位运算 XOR:位运算 XOR 由符号(^)表示,当然,也是直接对二进制形式进行运算。
左移运算:左移运算由两个小于号表示(<<)。
有符号右移运算:有符号右移运算符由两个大于号表示(>>)。
无符号右移运算:无符号右移运算符由三个大于号(>>>)表示,它将无符号 32 位数的所有数位整体右移。

ECMAScript Boolean 运算符

逻辑 NOT 运算符:都由感叹号(!)表示。注意针对不同数值的计算规则,特别是对null、0、undefined等变量的计算规则。
逻辑 AND 运算符:逻辑 AND 运算符用双和号(&&)表示。

如果某个运算数不是原始的 Boolean 型值,逻辑 AND 运算并不一定返回 Boolean 值:
如果一个运算数是对象,另一个是 Boolean 值,返回该对象。不过也要注意顺序。
如果两个运算数都是对象,返回第二个对象。
如果某个运算数是 null,返回 null。
如果某个运算数是 NaN,返回 NaN。
如果某个运算数是 undefined,发生错误。
ECMAScript 中的逻辑 AND 运算也是简便运算,即如果第一个运算数决定了结果,就不再计算第二个运算数。

逻辑 OR 运算符:双竖线(||)表示。

与逻辑 AND 运算符相似,如果某个运算数不是 Boolean 值,逻辑 OR 运算并不一定返回 Boolean 值:
如果一个运算数是对象,并且该对象左边的运算数值均为 false,则返回该对象。
如果两个运算数都是对象,返回第一个对象。
如果最后一个运算数是 null,并且其他运算数值均为 false,则返回 null。
如果最后一个运算数是 NaN,并且其他运算数值均为 false,则返回 NaN。
如果某个运算数是 undefined,发生错误。
与逻辑 AND 运算符一样,逻辑 OR 运算也是简便运算。对于逻辑 OR 运算符来说,如果第一个运算数值为 true,就不再计算第二个运算数。

ECMAScript 乘性运算符

乘法运算符由星号(*)表示,用于两数相乘。
除法运算符:除法运算符由斜杠(/)表示
取模运算符:除法(余数)运算符由百分号(%)表示

ECMAScript 加性运算符

1、加法运算符:法运算符由加号(+)表示。
如果某个运算数是字符串,那么采用下列规则:
如果两个运算数都是字符串,把第二个字符串连接到第一个上。
如果只有一个运算数是字符串,把另一个运算数转换成字符串,结果是两个字符串连接成的字符串。
2、减法运算符:减法运算符(-)

ECMAScript 关系运算符

关系运算符小于、大于、小于等于和大于等于执行的是两个数的比较运算,比较方式与算术比较运算相同。每个关系运算符都返回一个布尔值。
不过,对两个字符串应用关系运算符,它们的行为则不同。许多人认为小于表示“在字母顺序上靠前”,大于表示“在字母顺序上靠后”,但事实并非如此。对于字符串,第一个字符串中每个字符的代码都与会第二个字符串中对应位置的字符的代码进行数值比较。完成这种比较操作后,返回一个 Boolean 值。问题在于大写字母的代码都小于小写字母的代码。
另一种棘手的状况发生在比较两个字符串形式的数字时,两个运算数都是字符串,所以比较的是它们的字符代码,无论何时比较一个数字和一个字符串,ECMAScript 都会把字符串转换成数字,然后按照数字顺序比较它们。

ECMAScript 等性运算符

ECMAScript 提供了两套等性运算符:等号和非等号用于处理原始值,全等号和非全等号用于处理对象。
1、等号和非等号
在 ECMAScript 中,等号由双等号(==)表示,当且仅当两个运算数相等时,它返回 true。非等号由感叹号加等号(!=)表示,当且仅当两个运算数不相等时,它返回 true。为确定两个运算数是否相等,这两个运算符都会进行类型转换。
执行类型转换的规则如下:
如果一个运算数是 Boolean 值,在检查相等性之前,把它转换成数字值。false 转换成 0,true 为 1。
如果一个运算数是字符串,另一个是数字,在检查相等性之前,要尝试把字符串转换成数字。
如果一个运算数是对象,另一个是字符串,在检查相等性之前,要尝试把对象转换成字符串。
如果一个运算数是对象,另一个是数字,在检查相等性之前,要尝试把对象转换成数字。
在比较时,该运算符还遵守下列规则:
值 null 和 undefined 相等。
在检查相等性时,不能把 null 和 undefined 转换成其他值。
如果某个运算数是 NaN,等号将返回 false,非等号将返回 true。
如果两个运算数都是对象,那么比较的是它们的引用值。如果两个运算数指向同一对象,那么等号返回 true,否则两个运算数不等。
这里写图片描述

2、全等号和非全等号
全等号由三个等号表示(===),只有在无需类型转换运算数就相等的情况下,才返回 true。
非全等号由感叹号加两个等号(!==)表示,只有在无需类型转换运算数不相等的情况下,才返回 true。

ECMAScript 条件运算符

条件运算符是 ECMAScript 中功能最多的运算符,它的形式与 Java 中的相同。
variable = boolean_expression ? true_value : false_value;
也就是三目运算。

ECMAScript语句

1、if语句
其中 condition 可以是任何表达式,计算的结果甚至不必是真正的 boolean 值,ECMAScript 会把它转换成 boolean 值。

if (i > 30) {
  alert("大于 30");
} else if (i < 0) {
  alert("小于 0");
} else {
  alert("在 030 之间");
}

2、ECMAScript 迭代语句/循环语句
1)do-while 语句 do {statement} while (expression);
2)while 语句 while (expression) statement
3)for 语句 for (initialization; expression; post-loop-expression) statement
4) for-in 语句 for (property in expression) statement
5)break 和 continue 语句

3、ECMAScript with 语句
with 语句用于设置代码在特定对象中的作用域。

var sMessage = "hello";
with(sMessage) {
  alert(toUpperCase()); //输出 "HELLO"
}

在这个例子中,with 语句用于字符串,所以在调用 toUpperCase() 方法时,解释程序将检查该方法是否是本地函数。如果不是,它将检查伪对象 sMessage,看它是否为该对象的方法。
with 语句是运行缓慢的代码块,尤其是在已设置了属性值时。大多数情况下,如果可能,最好避免使用它。

4、switch 语句
每个情况(case)都是表示“如果 expression 等于 value,就执行 statement”。
关键字 break 会使代码跳出 switch 语句。如果没有关键字 break,代码执行就会继续进入下一个 case。
关键字 default 说明了表达式的结果不等于任何一种情况时的操作(事实上,它相对于 else 从句)。

switch (expression)
  case value: statement;
    break;
  case value: statement;
    break;
  case value: statement;
    break;
  case value: statement;
    break;
...
  case value: statement;
    break;
  default: statement;

在 ECMAScript 中,switch 语句可以用于字符串,而且能用不是常量的值说明情况:

var BLUE = "blue", RED = "red", GREEN  = "green";

switch (sColor) {
  case BLUE: alert("Blue");
    break;
  case RED: alert("Red");
    break;
  case GREEN: alert("Green");
    break;
  default: alert("Other");
}

ECMAScript 函数概述

函数是由这样的方式进行声明的:关键字 function、函数名、一组参数,以及置于括号中的待执行代码。
函数的基本语法是这样的:

function functionName(arg0, arg1, ... argN) {
  statements
}

函数在执行过 return 语句后立即停止代码。因此,return 语句后的代码都不会被执行。
如果函数无明确的返回值,或调用了没有参数的 return 语句,那么它真正返回的值是 undefined。
1、arguments 对象
在函数代码中,使用特殊对象 arguments,开发者无需明确指出参数名,就能访问它们。

var iName = arguments[0];
arguments.length; // 检测参数个数
// 用 arguments 对象判断传递给函数的参数个数,即可模拟函数重载

2、ECMAScript Function 对象(类)
ECMAScript 的函数实际上是功能完整的对象。
用 Function 类直接创建函数的语法如下:

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

如:
function sayHi(sName, sMessage) {
  alert("Hello " + sName + sMessage);
}
也可以这样定义:
var sayHi 
= 
new Function("sName", "sMessage", "alert(\"Hello \" + sName + sMessage);");
尽管可以使用 Function 构造函数创建函数,但最好不要使用它,因为用它定义函数比用传统方式要慢得多。不过,所有函数都应看作 Function 类的实例。

Function 对象的 length 属性:ECMAScript 定义的属性 length 声明了函数期望的参数个数。
Function 对象也有与所有对象共享的 valueOf() 方法和 toString() 方法。这两个方法返回的都是函数的源代码,在调试时尤其有用。

ECMAScript 闭包(closure)

ECMAScript 最易让人误解的一点是,它支持闭包(closure)。我更偏向于把闭包理解成ECMAScript的一种机制,这种机制允许函数使用函数之外定义的变量。不过也有前提,前提就是变量的作用域没有超。
也有资料描述闭包就是函数体,函数体可以读取函数体以外的所有父函数的变量。

不过总体来说,理解成机制比较容易理解。

ECMAScript 面向对象技术

ECMA-262 把对象(object)定义为“属性的无序集合,每个属性存放一个原始值、对象或函数”。严格来说,这意味着对象是无特定顺序的值的数组。
一种面向对象语言需要向开发者提供四种基本能力:
抽象 - 把事物描述成对象(类)
封装 - 把相关的信息(无论数据或方法)存储在对象中的能力
继承 - 由另一个类(或多个类)得来类的属性和方法的能力
多态 - 编写能以多种方法运行的函数或方法的能力
ECMAScript 支持这些要求,因此可被是看做面向对象的。在 ECMAScript 中,对象由特性(attribute)构成,特性可以是原始值,也可以是引用值。如果特性存放的是函数,它将被看作对象的方法(method),否则该特性被看作对象的属性(property)。
1、声明和实例化
对象的创建方式是用关键字 new 后面跟上实例化的类的名字。
2、对象废除
ECMAScript 拥有无用存储单元收集程序(garbage collection routine),意味着不必专门销毁对象来释放内存。当再没有对对象的引用时,称该对象被废除(dereference)了。这点跟java很类似。每当函数执行完它的代码,无用存储单元收集程序都会运行,释放所有的局部变量,还有在一些其他不可预知的情况下,无用存储单元收集程序也会运行。这点又跟java不一样。
把对象的所有引用都设置为 null,可以强制性地废除对象。旧的浏览器(如 IE/MAC)没有全面的无用存储单元收集程序,所以在卸载页面时,对象可能不能被正确销毁。废除对象和它的所有特性是确保内存使用正确的最好方法。
3、早绑定和晚绑定
所谓绑定(binding),即把对象的接口与对象实例结合在一起的方法。
早绑定(early binding)是指在实例化对象之前定义它的属性和方法,这样编译器或解释程序就能够提前转换机器代码。IDE就是依赖这个能力来做代码提示的。ECMAScript 不是强类型语言,不支持早绑定,所以编写javascript代码时很难获得代码提醒。
晚绑定(late binding)指的是编译器或解释程序在运行前,不知道对象的类型。
4、ECMAScript 对象类型
一般来说,可以创建并使用的对象有三种:本地对象、内置对象和宿主对象。
本地对象(native object)就是“独立于宿主环境的 ECMAScript 实现提供的对象”。如:Object、Function、Array等。
内置对象(built-in object)就是“由 ECMAScript 实现提供的、独立于宿主环境的所有对象,在 ECMAScript 程序开始执行时出现”。这意味着开发者不必明确实例化内置对象,它已被实例化了。ECMA-262 只定义了两个内置对象,即 Global 和 Math (它们也是本地对象,根据定义,每个内置对象都是本地对象)。
宿主对象
所有非本地对象都是宿主对象(host object),即由 ECMAScript 实现的宿主环境提供的对象。所有 BOM 和 DOM 对象都是宿主对象。
5、ECMAScript 对象作用域
ECMAScript 只有公用作用域,由于缺少私有作用域,开发者确定了一个规约,说明哪些属性和方法应该被看做私有的,就是在属性前后加下划线。
6、关键字 this
在 ECMAScript 中,要掌握的最重要的概念之一是关键字 this 的用法,它用在对象的方法中。关键字 this 总是指向调用该方法的对象。
引用对象的属性时,必须使用 this 关键字。如果不用对象或 this 关键字引用变量,ECMAScript 就会把它看作局部变量或全局变量。
7、ECMAScript 定对象
1)原始的方式:用 new Object()创建对象,然后动态绑定属性和行为。每次创建对象都需要定义一遍变量和行为,明显不方便。
2)工厂方式:创建能创建并返回特定类型对象的工厂函数(factory function)。
每次获取对象时,对象内的行为都需要重新执行,也就是在内存中需要新开辟一块一样的代码块,比前原始方式好,但是消耗太大。把函数抽取到工厂函数之外就会好一点,但是从语义上讲,该函数不太像是对象的方法。类似java语言,定义属性和行为都是在类定义体内的,这样看起来和管理起来也方便。
如:

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();

3)构造函数方式
构造函数看起来很像工厂函数,不过构造函数内没有创建对象,而是使用 this 关键字。使用 new 运算符构造函数时,在执行第一行代码前先创建一个对象,这个对象调用构造函数,构造函数内用 this 才访问该对象。然后可以直接赋予 this 属性,默认情况下可以理解this是new关键字的返回值。

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);

这块代码跟工厂方法有着同样的问题,不过把函数抽取出来就好。
4)原型方式
原型方式看起来是个不错的解决方案,不过也比较多问题,譬如变量不同通过构造函数提供,需要获取对象后再修改。如果属性赋值的是下面代码里面那样的数组的话,创建出来的对象因为共用了同一个数组对象,也会导致数组对象的误读,除非不变更该数组对象。

function Car() {
}

Car.prototype.color = "blue";
Car.prototype.doors = 4;
Car.prototype.mpg = 25;
Car.prototype.drivers = new Array("Mike","John");
Car.prototype.showColor = function() {
  alert(this.color);
};

var oCar1 = new Car();
var oCar2 = new Car();

oCar1.drivers.push("Bill");

alert(oCar1.drivers);   //输出 "Mike,John,Bill"
alert(oCar2.drivers);   //输出 "Mike,John,Bill"

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

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"

6)动态原型方法
对于习惯使用其他语言的开发者来说,使用混合的构造函数/原型方式感觉不那么和谐。毕竟,定义类时,大多数面向对象语言都对属性和方法进行了视觉上的封装。
动态原型方法的基本想法与混合的构造函数/原型方式相同,即在构造函数内定义非函数属性,而函数属性则利用原型属性定义。唯一的区别是赋予对象方法的位置。这种方式虽然添加了一个无关变量,不过从代码和功能来看都比较好。

function Car(sColor,iDoors,iMpg) {
  this.color = sColor;
  this.doors = iDoors;
  this.mpg = iMpg;
  this.drivers = new Array("Mike","John");

  if (typeof Car._initialized == "undefined") {
    Car.prototype.showColor = function() {
      alert(this.color);
    };

    Car._initialized = true;
  }
// 更优雅一点的代码就是
Car.prototype.showColor = (typeof Car._initialized == "undefined")  and function() {
      alert(this.color);
    };
}
Car._initialized = true;

8、ECMAScript 修改对象
通过使用 ECMAScript,不仅可以创建对象,还可以修改已有对象的行为。
prototype 属性不仅可以定义构造函数的属性和方法,还可以为本地对象添加属性和方法。也就是由两种途径修改对象,要斟酌好究竟要用哪一种。prototype是会被继承的,而通过变量的方式修改对象就只有当前对象有属性值。

9、ECMAScript 继承机制实现
1)对象冒充
如下代码,通过运行时this的指向不一样而复用同样的代码,从而达到继承的效果。

function ClassA(sColor) {
    this.color = sColor;
    this.sayColor = function () {
        alert(this.color);
    };
}

function ClassB(sColor, sName) {
    this.newMethod = ClassA;
    this.newMethod(sColor);
    delete this.newMethod;

    this.name = sName;
    this.sayName = function () {
        alert(this.name);
    };
}

2)call() 方法
结合对象冒充,可以把继承代码写成这样:

function ClassB(sColor, sName) {
    //this.newMethod = ClassA;
    //this.newMethod(color);
    //delete this.newMethod;
    ClassA.call(this, sColor);

    this.name = sName;
    this.sayName = function () {
        alert(this.name);
    };
}

3)apply() 方法
apply() 方法有两个参数,用作 this 的对象和要传递给函数的参数的数组。其实基本上跟call方法差不多,只是apply()的第二个参数只能是数组。

function ClassB(sColor, sName) {
    //this.newMethod = ClassA;
    //this.newMethod(color);
    //delete this.newMethod;
    ClassA.apply(this, arguments);

    this.name = sName;
    this.sayName = function () {
        alert(this.name);
    };
}

4)原型链(prototype chaining)
如下代码,利用原型链来继承,方式就是把父类的实例直接赋值给子类的原型,然后再定义子类的属性和方法。与对象冒充相似,子类的所有属性和方法都必须出现在 prototype 属性被赋值后,因为在它之前赋值的所有方法都会被删除。不过原型链无法支持多重继承。

function ClassA() {
}

ClassA.prototype.color = "blue";
ClassA.prototype.sayColor = function () {
    alert(this.color);
};

function ClassB() {
}

ClassB.prototype = new ClassA();

5)混合方式
创建类的最好方式是用构造函数定义属性,用原型定义方法。这种方式同样适用于继承机制,用对象冒充继承构造函数的属性,用原型链继承 prototype 对象的方法。再优化一下代码,把方法定义的代码搬到对象定义的{}里,就完美了。
如下代码:

function ClassA(sColor) {
    this.color = sColor;
}

ClassA.prototype.sayColor = function () {
    alert(this.color);
};

function ClassB(sColor, sName) {
    ClassA.call(this, sColor);
    this.name = sName;
}

ClassB.prototype = new ClassA();

ClassB.prototype.sayName = function () {
    alert(this.name);
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值