注意:废除对象的所有引用时要当心。如果一个对象有两个或更多引用,则要正确废除该对象,必须将其所有引用都设置为 null。
早绑定和晚绑定
所谓绑定(binding),即把对象的接口与对象实例结合在一起的方法。
早绑定(early binding)是指在实例化对象之前定义它的属性和方法,这样编译器或解释程序就能够提前转换机器代码。在 Java 和 Visual Basic 这样的语言中,有了早绑定,就可以在开发环境中使用 IntelliSense(即给开发者提供对象中属性和方法列表的功能)。ECMAScript 不是强类型语言,所以不支持早绑定。
另一方面,晚绑定(late binding)指的是编译器或解释程序在运行前,不知道对象的类型。使用晚绑定,无需检查对象的类型,只需检查对象是否支持属性和方法即可。ECMAScript 中的所有变量都采用晚绑定方法。这样就允许执行大量的对象操作,而无任何惩罚。
公用、私有和受保护作用域
概念
在传统的面向对象程序设计中,主要关注于公用和私有作用域。公用作用域中的对象属性可以从对象外部访问,即开发者创建对象的实例后,就可使用它的公用属性。而私有作用域中的属性只能在对象内部访问,即对于外部世界来说,这些属性并不存在。这意味着如果类定义了私有属性和方法,则它的子类也不能访问这些属性和方法。
受保护作用域也是用于定义私有的属性和方法,只是这些属性和方法还能被其子类访问。
ECMAScript 只有公用作用域
对 ECMAScript 讨论上面这些作用域几乎毫无意义,因为 ECMAScript 中只存在一种作用域 - 公用作用域。ECMAScript 中的所有对象的所有属性和方法都是公用的。因此,定义自己的类和对象时,必须格外小心。记住,所有属性和方法默认都是公用的!
建议性的解决方法
许多开发者都在网上提出了有效的属性作用域模式,解决了 ECMAScript 的这种问题。
由于缺少私有作用域,开发者确定了一个规约,说明哪些属性和方法应该被看做私有的。这种规约规定在属性前后加下划线:
obj._color_ = "blue";
这段代码中,属性 color 是私有的。注意,下划线并不改变属性是公用属性的事实,它只是告诉其他开发者,应该把该属性看作私有的。
有些开发者还喜欢用单下划线说明私有成员,例如:obj._color。
静态作用域
静态作用域定义的属性和方法任何时候都能从同一位置访问。在 Java 中,类可具有属性和方法,无需实例化该类的对象,即可访问这些属性和方法,例如 java.net.URLEncoder 类,它的函数 encode() 就是静态方法。
ECMAScript 没有静态作用域
严格来说,ECMAScript 并没有静态作用域。不过,它可以给构造函数提供属性和方法。还记得吗,构造函数只是函数。函数是对象,对象可以有属性和方法。例如:
function sayHello() { alert("hello"); } sayHello.alternate = function() { alert("hi"); } sayHello(); //输出 "hello" sayHello.alternate(); //输出 "hi"
关键字 this
this 的功能
在 ECMAScript 中,要掌握的最重要的概念之一是关键字 this 的用法,它用在对象的方法中。关键字 this 总是指向调用该方法的对象,例如:
var oCar = new Object;
oCar.color = "red";
oCar.showColor = function() {
alert(this.color
);
};
oCar.showColor(); //输出 "red"
对象令人感兴趣的一点是用它们解决问题的方式。ECMAScript 中最常见的一个问题是字符串连接的性能。与其他语言类似,ECMAScript 的字符串是不可变的,即它们的值不能改变。请考虑下面的代码:
var str = "hello "; str += "world";
实际上,这段代码在幕后执行的步骤如下:
- 创建存储 "hello " 的字符串。
- 创建存储 "world" 的字符串。
- 创建存储连接结果的字符串。
- 把 str 的当前内容复制到结果中。
- 把 "world" 复制到结果中。
- 更新 str,使它指向结果。
每次完成字符串连接都会执行步骤 2 到 6,使得这种操作非常消耗资源。如果重复这一过程几百次,甚至几千次,就会造成性能问题。解决方法是用 Array 对象存储字符串,然后用 join() 方法(参数是空字符串)创建最后的字符串。想象用下面的代码代替前面的代码:
var arr = new Array(); arr[0] = "hello "; arr[1] = "world"; var str = arr.join("");
这样,无论数组中引入多少字符串都不成问题,因为只在调用 join() 方法时才会发生连接操作。此时,执行的步骤如下:
- 创建存储结果的字符串
- 把每个字符串复制到结果中的合适位置
对象令人感兴趣的一点是用它们解决问题的方式。ECMAScript 中最常见的一个问题是字符串连接的性能。与其他语言类似,ECMAScript 的字符串是不可变的,即它们的值不能改变。请考虑下面的代码:
var str = "hello "; str += "world";
实际上,这段代码在幕后执行的步骤如下:
- 创建存储 "hello " 的字符串。
- 创建存储 "world" 的字符串。
- 创建存储连接结果的字符串。
- 把 str 的当前内容复制到结果中。
- 把 "world" 复制到结果中。
- 更新 str,使它指向结果。
每次完成字符串连接都会执行步骤 2 到 6,使得这种操作非常消耗资源。如果重复这一过程几百次,甚至几千次,就会造成性能问题。解决方法是用 Array 对象存储字符串,然后用 join() 方法(参数是空字符串)创建最后的字符串。想象用下面的代码代替前面的代码:
var arr = new Array(); arr[0] = "hello "; arr[1] = "world"; var str = arr.join("");
这样,无论数组中引入多少字符串都不成问题,因为只在调用 join() 方法时才会发生连接操作。此时,执行的步骤如下:
- 创建存储结果的字符串
- 把每个字符串复制到结果中的合适位置
对象令人感兴趣的一点是用它们解决问题的方式。ECMAScript 中最常见的一个问题是字符串连接的性能。与其他语言类似,ECMAScript 的字符串是不可变的,即它们的值不能改变。请考虑下面的代码:
var str = "hello "; str += "world";
实际上,这段代码在幕后执行的步骤如下:
- 创建存储 "hello " 的字符串。
- 创建存储 "world" 的字符串。
- 创建存储连接结果的字符串。
- 把 str 的当前内容复制到结果中。
- 把 "world" 复制到结果中。
- 更新 str,使它指向结果。
每次完成字符串连接都会执行步骤 2 到 6,使得这种操作非常消耗资源。如果重复这一过程几百次,甚至几千次,就会造成性能问题。解决方法是用 Array 对象存储字符串,然后用 join() 方法(参数是空字符串)创建最后的字符串。想象用下面的代码代替前面的代码:
var arr = new Array(); arr[0] = "hello "; arr[1] = "world"; var str = arr.join("");
这样,无论数组中引入多少字符串都不成问题,因为只在调用 join() 方法时才会发生连接操作。此时,执行的步骤如下:
- 创建存储结果的字符串
- 把每个字符串复制到结果中的合适位置
对象令人感兴趣的一点是用它们解决问题的方式。ECMAScript 中最常见的一个问题是字符串连接的性能。与其他语言类似,ECMAScript 的字符串是不可变的,即它们的值不能改变。请考虑下面的代码:
var str = "hello "; str += "world";
实际上,这段代码在幕后执行的步骤如下:
- 创建存储 "hello " 的字符串。
- 创建存储 "world" 的字符串。
- 创建存储连接结果的字符串。
- 把 str 的当前内容复制到结果中。
- 把 "world" 复制到结果中。
- 更新 str,使它指向结果。
每次完成字符串连接都会执行步骤 2 到 6,使得这种操作非常消耗资源。如果重复这一过程几百次,甚至几千次,就会造成性能问题。解决方法是用 Array 对象存储字符串,然后用 join() 方法(参数是空字符串)创建最后的字符串。想象用下面的代码代替前面的代码:
var arr = new Array(); arr[0] = "hello "; arr[1] = "world"; var str = arr.join("");
这样,无论数组中引入多少字符串都不成问题,因为只在调用 join() 方法时才会发生连接操作。此时,执行的步骤如下:
- 创建存储结果的字符串
- 把每个字符串复制到结果中的合适位置