1、闭包:
闭包在实现上是一个结构体,它包存储了一个函数(通常是其入口地址)和一个相关联的环境(相当于一个符号查找表)。它既要包括约束变量(该函数内部绑定的符号),也要包括自由变量(在函数外部定义但在函数内被引用),有些函数也可能没有自由变量。闭包跟函数最大的不同在于,当捕捉闭包的时候,它的自由变量会在捕捉时被确定,这样即便脱离了捕捉时的上下文,它也能照常运行。
闭包和匿名函数经常被用作同义词。但严格来说,匿名函数就是字面意义上没有被赋予名称的函数,而闭包则实际上是一个函数的实例,也就是说它是存在于内存里的某个结构体。如果从实现上来看的话,匿名函数如果没有捕捉自由变量,那么它其实可以被实现为一个函数指针,或者直接内联到调用点,如果它捕捉了自由变量那么它将是一个闭包;而闭包则意味着同时包括函数指针和环境两个关键元素。在编译优化当中,没有捕捉自由变量的闭包可以被优化成普通函数,这样就无需分配闭包结构体,这种编译技巧被称为函数跃升
----维基百科
function fun(c){
var num = c;
return function (){
num++;
return num;
}
}
var b = fun(1);
js中利用匿名函数实现闭包
b就是一个闭包,它保存了fun的自由变量num和A函数的入口地址;一般情况下,fun执行完后,系统就会把其内部变量回收,但是由于b引用了fun函数内部的函数A,所以fun不会被回收
个人理解:匿名函数相当于把一段代码放到一个特定的位置执行,闭包相当于把一段代码从一个位置取出来,放到其它位置执行
2、this
一、误解:
(1)指向自身:人们很容易把this理解成指向函数自身,这个推断从英语的语法角度来说是说得通的。那么为什么需要从函数内部引用函数自身呢?常见的原因是递归(从函数内部调用这个函数)或者可以写一个在第一次被调用后自己解除绑定的事件处理器。
JavaScript的新手开发者会认为,既然函数看作一个对象(JavaScript中的所有函数都是对象),那就可以在调用函数时存储状态(属性的值)。这是不可行的。
function foo(num) {
console.log( "foo: " + num );
// 记录 foo 被调用的次数
this.count++;
}
foo.count = 0;
var i;
for (i=0; i<10; i++) { if (i > 5) {
foo( i );
}
}
// foo: 6 // foo: 7 // foo: 8 // foo: 9
// foo 被调用了多少次?
console.log( foo.count ); // 0 -- WTF?
执行foo.count = 0时,的确向函数对象foo添加了一个属性count。但是函数内部代码this.count中的this并不是指向那个函数对象,所以虽然属性名相同,根对象却并不相同。
(2)this指向函数作用域。这个问题有点复杂,因为在某种情况下它是正确的,但是在其他情况下它却是错误的。需要明确的是,this在任何情况下都不指向函数的词法作用域。在JavaScript内部,作用域确实和对象类似,可见的标识符都是它的属性。但是作用域“对象”无法通过JavaScript代码访问,它位于JavaScript引擎内部。
function foo() {
var a = 2;
this.bar();
}
function bar() {
console.log( this.a );
}
foo(); // ReferenceError: a is not defined
二、this到底是什么?
(1)this是在运行时进行绑定的,并不是在编写时绑定,它的上下文取决于函数调用时的各种条件。this的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。当一个函数被调用时,会创建一个活动记录(有时候也成为执行上下文)。这个记录会包含函数在哪里被调用(调用栈)、函数的调用方法、传入的参数等信息。this就是记录的其中一个属性,会在函数执行的过程中用到。
(2)调用栈
funciton baz() {
//当前调用栈是:baz,因此,当前调用位置是全局作用域
console.log("baz");
bar();// <-- bar的调用位置
}
function bar() {
//当前调用栈是baz -> bar,因此,当前调用位置在baz中
console.log("bar");
foo();//< -- foo的调用位置
}
function foo() {
//当前调用栈是:baz -> bar -> foo,因此,当前调用位置在bar中
console.log("foo");
}
baz();// < -- baz的调用位置
全局作用域中声明的var变量,会直接变成window的属性。
(3)绑定规则
1.默认绑定:
function foo() {
console.log( this.a );
}
var a = 2;
foo(); // 2
foo() 是直接使用不带任何修饰符的函数引用进行调用的,因此只能使用默认绑定,无法应用其他规则。虽然this的绑定规则完全取决于调用位置,但是只有foo()运行在strict mode下时,默认绑定才能绑定到全局对象;严格模式下与foo()的调用位置无关。
2.隐式绑定:
需要考虑的规则是调用位置是否有上下文对象,或者说是否被某个对象拥有或者包含,不过这种说法可能会造成一些误导。
function foo() {
console.log(this.a);
}
var obj = {
a: 2,
foo: foo,
}
obj.foo(); //2
首先需要注意的是foo()的声明方式,及其之后是如何被当做引用属性添加到obj中的。但是无论是直接在obj中定义还是先定义再添加为引用属性,这个属性严格来说都不属于obj对象。
然而,调用位置会使用obj上下文来引用函数,因此你可以说函数被调用时obj对象“拥有”或者“包含”它。
对象属性引用链中只有最顶层或者最后一层会影响调用位置
function foo() {
console.log(this.a);
}
var obj2 = {
a: 42,
foo: foo,
};
var obj1 = {
a: 2,
obj2: obj2,
}
obj1.obj2.foo(); //42
3.显式绑定:call()、apply()
4、new绑定
function foo(a) {
this.a = a;
}
var bar = new foo(2);
console.log( bar.a ); // 2
(4)优先级
显式绑定 > new >隐式绑定
三、对象
(1)in操作符会检查属性是否在对象及其原型链中。相比之下,hasOwnProperty只会检查属性是否在对象中,不会检查原型链。in操作符可以检查容器内是否有某个值,但是它实际上检查的是某个属性名是否存在。对于数组来说这个区别很重要,4 in [2, 4 ,6]的结果并不是true,因为[2, 4, 6]这个数组中包含的属性名是0、1、2,并没有4.
(2)枚举
可枚举就相当于“可以出现在对象属性的遍历中(for...in)”,在数组上应用for...in循环有时会产生出人意料的结果,因为这种枚举仅包含所有数值索引,还会包含所有可枚举属性。最好只在对象上应用for...in循环,如果要遍历数组就使用传统的for循环来遍历数值索引。Object.keys()会返回一个数组,包含所有可枚举属性,Object.getOwnPropertyNames()会返回一个数组,包含所有属性,无论他们是否可枚举。in和hasOwnProperty()的区别在于是否查找原型链,然而,Object.keys()和Object.getOwnPropertyName()都只会查找对象直接包含的属性
(3)继承:JavaScript只有一种结构:对象。每个实例对象都有一个私有属性(__proto__)指向它的构造函数的原型对象的链。该原型对象也有一个自己的原型对象(__proto__),层层向上直到一个对象的原型对象为null。根据定义,null没有原型,并作为这个原型链的最后一个环节。类可以通过prototype来访问自己的原型。
(4)原型链:实际上是class(构造函数)的一个属性,只不过该class(构造函数)创建的对象可以直接访问它里面的属性。