在 ECMAScript 中,变量可以存在两种类型的值,即原始值和引用值。
原始值:存储在栈(stack)中的简单数据段,也就是说,它们的值直接存储在变量访问的位置。
引用值:存储在堆(heap)中的对象,也就是说,存储在变量处的值是一个指针(point),指向存储对象的内存处。
一、原始类型
ECMAScript 有 5 种原始类型(primitive type),即 Undefined、Null、Boolean、Number 和 String。
1、 typeof 运算符
对变量或值调用 typeof 运算符将返回下列值之一:
undefined - 如果变量是 Undefined 类型的
boolean - 如果变量是 Boolean 类型的
number - 如果变量是 Number 类型的
string - 如果变量是 String 类型的
object - 如果变量是一种引用类型或 Null 类型的
var oTemp;
alert(oTemp == undefined); //输出 "true"
alert(typeof oTemp); //输出 "undefined"
以下代码第一句输出的是 “undefined”,即使变量 oTemp2 从未被声明过。如果对 oTemp2 使用除 typeof 之外的其他运算符的话,会引起错误,因为其他运算符只能用于已声明的变量上。
alert(typeof oTemp2); //输出 "undefined"
alert(oTemp2 == undefined); //报错
当函数无明确返回值时, typeof 运算符返回的也是值 "undefined"
function testFunc() {
}
alert(testFunc() == undefined); //输出 "true" !!!
值 undefined 实际上是从值 null 派生来的,因此 ECMAScript 把它们定义为相等的,但它们的含义不同。undefined 声明了变量但未对其初始化 时赋予该变量的值,null 表示尚未存在的对象(在讨论 typeof 运算符时,简单地介绍过这一点)。如果函数或方法要返回的是对象,那么找不到该对象时,返回的通常是 null。
alert(null == undefined); //输出 "true"
2、特殊的number值
浮点数进行计算前,真正存储的是字符串。
MAX_VALUE 可表示的最大的数。
MIN_VALUE 可表示的最小的数。
NaN 非数字值。
NEGATIVE_INFINITY 负无穷大,溢出时返回该值。
POSITIVE_INFINITY 正无穷大,溢出时返回该值。
isFinite() 方法,检查其参数是否是无穷大。
如果 number 是有限数字(或可转换为有限数字),那么返回 true。否则,如果 number 是 NaN(非数字),或者是正、负无穷大的数,则返回 false。
document.write(isFinite(123)+ "<br />") //true
document.write(isFinite(-1.23)+ "<br />") //true
document.write(isFinite(5-2)+ "<br />") //true
document.write(isFinite(0)+ "<br />") //true
document.write(isFinite("Hello")+ "<br />") //false
document.write(isFinite("2005/12/12")+ "<br />") //false
NaN表示非数,一般发生在类型转换失败时。例如,要把单词 blue 转换成数值就会失败,因为没有与之等价的数值。不能用于算术计算。与自身不相等。
alert(NaN == NaN); //输出 "false"
alert(isNaN("blue")); //输出 "true"
alert(isNaN("666")); //输出 "false"
二、类型转换
1、转数字
** parseInt() parseFloat() 转换第一个无效字符之前的字符串**
parseInt()
var iNum1 = parseInt("12345red"); //返回 12345
var iNum1 = parseInt("0xA"); //返回 10
var iNum1 = parseInt("56.9"); //返回 56
var iNum1 = parseInt("red"); //返回 NaN
var iNum1 = parseInt("AF", 16); //十六进制,返回 175
parseFloat() 字符串必须以十进制形式表示浮点数
var fNum1 = parseFloat("12345red"); //返回 12345
var fNum2 = parseFloat("0xA"); //返回 NaN
var fNum4 = parseFloat("11.22.33"); //返回 11.22
var fNum5 = parseFloat("0102"); //返回 102
2、强制转换
1)Boolean(value) - 把给定的值转换成 Boolean 型;
当要转换的值是至少有一个字符的字符串、非 0 数字或对象时,Boolean() 函数将返回 true。如果该值是空字符串、数字 0、undefined 或 null,它将返回 false。
var b1 = Boolean(""); //false - 空字符串
var b2 = Boolean("hello"); //true - 非空字符串
var b1 = Boolean(50); //true - 非零数字
var b1 = Boolean(null); //false - null
var b1 = Boolean(0); //false - 零
var b1 = Boolean(new object()); //true - 对象
2)Number(value) - 把给定的值转换成数字(可以是整数或浮点数);
用 Number() 进行强制类型转换,“1.2.3” 将返回 NaN,因为整个字符串值不能转换成数字。如果字符串值能被完整地转换,Number() 将判断是调用 parseInt() 方法还是 parseFloat() 方法。
3)String(value) - 把给定的值转换成字符串;
三、引用类型
对象是由 new 运算符加上要实例化的对象的名字创建的。
var o = new Object();
1、Object 对象
属性:
constructor:对创建对象的函数的引用(指针)。对于 Object 对象,该指针指向原始的 Object() 函数。
prototype:对该对象的对象原型的引用。对于所有的对象,它默认返回 Object 对象的一个实例。
方法:
hasOwnProperty(property):判断对象是否有某个特定的属性。(例如,o.hasOwnProperty(“name”))
IsPrototypeOf(object):判断该对象是否为另一个对象的原型。
PropertyIsEnumerable:判断给定的属性是否可以用 for…in 语句进行枚举。
注:上面列出的每种属性和方法都会被其他对象覆盖。
2、Number 对象
要得到数字对象的 Number 原始值,使用 valueOf():
var iNumber = oNumberObject.valueOf();
toFixed() 返回指定位数小数的数字的字符串表示:
var oNumberObject = new Number(68);
alert(oNumberObject.toFixed(2)); //输出 "68.00"
注:toFixed() 方法能表示具有 0 到 20 位小数的数字,超过这个范围的值会引发错误。
toExponential():返回用科学计数法表示的数字的字符串形式,参数指定要输出的小数的位数:
var oNumberObject = new Number(68);
alert(oNumberObject.toExponential(1)); //输出 "6.8e+1"
toPrecision() :返回指定数字总数的预定形式或指数形式。参数用于表示数的数字总数(不包括指数)。会对数进行舍入。
var oNumberObject = new Number(68);
alert(oNumberObject.toPrecision(1)); //输出 "7e+1",即70
alert(oNumberObject.toPrecision(3)); //输出 "68.0"
3、String 对象
charAt() :返回指定位置处的字符串:
var oStringObject = new String("hello world");
alert(oStringObject.charAt(1)); //输出 "e"
concat() :连接一个/多个字符串,返回String 原始值,原始的 String 对象不变。相当于+号。
var oStringObject = new String("hello ");
var sResult = oStringObject.concat("world");
alert(sResult); //输出 "hello world"
alert(oStringObject); //输出 "hello "
instanceof 识别正在处理的对象的类型。与 typeof 方法不同的是,instanceof 方法要求开发者明确地确认对象为某特定类型。
var oStringObject = new String("hello world");
alert(oStringObject instanceof String); //输出 "true"
四、运算符
1、delete 运算符 删除对以前定义的对象属性或方法的引用,不能删除开发者未定义的属性和方法
var o = new Object;
o.name = "David";
alert(o.name); //输出 "David"
delete o.name;
alert(o.name); //输出 "undefined"
2、void 运算符 对任何值返回 undefined。通常用于避免输出不应该输出的值,没有返回值的函数真正返回的都是 undefined。
<a href="javascript:void(window.open('about:blank'))">Click me</a>
不加void 运算符,点击其中的链接,即可看到屏幕上显示 “[object]”。因为 window.open() 方法返回了新打开的窗口的引用,然后该对象将被转换成要显示的字符串。
五、语句
1、arguments对象
在函数代码中,使用特殊对象 arguments,开发者无需明确指出参数名,就能访问它们。
function sayHi() {
alert(arguments[0]);
}
还可以用 arguments 对象检测函数的参数个数,引用属性 arguments.length 即可
function howManyArgs() {
alert(arguments.length);
}
howManyArgs("string", 45); //2
howManyArgs(); //0
howManyArgs(12); //1
注:ECMAScript 不会验证传递给函数的参数个数是否等于函数定义的参数个数。开发者定义的函数都可以接受任意个数的参数(根据 Netscape 的文档,最多可接受 25 个),而不会引发任何错误。任何遗漏的参数都会以 undefined 传递给函数,多余的函数将忽略。
2、Function 对象
用 Function 类直接创建函数的语法如下:
var function_name = new function(_arg1_, _arg2_, ..., _argN_, _function_body_)
每个 arg 都是一个参数,最后一个参数是函数主体。这些参数必须是字符串。
var sayHi = new Function("sName", "sMessage", "alert(\"Hello \" + sName + sMessage);");
如果函数名只是指向函数的变量,那么可以把函数作为参数传递给另一个函数
function callAnotherFunc(fnFunction, vArgument) {
fnFunction(vArgument);
}
var doAdd = new Function("iNum", "alert(iNum + 10)");
callAnotherFunc(doAdd, 10); //输出 "20"
ECMAScript 定义的Function 对象的属性 length 声明了函数期望的参数个数
function doAdd(iNum) {
alert(iNum + 10);
}
alert(doAdd.length); //输出 "1"
Function 对象也有与所有对象共享的 valueOf() 方法和 toString() 方法。这两个方法返回函数源代码,在调试时尤其有用。
function doAdd(iNum) {
alert(iNum + 10);
}
document.write(doAdd.toString());
3、闭包
闭包,指的是词法表示包括不被计算的变量的函数,也就是说,函数可以使用函数之外定义的变量。
在 ECMAScript 中使用全局变量是一个简单的闭包实例。
var sMessage = "hello world";
function sayHelloWorld() {
alert(sMessage);
}
sayHelloWorld();
在上面这段代码中,脚本被载入内存后,并没有为函数 sayHelloWorld() 计算变量 sMessage 的值。该函数捕获 sMessage 的值只是为了以后的使用,也就是说,解释程序知道在调用该函数时要检查 sMessage 的值。sMessage 将在函数调用 sayHelloWorld() 时被赋值,显示消息 “hello world”。
在一个函数中定义另一个会使闭包变得更加复杂。
var iBaseNum = 10;
function addNum(iNum1, iNum2) {
function doAdd() {
return iNum1 + iNum2 + iBaseNum;
}
return doAdd();
}
这里,函数 addNum() 包括函数 doAdd() (闭包)。内部函数是一个闭包,因为它将获取外部函数的参数 iNum1 和 iNum2 以及全局变量 iBaseNum 的值。 addNum() 的最后一步调用了 doAdd(),把两个参数和全局变量相加,并返回它们的和。
这里要掌握的重要概念是,内部函数doAdd() 函数根本不接受参数,它使用的值是从执行环境中获取的。
六、对象
1、面向对象
一种面向对象语言需要向开发者提供四种基本能力:
封装 - 把相关的信息(无论数据或方法)存储在对象中的能力
聚集 - 把一个对象存储在另一个对象内的能力
继承 - 由另一个类(或多个类)得来类的属性和方法的能力
多态 - 编写能以多种方法运行的函数或方法的能力
2、对象作用域
ECMAScript 只有公用作用域,所有对象的所有属性和方法都是公用的。
由于缺少私有作用域,开发者确定了一个规约,在属性前后加下划线把变量看为私有的。下划线并不改变属性是公用属性的事实,它只是告诉其他开发者,应该把该属性看作私有的。
obj._color_ = "blue";
严格来说,ECMAScript 并没有静态作用域。不过,它可以给构造函数提供属性和方法。
function sayHello() {
alert("hello");
}
sayHello.alternate = function() {
alert("hi");
}
sayHello.alternate(); //输出 "hi"
这里,方法 alternate() 实际上是函数 sayHello 的方法。可以调用 sayHello.alternate() 输出 “hi”。即使如此,alternate() 也是 sayHello() 公用作用域中的方法,而不是静态方法。
3、this
关键字this用在对象的方法中,指向调用该方法的对象
var oCar = new Object;
oCar.color = "red";
oCar.showColor = function() {
alert(`this.color`);
};
oCar.showColor(); //输出 "red"
在上面的代码中,关键字 this 用在对象的 showColor() 方法中。在此环境中,this 等于 oCar。
为什么使用 this 呢?因为在实例化对象时,总是不能确定开发者会使用什么样的变量名。使用 this,即可在任何多个地方重用同一个函数。
4、修改对象
prototype 属性不仅可以定义构造函数的属性和方法,还可以为本地对象添加属性和方法。
1)通过已有的方法创建新方法
Number.prototype.toHexString = function() {
return this.toString(16);
};
var iNum = 15;
alert(iNum.toHexString()); //输出 "F"
2)重命名已有方法
Array.prototype.enqueue = function(vItem) {
this.push(vItem);
};
Array.prototype.dequeue = function() {
return this.shift();
};
3)添加与已有方法无关的方法
//在数组中检索每个项,直到发现与传进来的项相同的项目为止。如果找到相同的项,则返回该项的位置,否则,返回 -1。
Array.prototype.indexOf = function (vItem) {
for (var i=0; i<this.length; i++) {
if (vItem == this[i]) {
return i;
}
}
return -1;
}
var aColors = new Array("red","green","blue");
alert(aColors.indexOf("green")); //输出 "1"
4)为本地对象添加新方法
如果想给 ECMAScript 中每个本地对象添加新方法,必须在 Object 对象的 prototype 属性上定义它。所有本地对象都继承了 Object 对象,所以对 Object 对象做任何改变,都会反应在所有本地对象上。
Object.prototype.showValue = function () {
alert(this.valueOf());
};
var str = "hello";
var iNum = 25;
str.showValue(); //输出 "hello"
iNum.showValue(); //输出 "25"
5)重定义已有方法
Function.prototype.toString = function() {
return "Function code hidden";
}
function sayHi() {
alert("hi");
}
alert(sayHi.toString()); //输出 "Function code hidden"
不过,toString() 指向的原始函数将被无用存储单元回收程序回收,因为它被完全废弃了。没有能够恢复原始函数的方法,所以在覆盖原始方法前,比较安全的做法是存储它的指针,以便以后的使用。
`Function.prototype.originalToString = Function.prototype.toString;`
Function.prototype.toString = function() {
if (this.originalToString().length > 100) {
return "Function too long to display.";
} else {
return this.originalToString();
}
};