定义函数表达式的体式格局有两种:
// 函数声明
function functionName(params) {
...
}
// 函数表达式有几种差别的体式格局,下面是最罕见的一种
var functiOnName= function(params) {
...
}
sayHi(); // 毛病,函数还不存在
var sayHi = function () {
console.log('Hi!');
};
condition
为true
时,运用一个定义,不然运用另一个定义。现实上,在ECMAScript中属于无效语法,Javascript引擎会尝试修正毛病,将其转换为合理状况。但题目是浏览器尝试修正毛病的做法并不一致。大多数浏览器会返回第二个声明,疏忽condition
的值;Firefox会在condition
为true的时刻返回第一个声明。因而这类做法很风险,不应该涌如今你的代码中。// 不要如许做!
if (condition) {
function sayHi() {
console.log('Hi!');
}
} else {
function sayHi() {
console.log('Yo!');
}
}
// 可以如许做
var sayHi;
if (condition) {
sayHi = function() {
console.log('Hi!');
}
} else {
sayHi = function() {
console.log('Yo!');
}
}
createComparisonFunction()
就返回了一个匿名函数。function createComparisonFunction (propertyName) {
return function (object1, object2) {
var value1 = object1[propertyName];
var value2 = object2[propertyName];
if (value1
} else if (value1 > value2) {
return 1;
} else {
return 0;
}
}
}
// 典范的递归阶乘函数
function factorial (num) {
if (num <= -1) {
return 1;
} else {
return num * factorial(num - 1);
}
}
// 把factorial() 函数保留在变量anotherFactorial中
var anotherFactorial = factorial;
// 将factorial设置为null
// 如今指向原始函数的援用只剩下anotherFactorial
factorial = null;
// 原始函数必需实行factorial()
// 但factorial不再是函数,所以致使失足
anotherFactorial(4); // throw error!
// 非严厉形式
function factorial (num) {
if (num <= -1) {
return 1;
} else {
return num * arguments.callee(num - 1);
}
}
arguments.callee
,会致使失足。可以运用定名函数表达式来杀青雷同的效果var factorial = (function f(num) {
if (num <= -1) {
return 1;
} else {
return num * f(num - 1);
}
})
createComparisonFunction()
函数为例function createComparisonFunction (propertyName) {
return function (object1, object2) {
// 下面两行代码接见了外部函数中的变量propertyName
// 纵然这个内部函数被返回了,而且是在其他处所被挪用了
// 它依旧可以接见变量 propertyName
var value1 = object1[propertyName];
var value2 = object2[propertyName];
if (value1
} else if (value1 > value2) {
return 1;
} else {
return 0;
}
}
}
createComparisonFunction()
的作用域。要搞清晰个中细节,必需从明白函数被挪用的时刻都邑发作什么入手。第4章引见过 作用域链。当某个函数被 挪用 时会发作以下事变:
function compare(value1, value2) {
if (value1
} else if (value1 > value2) {
return 1;
} else {
return 0;
}
}
var result = compare(5, 10);
背景的每一个实行环境都有一个示意变量的对象——变量对象。全局环境的变量对象一直存在,而像compare()
函数如许的部分环境的变量对象,则只在函数实行的过程当中存在。
compare()
函数时,会建立一个预先包括全局变量对象的作用域链,这个作用域链被保留在内部的[[scope]]属性中。compare()
函数时,会为函数建立一个实行环境,然后经由过程复制函数的[[scope]]属性中的对象构建起实行环境的作用域链。关于本例, compare()
函数的实行环境而言,其作用域链中包括两个变量对象:
function createComparisonFunction (propertyName) {
return function (object1, object2) {
// 下面两行代码接见了外部函数中的变量propertyName
// 纵然这个内部函数被返回了,而且是在其他处所被挪用了
// 它依旧可以接见变量 propertyName
// 即为 createComparisonFunction 的运动对象
var value1 = object1[propertyName];
var value2 = object2[propertyName];
if (value1
} else if (value1 > value2) {
return 1;
} else {
return 0;
}
}
}
// 建立比较函数
// 挪用了 createComparisonFunction() 要领
// 建立了 createComparisonFunction 的运动对象
// 返回内部的匿名函数 保留在 compareNames
// createComparisonFunction 实行终了
// 但它的运动对象仍被 内部匿名函数援用,所以运动对象依旧存在,不会烧毁
var compareNames = createComparisonFunction("name");
// 此时result挪用了 保留在 compareNames 的匿名函数
// 该匿名函数坚持了对 createComparisonFunction 运动对象的援用
var result = compareNames({ name: "Nicholas" }, { name: "Greg" });
// 纵然 compareNames 实行终了,createComparisonFunction 运动对象依旧存在
// 须要手动消弭对匿名函数的援用(以便开释内存)
compareNames = null;
compareNames
中,而经由过程将其设置为null消弭援用,就即是关照渣滓接纳例程将其消弭。跟着匿名函数的作用域链被烧毁,其他作用域链(除了全局作用域)也都可以安全地的烧毁了。图7-2展现了挪用compareNames()
的过程当中发生的作用域链之间的关联function createFunctions() {
var result = new Array();
for (var i=0; i <10; i++) {
// 赋值给数组元素的是匿名函数自身,不是详细的值
// 所以在 createFunctions() 实行终了后,挪用数组内的函数,返回的是变量i的值
// 而变量i在实行终了后,即是 10
result[i] = function() {
// 返回指向变量 i 的指针
return i;
};
}
return result;
}
result
里的每一项函数都应该返回本身的索引值。但现实上每一个函数返回的都是10
。createFunctions()
函数的运动对象,所以它们援用的都是同一个变量i
。当createFunctions()
函数返回后,变量i
的值是10
,此时每一个函数都援用着保留变量i
的同一个变量对象,所以在每一个函数内部i
的值都是10
.function createFunctions() {
var result = new Array();
for (var i=0; i <10; i++) {
// 此时返回的里层匿名函数挪用了外层匿名函数的 num
// 里层匿名函数建立并返回了一个接见 num 的闭包
// 如此一来 result 数组中的每一个函数都有本身的num变量副本
result[i] = function(num) {
// 返回建立的另一个匿名函数
return function() {
return num;
};
}(i);
}
return result;
}
this
对象在闭包中运用this
对象也能够会致使一些题目。this
对象是在运转时基于函数的实行环境绑定的:
this
即是window
this
即是谁人对象。this
对象一般指向window
(经由过程call()
apply()
转变函数的实行环境的状况除外)。但有时刻因为变成写闭包的体式格局差别,这一点能够不会那末显著var name = "The Window";
var object = {
name: "My Object",
getNameFunc: function() {
return function() {
return this.name;
};
}
};
// 在非严厉形式下
object.getNameFunc()(); // "The Window"
this
和arguments
。内部函数在搜刮这两个变量时,只会搜刮到其运动对象为止,因而永久不能够直接接见外部函数中的这两个变量(这一点经由过程图7-2可以看的清晰)。this
对象保留在一个闭包可以接见到的变量里,就可以让闭包接见该对象了。var name = "The Window";
var object = {
name: "My Object",
getNameFunc: function() {
var that = this;
return function() {
return that.name;
};
}
};
object.getNameFunc()(); // "My Object"
this
的值能够会不测的转变。var name = "The Window";
var object = {
name: "My Object",
getName: function() {
return this.name;
}
};
// this 指向 object
object.getName(); // "My Object"
// 加上了括号,看似在援用一个函数
// 但 (object.getName) 和 object.getName 的定义是雷同的
// 所以这行代码与上面的代码无异
(object.getName)(); // "My Object"
// 非严厉形式
// 赋值语句会返回 object.getName 的匿名函数
// 相当于将匿名函数在全局环境下运转
(object.getName = object.getName)(); // "The Window"
this
的值不能获得保持,效果就返回了 &#8220;The Window&#8221; 。HTML
元素,那末就意味着该元素将没法被烧毁function assignHandler() {
var element = document.getElementById("someElement");
element.Onclick= function() {
console.log(element.id);
};
}
element
元素处置惩罚顺序的闭包,而这个闭包则又建立了一个轮回援用(事宜将在第13章议论)。因为匿名函数保留了一个对assignHandler()
的运动对象的援用,因而就会致使没法削减element
的援用数。只需匿名函数存在,element
的援用数至少是1。// 以下修正可以防止这个题目
function assignHandler() {
var element = document.getElementById("someElement");
var id = element.id;
element.Onclick= function() {
console.log(id);
};
element = null;
}
element
。纵然闭包不直接援用element
,包括函数的运动对象也依旧会保留一个援用。因而有必要把element
变量设置为null
function outputNumerbs(cout) {
for (var i=0; i
}
console.log(i); // 计数
}
i
只会在for
轮回的语句块中有定义,轮回一旦完毕,变量i
就会被烧毁。但是在Javascript中,变量i
是定义在outputNumbers()
的运动对象中的,因而从它有定义最先,就可以在函数内部到处接见它。纵然像下面如许毛病的从新声明变量也不会转变值。function outputNumerbs(cout) {
for (var i=0; i
}
var i; // 从新声明变量
console.log(i); // 计数
}
(function() {
// 这里是块级作用域
...
})()
// 罕见的代码片断
// 定义了一个函数,然后马上挪用它
var someFunction = function() {
// 这里是块级作用域
...
};
someFunction();
Javascript
将function
关键字当作一个函数声明的最先,而函数声明背面不能跟圆括号。(函数表达式可以)function() {
// 这里是块级作用域
...
}() // 失足!
(function() {
// 这里是块级作用域
...
}())
function outputNumbers(cout) {
// 这里是一个闭包,匿名函数可以接见 cout
(function () {
for (var i=0; i
}
})();
// 在这里挪用变量 i 会报错
console.log(i); // throw error
}
(function() {
var now = new Date();
if (now.getMonth() == 0 && now.gettDate() == 1) {
console.log('Haapy new year!");
}
})();
有权接见私有变量和私有函数的公有要领称为 特权要领(privileged method) 。有两种建立特权要领的体式格局:
// 组织函数Person
// 入参 name 是它的私有变量
function Person(name) {
this.getName = function() {
return name;
};
this.setName = function(value) {
name = value;
};
}
var person = new Person("Nicholas");
console.log(person.getName()); // "Nicholas"
person.setName("Greg");
console.log(person.getName()); // "Greg"
(function() {
// 私有变量
var privateVariable = 10;
// 私有函数
function privateFunction() {
return false;
}
// 组织函数
// 这里没有运用var操作符,自动建立全局变量
// 严厉形式下不能运用
MyObject = function() {};
// 公有/特权要领
MyObject.prototype.publicMethod = function() {
privateVariable++;
return privateFunction();
};
})();
(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");
console.log(person1.getName()); // "Nicholas"
person1.setName("Greg");
console.log(person1.getName()); // "Greg"
var person2 = new Person("Michael");
console.log(person1.getName()); // "Michael"
console.log(person2.getName()); // "Michael"
Person
组织函数与getName()
setName()
要领一样,都有权接见私有变量name
。name
变成了一个静态的、由一切实例同享的属性。var application = function() {
// 私有变量和函数
var compOnents= new Array();
// 初始化
components.push(new BaseComponent());
// 大众
return {
getComponentCuont: function() {
return components.length;
},
registerComponent: function(component) {
if (typeof compOnent== "object") {
components.push(component)
}
}
};
}();
application
对象。Object
的实例,因为终究要经由过程一个对象字面量来示意他。application
对象必需是BaseComponent
的实例,可以以下代码var application = function() {
// 私有变量和函数
var compOnents= new Array();
// 初始化
components.push(new BaseComponent());
// 建立 application 的一个部分副本
var app = new BaseComponent();
// 大众接口
app.getCompOnentCuont= function() {
return components.length;
};
app.registerCompOnent= function(component) {
if (typeof compOnent== "object") {
components.push(component);
}
};
// 返回这个副本
return app;
}();