面向对象的 JavaScript
引用( reference )
“引用”是一个指向对象实际位置的指针。这是一个极为强大的特性,但有一个前提:实际的对象肯定不会是引用。字符串永远是字符串,数组永远是数组。不过多个变量却能够指向同一对象。 JavaScript 基于的就是这样一个引用系统。
这门语言通过维护一系列对其他对象的引用,提供了极大的灵活性。
此外,对象可以包含一系列的属性( property ),这些属性也都不过是到其他对象(比如字符串、数字、数组等)的引用。
如果多个变量指向的是同一个对象,那该对象的类型一改变,所有这些变量也会跟着相应改变。
例如:自修改
创建一个数组
var items = new Array("one","two","three");
创建一个数组的引用
var itemsRef = items;
将一个元素添加到原数组中
items.push("four");
这两个数组的长度应该是一样的。
因为他们指向同一个数组对象。
alert(items.length == itemsRef.length);
注意:必须记住,引用指向的只能是具体的对象,而不是另一个引用。
例如:修改对象的引用,同时保持完整性
var items = new Array("one","two","three");
var itemsRef = items;
items = new Array("other","array");
此时, items 和 itemsRef 指向不同的对象了。
Items 指向的是 new Array("other","array");
itemsRef 指向的是 new Array("one","two","three");
alert(items!=itemsRef);
例如:修改对象而生成新对象
将 item 置为一个字符串对象
var item = "zxr";
itemRef 现在指向同一个字符串对象
var itemRef = item;
将一些新的文本接在这个字符串后面
注意:这会创建一个新的对象,而非修改原对象
item += "loveyou";
item 和 itemRef 的值不相等了,因为新的字符串对象已被创建
alert(item!=itemRef);
类型检查:
JavaScript 是一个动态类型( dynamically typed )的语言,类型检查必然是一个非常有用的而且重要的话题。
我们只讨论两种特别有用的方法:
第一种方法是使用显而易见的 typeof 操作符。这个工具提供了一个字符串名称,用于表达式变量内容的类型。当变量不是 object 或者 array 类型时,这应该算是最完美的解决方法了。
例如:
检查我们的数字是否实际上是字符串
if(typeof num == "string")
如果是,则根据这个字符串解析出整数来
num = parseInt(num);
检查我们的数组是否实际上时字符串
if(typeof arr == "string")
若是,则根据逗号切分出数组来
arr = arr.split(",");
第二种检查对象的方法,需要引用所有 JavaScript 对象都带有的一个属性,称为构造函数( constructor ) . 这一属性引用的是原本用来构造该对象的那个函数。
例如:
检查我们的数字实际上是否为字符串
if(num.constructor == String)
若是,则根据这个字符串解析出整数来
num = parseInt(num);
检查我们的字符串实际上是否为数组
if(str.constructor == Array)
若是,则根据数组逗号归并出字符串来
str = str.join(',');
因此:我们把变量的构造函数作为对象类型引用恐怕是最不容易犯错的合法类型检查了。
例如:
用一个变量类型列表严格检查一个参数列表
function strict(types, args){
保证类型的数量和参数的数量匹配
if(types.length != args.length){
否则抛出一个有用的异常
throw "Invalid number of arguments. Expected "+types.length+", received "+args.length + " instead.";
}
遍历所有的参数,检查他们的类型
for(var i=0; i<args.length; i++){
if(args[i].constructor != types[i]){
throw "Invalid argument type. Expected "+ types[i].name +", received "+args[i].constructor.name +" instead";
}
}
}
一个简单的函数,打印用户列表
function userList(prefix, num, users){
保证 prefix 是字符串, num 是数字, users 是数组
strict([String, Number, Array],arguments);
遍历 'num' 个用户
for(){
显示每个用户信息
print(prefix +":"+users[i]);
}
}
作用域( scope )
作用域是 JavaScript 中一项让人感到棘手的特性。
所有的面向对象编程语言都有某种形式的作用域,不过和把这个概念放在什么上下文中有关。
在 javaScript 里,作用域是由函数划分的,而不是由块( block )划分(比如 while 、 if 和 for 语句中间)的。这样导致的结果是某些代码不好理解(如果你曾经使用过用块划分域的语言)。
例如:
设置全局变量 foo ,并置为“ test”
var foo = "test";
在 if 块中
if(true){
将 foo 置为” new test”
var foo = "new test";
}
如我们所见,现在 foo 等于“ new test” 了
alert(foo == "new test");
创建一个会修改变量 foo 的新函数
function test(){
var foo = "old test";
}
然而在调用时, foo 只在函数作用域内起作用
test();
这里确认了 foo 还是等于“ new test”
alert(foo == "new test");
基于浏览器的 JavaScript 的一个有趣特性是,所有属于全局作用域的变量其实都是我 window 对象的属性( property )。
例如:
一个全局作用域下的变量,存储了字符串“ test”
var test = "test";
你可以发现我们的全局变量和 window 对象的 test 属性是一致的
alert(window.test == test);
如果变量没有显式定义,她就是全局定义的,虽然他可能只在这个函数作用域的范围内使用。
例如:隐式全局作用域的变量声明
一个设置了 foo 值得函数
function test(){
foo = "test";
}
调用此函数以设置 foo 的值
test();
我们发现 foo 现在是在全局作用域下
alert(window.foo == "test");
虽然 JavaScript 中作用域规则不如块级作用域语言那么严格,但她还是非常强大和功能完备的。尤其是在和闭包概念一起使用时, JavaScript 就能表现出脚本语言的强大本色。