标签:
第一:函数声明
第二:函数表达式
函数声明提升
sayHi();
function sayHi(){
alert("Hello world!")
}
递归函数是在一个函数通过名字调用自身的情况下构成的。
function fac(num) {
if (num <=1) {
return 1;
} else {
return num * fac(num - 1);
}
}
var anotherFac = fac;
fac = null;
alert(anotherFac(4))
上述代码会报错因为,fac = null解除了fac对于函数的引用,使得只有anotherFac指向函数,可以通过
function fac(num) {
if (num <=1) {
return 1;
} else {
return num * arguments.callee(num - 1);
}
}
var anotherFac = fac;
fac = null;
alert(anotherFac([4,4]))
arguments.callee
是一个指向正则执行的函数的指针。
但是use strict
情况下,arguments.callee
会报错。
可以使用命名函数表达式
var factory = (function fac(num) {
if (num <=1) {
return 1;
} else {
return num * fac(num - 1);
}
})
闭包是指有权访问另一个函数作用域变量的函数。创建闭包的常见方式,就是在一个函数内部创建另外一个函数。
因为内部匿名函数的作用域链(一个栈结构)指向2:全局变量对象,1:包含函数变量对象,0:自己的变量对象。所以虽然随着包含函数的
执行结束,包含函数的作用域链销毁了,但是因为任然有函数的作用域链指向包含函数的变量对象,所以不会触发回收机制。
回收机制:为了及时的回收内存资源,当没有指针指向一个存储在内存中的对象时,该对象就会被回收,即销毁。
举例说明
function test(arg){
var ss = arg;
return function(opt){
console.log(ss); //首先,在作用域链顶层指向的自己的变量对象中查找ss,没查找到,继续向上一级作用域链即包含函数变量对象中查找,找到ss = arg,输出
console.log(opt); //输出参数opt
}
}
var testCache = test("name"); //创建函数,此时ss = arg ="name";testCache指向局部函数
var result = testCache("test"); //调用局部函数,此时opt = "test",这个时候才会触发匿名函数内部的log
因为testCache指向内部的匿名函数(不满足回收机制),所以内部的匿名函数一直得不到销毁,test的作用域链
虽然被销毁了,但是他的活动对象始终留在内存中,直到匿名函数被销毁之后。
testCache = null //解除对匿名函数的引用(以便释放内存)
作用域链这种配置机制引出了一个值得注意的副作用,即闭包只能取得包含函数中任何变量的最后一个值。
闭包保存的是整个变量对象,而不是某个特殊的变量。
function test(){
var result = new Array();
for (var i = 0; i < 10; i++) {
result[i] = function(){
return i;
}
}
return result;
}
var testCache = test(); //testCache指向result
for (var i = 0; i < 10; i++) {
var anonymous = testCache[i]; // testCache[i]指向result数组中对应的第i个指向的匿名函数;
console.log(anonymous()); //调用匿名函数,并输出匿名函数返回的值
}
以上代码输出的都是10,并非预期的1~10;因为anonymous指向的匿名函数保存的都是test函数的活动对象,所以他们引用的都是同一个i;
当test函数调用完成并返回result之后,变量i的值为10,所以anonymous引用的都是保存变量i的同一个变量对象。
function test(){
var result = new Array();
for (var i = 0; i < 10; i++) {
result[i] = (function(num){
return function () {
console.log(i);
return num;
}
})(i)
}
return result;
}
var testCache = test(); //testCache指向result
for (var i = 0; i < 10; i++) {
var anonymous = testCache[i]; // testCache[i]指向result数组中对应的第i个指向的匿名函数;
console.log(anonymous()); //调用匿名函数,并输出匿名函数返回的值
// anonymous()
}
第二份代码中,我们没有直接把闭包复制给数组,而是定义了一个匿名函数,并将立即执行该匿名函数的结果赋值给数组。这里的匿名函数有一个参数num,
也就是最后函数要返回的值,num是由i按值传递的,所以每次将i的当前值给参数num,而在这个匿名函数内部,又创建并返回一个访问num的闭包。这样result数组
中的每个函数都有自己num变量的一个副本,因此可以返回各自不同的值。
匿名函数的执行环境具有全局性,因此其this对象通常指向window(可以用call()或者apply()改变指向)
每个函数在被调用的时候都会自动获取两个特殊变量:this和arguments,内部函数在搜索这两个变量时候,只会搜索到其活动对象位置,
因此永远也不可能直接访问外部函数中的这两个变量。
var name = "this window";
var object = {
name: "my object",
getNameFun: function(){
return function(){
return this.name;
}
}
}
console.log(object.getNameFun()()); //log "this window" 匿名函数this指向全局window
如果想要上述代码中内部的返回匿名函数能够return到object里的变量值,
var name = "this window";
var object = {
name: "my object",
getNameFun: function(){
var that = this;
return function(){
return that.name;
}
}
}
console.log(object.getNameFun()()); //log "my object"将that 指向的是object,闭包可以访问包含函数中的变量that
function test(){
var el = document.getElementById(‘someOne‘);
el.onclick = function(){
alert(el.id);
}
}
如上test函数里有对dom元素的引用,而闭包中又对dom元素进行了引用,这样,dom元素占用的内存始终不会被回收。
function test(){
var el = document.getElementById(‘someOne‘);
var id = el.id;
el.onclick = function(){
alert(id);
}
el = null;
}
不过可以小小的改动,即可让dom被回收。
JavaScript中没有块级作用域的概念,这意味着在块级语句中定义的变量,实际上是在包含函数中而非语句中创建,请看下例
function test(count){
for (var i = 0; i < count; i++) {
console.log(i);//输出1~count-1
}
console.log(i);//输出count
}
test(5)
在C++,Java等语言中,变量i只会在for循环的语句块中有定义,循环一旦结束,变量i便会被销毁。但是JavaScript中
变量i是定义在test()的活动对象中的。
因此,匿名函数可以用来模仿块级作用域。
function(){
//这里是块级作用域
}() //出错
上述代码会导致语法错误,是因为JavaScript将function关键字当做一个函数声明的开始,而函数声明后面不能跟圆括号。
然而,函数表达式的后面可以跟圆括号。要将函数声明转换成函数表达式,只需要像下面这样给他加上一对圆括号即可
(function(){
//这里是块级作用域
})()
严格的说来,js中没有私有成员的概念,所以对象属性都是公有的。不过,倒是有一个私有变量的概念,
任何在函数内定义的变量,都可以认为是私有变量,因为在函数的外部无法访问到这些变量。私有变量
包括函数的参数,局部变量和在函数内部定义的其他函数。
我们把有权访问私有变量和私有函数的公有方法称为特权方法。有两种在对象上创建特权方法的方式。
第一种,是在构造函数中定义特权方法,基本模式如下:
function Person(name){
var year = 2015;
this.getName = function(){
return name;
}
this.setName = function(Value){
name = Value;
}
console.log(this);
}
var person = new Person("example"); //实例化一个对象,log(this)是实例化的Person对象,person是指向该对象的指针
注意:如果采用Person("test");
直接调用该函数,则此时this指代全局对象window,将会在window上添加上2个方法。所以必须用new
实例化对象。每次实例化的对象中,name值都不同,同时每次都会重新创建这两个函数,这个也是使用构造函数来创建特权方法的缺点。
2个方法虽然能够通过作用域链访问到year这个私有变量,但是如果year并不是出现在特权方法中,则方法的closure(闭包中没有year这个属性)。
通过在私有作用域中定义私有变量或函数,同样也可以创建特权方法。
所以
(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("test");
console.log(person1.getName());
注意点:这个模式在定义构造函数时并没有使用函数声明,而是使用函数表达式。函数声明只能创建局部函数。
我们也没有在声明Person时使用var,因此Person就是一个全局变量。
与前者的区别在于私有变量和函数都是由实例共享的
前面的模式是用于为自定义类型创建私有变量和特权方法的。而模块模式则是为单例创建私有变量和
特权方法。所谓单例,指的就是只有一个实例对象。如下所示:
var singleton = function(){
// 私有变量和函数
var privateVariable = 10;
function privateFunction(){
return false;
}
// 特权/公有方法和属性
return {
publicProperty: true,
publicMethod: function(){
privateVariable++;
return privateFunction();
}
}
}()
在web应用程序中,经常要使用一个单例来管理应用程序级的信息。
var application = function(){
// 私有变量和函数
var privateVariable = 10;
function privateFunction(){
return false;
};
//实例化BaseComponent,app的
var app = new BaseComponent();
// 特权/公有方法和属性
app.publicProperty = true;
app.publicMethod = function(){
privateVariable++;
return privateFunction();
};
return app;
}()
这种增强的模块模式适用于那些单例必须是某种类型的实例,同时还必须添加某些属性和方法对其加以增强的情况。
JavaScript 有一套完全不同于其它语言的对 this 的处理机制。 在五种不同的情况下 ,this 指向的各不相同。
- 全局范围内
this
当在全部范围内使用 this,它将会指向全局对象。
- 函数调用
foo();
这里 this 也会指向全局对象。
- 方法调用
test.foo();
这个例子中,this 指向 test 对象。
- 调用构造函数
new foo();
如果函数倾向于和 new 关键词一块使用,则我们称这个函数是 构造函数。 在函数内部,this 指向新创建的对象。
- 显式的设置 this
function foo(a, b, c) {}
var bar = {};
foo.apply(bar, [1, 2, 3]); // 数组将会被扩展,如下所示
foo.call(bar, 1, 2, 3); // 传递到foo的参数是:a = 1, b = 2, c = 3
当使用 Function.prototype 上的 call 或者 apply 方法时,函数内的 this 将会被 显式设置为函数调用的第一个参数。
一个常见的误解是 test 中的 this 将会指向 Foo 对象,实际上不是这样子的。
Foo.method = function() {
function test() {
// this 将会被设置为全局对象(译者注:浏览器环境中也就是 window 对象)
}
test();
}
为了在 test 中获取对 Foo 对象的引用,我们需要在 method 函数内部创建一个局部变量指向 Foo 对象。
Foo.method = function() {
var that = this;
function test() {
// 使用 that 来指向 Foo 对象
}
test();
}
that 只是我们随意起的名字,不过这个名字被广泛的用来指向外部的 this 对象。 在 闭包 一节,我们可以看到 that 可以作为参数传递。
另一个看起来奇怪的地方是函数别名,也就是将一个方法赋值给一个变量。
var test = someObject.methodTest;
test();
上例中,test 就像一个普通的函数被调用;因此,函数内的 this 将不再被指向到 someObject 对象。
标签:
原文地址:http://blog.csdn.net/liusheng95/article/details/51263275