个人学习笔记

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(构造函数)创建的对象可以直接访问它里面的属性。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值