In [2]:
// golbal define function alert
if((typeof alert) === 'undefined') {
global.alert = function(message) {
console.log(message);
}
}
function functionName(){
//noop
}
//works only in Firefox, Safari, Chrome, and Opera
alert(functionName.name); //"functionName"
Out[2]:
关于函数声明,它的一个重要特性就是函数声明提升(function declaration hoisting),意思是在执行代码之前会先读取函数声明:
In [3]:
sayHi();
function sayHi(){
alert("Hi!");
}
Out[3]:
下面代码不推荐:
In [6]:
var condition = true;
//never do this!
if(condition){
function sayHi(){
alert("Hi!");
}
} else {
function sayHi(){
alert("Yo!");
}
}
sayHi();
Out[6]:
可以这样做:
In [7]:
var condition = true;
var sayHi;
//never do this!
if(condition){
sayHi = function(){
alert("Hi!");
}
} else {
sayHi = function(){
alert("Yo!");
}
}
sayHi();
Out[7]:
In [8]:
function factorial(num){
if (num <= 1){
return 1;
} else {
return num * factorial(num-1);
}
}
var anotherFactorial = factorial;
factorial = null;
alert(anotherFactorial(4)); //error!
arguments.callee是一个指向正在执行的函数的指针:
In [9]:
function factorial(num){
if (num <= 1){
return 1;
} else {
return num * arguments.callee(num-1);
}
}
var anotherFactorial = factorial;
factorial = null;
alert(anotherFactorial(4)); //24
Out[9]:
In [12]:
function createFunctions(){
var result = new Array();
for (var i=0; i < 10; i++){
result[i] = function(){
return i;
};
}
return result;
}
var funcs = createFunctions();
//every function outputs 10
for (var i=0; i < funcs.length; i++){
alert(funcs[i]() + "<br />");
}
Out[12]:
因为每个函数的作用域链中都保存着createFunctions()函数的活动对象,所以它们引用的都是同一个变量i。当createFunctions()返回后,变量i的值是10,此时每个函数引用着保存变量i的同一个变量对象,所以在每个函数内部i的值都是10。可以通过创建另一个匿名函数强制让闭包的行为符合预期:
In [13]:
function createFunctions(){
var result = new Array();
for (var i=0; i < 10; i++){
result[i] = function(num){
return function(){
return num;
};
}(i);
}
return result;
}
var funcs = createFunctions();
//every function outputs 10
for (var i=0; i < funcs.length; i++){
alert(funcs[i]() + "<br />");
}
Out[13]:
In [14]:
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
return function(){
return this.name;
};
}
};
alert(object.getNameFunc()()); //"The Window"
Out[14]:
In [15]:
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
var that = this;
return function(){
return that.name;
};
}
};
alert(object.getNameFunc()()); //"MyObject"
Out[15]:
下面第一行代码跟平常一样调用object.getName(),第二行代码在调用这个方法前先给它加上了括号,不改变了执行环境。第三行代码赋值行为,改变了执行环境:
In [16]:
var name = "The Window";
var object = {
name : "My Object",
getName: function(){
return this.name;
}
};
alert(object.getName()); //"My Object"
alert((object.getName)()); //"My Object"
alert((object.getName = object.getName)()); //"The Window" in non-strict mode
Out[16]:
In [17]:
function outputNumbers(count){
for (var i=0; i < count; i++){
alert(i);
}
alert(i); //count
}
outputNumbers(5);
Out[17]:
即使像下面这样错误地重新声明同一个变量,也不会改变它的值:
In [19]:
function outputNumbers(count){
for (var i=0; i < count; i++){
alert(i);
}
var i; //variable re-declared
alert(i); //count
}
outputNumbers(5);
Out[19]:
我们在for循环外部插入了一个私有作用域。在匿名函数中定义的任何变量,都会在执行结束时被销毁。因此,变量i只能在循环中被使用,使用后即被销毁。而在私有作用域中能够访问变量count,是因为这个匿名函数是一个闭包,它能够访问包含作用域中的所有变量。
这种技术经常在全局作用域中被用在函数外部,从而限制向全局作用域中添加过多的变量和函数。
In [20]:
function outputNumbers(count){
(function () {
for (var i=0; i < count; i++){
alert(i);
}
})();
alert(i); //causes an error
}
outputNumbers(5);
Out[20]:
任何在函数中定义的变量,都可以认为是私有变量,因为不能在函数的外部访问这些变量。私有变量包括函数的参数,局部变量和在函数内部定义的其他函数。
我们把有权访问私有变量和私有函数的公有方法称为特权方法(privileged method)。有两种在对象上创建特权方法的方式。第一种是在构造函数中定义特权方法:
function MyObject(){
// 私有变量和私有函数
var privateVariable = 10;
function privateFunction() {
return false;
}
// 特权方法
this.publicMethod = function() {
privateVariable++;
return privateFunction();
};
}
In [21]:
function Person(name){
this.getName = function(){
return name;
};
this.setName = function (value) {
name = value;
};
}
var person = new Person("Nicholas");
alert(person.getName()); //"Nicholas"
person.setName("Greg");
alert(person.getName()); //"Greg"
Out[21]:
不过构造函数中定义特权方法也有一个缺点,那就是你必须使用构造函数模式来达到这个目的。第6章曾经讨论过,构造函数模式的缺点是针对每个实例都会创建同样的一组新方法,而使用静态私有变量来实现特权方法可以避免这个问题。
通过在私有作用域中定义私有变量或函数:
(function(){
// 私有变量和私有函数
var privateVariable = 10;
function privateFunction() {
return false;
}
// 构造函数
MyObject = function(){
};
// 公有/特权方法
this.publicMethod = function() {
privateVariable++;
return privateFunction();
};
})();
In [22]:
(function(){
var name = "";
Person = function(value){
name = value;
};
Person.prototype.getName = function(){
return name;
};
Person.prototype.setName = function (value){
name = value;
};
})();
var person1 = new Person("Nicholas");
alert(person1.getName()); //"Nicholas"
person1.setName("Greg");
alert(person1.getName()); //"Greg"
var person2 = new Person("Michael");
alert(person1.getName()); //"Michael"
alert(person2.getName()); //"Michael"
Out[22]:
以这种方式创建静态私有变量会因为使用原型而增进代码复用,当每个实例都没有自己的私有变量。到底是使用实例变量,还是静态私有变量,最终要视情况而定。
JavaScript是以对象字面量的方式来创建单例对象的:
var singleton = {
name : value,
method: function() {
//这里是方法代码
}
};
模块模式通过为单例添加私有变量和特权方法能够使其得到增强:
var singleton = function(){
// 私有变量和私有函数
var privateVariable = 10;
function privateFunction(){
return false;
}
// 特权/公有方法和属性
return {
publicProperty: true;
publicMethod: function(){
privateVariable++;
return privateFunction();
}
};
}();
In [23]:
function BaseComponent(){
}
function OtherComponent(){
}
var application = function(){
//private variables and functions
var components = new Array();
//initialization
components.push(new BaseComponent());
//public interface
return {
getComponentCount : function(){
return components.length;
},
registerComponent : function(component){
if (typeof component == "object"){
components.push(component);
}
}
};
}();
application.registerComponent(new OtherComponent());
alert(application.getComponentCount()); //2
Out[23]:
这种增强的模块模式适合那些单例必须是某种类型的实例,同时还必须添加某些属性和(或)方法对其加以增强的情况。
var singleton = function(){
// 私有变量和私有函数
var privateVariable = 10;
function privateFunction(){
return false;
}
// 创建对象
var object = new CustomType();
// 添加特权/公有方法和属性
object.publicProperty = true;
object.publicMethod = function(){
privateVariable++;
return privateFunction();
};
return object;
}();
In [24]:
function BaseComponent(){
}
function OtherComponent(){
}
var application = function(){
//private variables and functions
var components = new Array();
//initialization
components.push(new BaseComponent());
//create a local copy of application
var app = new BaseComponent();
//public interface
app.getComponentCount = function(){
return components.length;
};
app.registerComponent = function(component){
if (typeof component == "object"){
components.push(component);
}
};
//return it
return app;
}();
alert(application instanceof BaseComponent);
application.registerComponent(new OtherComponent());
alert(application.getComponentCount()); //2
Out[24]:
In [ ]: