1.js中的闭包是什么,有什么作用
[MDN解释]:函数和对其周围状态(词法环境)的引用捆绑在一起构成闭包。也就是说,闭包可以让你从内部函数访问外部函数作用域,在JS中,每当函数被创建,就会在函数生成时生成闭包。
答:【函数】和【函数内部能访问到的变量】(也叫环境)的总和,就是一个闭包
闭包不是需要函数套函数,然后return一个函数吗?为什么要函数套函数呢?
答:是因为需要局部变量,所以才要把变量放在一个函数里,如果不放在一个函数里,就是一个全局变量了,就达不到使用闭包的目的---隐藏变量,所以函数套函数只是为了造出一个局部变量,跟闭包无关
为什么要return 函数呢?
答:因为如果不return,你就无法使用闭包。把return 函数 改成window.函数也是一样的,只要让外面可以访问到这个函数就行了。所以return 函数只是为了函数能被使用,也跟闭包无关。
闭包的作用
答:闭包常常用来[间接访问一个变量]。换句话说,[隐藏一个变量]
假设我们在做一个游戏,在写其中关于「还剩几条命」的代码。
如果不用闭包,你可以直接用一个全局变量:
window.lives = 30 // 还有三十条命
这样看起来很不妥。万一不小心把这个值改成 -1 了怎么办。所以我们不能让别人「直接访问」这个变量。怎么办呢?
用局部变量。
但是用局部变量别人又访问不到,怎么办呢?
暴露一个访问器(函数),让别人可以「间接访问」。
代码如下:
!function(){
var lives = 50
window.奖励一条命 = function(){
lives += 1
}
window.死一条命 = function(){
lives -= 1
}
}()
那么在其他的 JS 文件,就可以使用 window.奖励一条命() 来涨命,使用 window.死一条命() 来让角色掉一条命。
闭包是 JS 函数作用域的副产品。
换句话说,正是由于 JS 的函数内部可以使用函数外部的变量,所以这段代码正好符合了闭包的定义。而不是 JS 故意要使用闭包。
很多编程语言也支持闭包,另外有一些语言则不支持闭包。
只要你懂了 JS 的作用域,你自然而然就懂了闭包,即使你不知道那就是闭包!
2.this关键字
this是JS的关键字,指函数执行时的上下文,跟函数定义时的上下文无关。随着函数使用场合的不同,this的值也会发生变化。但有一个总的原则,就是this指代的是调用函数的那个对象
全局上下文
在全局上下文中,也就是在任何函数体外部,this指代全局对象
//在浏览器中,this指代全局对象 window
console.log(this === window) // true
函数上下文
在函数上下文中,也就是在任何函数体内部,this指代调用函数的那个对象
函数调用中的this
function f1() {
return this
}
console.log(f1() === window) // true
如上代码所示,直接定义一个函数f1,相当于为window对象定义了一个属性。直接执行函数f1(),相当于执行window.f1()。所以函数f1中的this指代调用函数的那个对象,也就是window对象。
function f2(){
"use strict"; // 这里是严格模式
return this;
}
console.log(f2() === undefined); // true
如上代码所示,在严格模式下,禁止this关键字指代全局对象,this的值将维持undefined状态。
对象方法中的this
var o = {
name: "stone",
f: function() {
return this.name;
}
};
console.log(o.f()); // "stone"
如上代码所示,对象o
中包含一个属性name
和一个方法f。当我们执行o.f()时,方法f中的this指代调用函数的那个对象,也就是对象o,所以this.name也就是o.name。
注意,在何处定义函数完全不会影响到this的行为,我们也可以首先定义函数,然后再将其附属到o.f。这样做this的行为也一致。
var fun = function() {
return this.name
}
var o = {name: 'mm'}
o.f = fun
console.log(o.f()) // mm
类似的,this的绑定只受最靠近的成员引用的影响,在下面的这个例子中,我们把一个方法 g() 当作对象 o.b 的函数调用。在这次执行期间,函数中的 this 将指向 o.b。事实上,这与对象本身的成员没有多大关系,最靠近的引用才是最重要的。
o.b = {
name: "mm"
g: fun,
};
console.log(o.b.g()); // "mm"
eval()方法中的this
eval()方法可以将字符串转换为JS代码,使用eval方法时,this的指向哪里呢?答案很简单,看谁在调用eval方法,调用者的执行环境中的this就被eval方法继承下来了
// 全局上下文
function f1() {
return eval('this')
}
console.log(f1() === window) //true
//函数上下文
var o = {
name: 'stone',
f: function() {
return eval('this.name')
}
}
call和apply方法中的this
call和apply是函数对象的方法,它的作用是改变函数的调用对象,它的第一个参数就表示改变后的调用这个函数的对象。因此,this指代的就是这两个方法的第一个参数。
var x = 0
function f() {
console.log(this.x)
}
var o = {}
o.x = 1
o.m = f
o.m.apply() // 0
call和apply的参数为空时,默认调用全局对象。因此,这时的运行结果为0,证明this指的是全局对象。如果把最后一行改为
o.m.apply(o) //1
运行结果就变成了1,证明了这时this指代的是对象o
bind方法中的this
ES5引入了Function.prototype.bind.调用f.bind(someObject)会创建一个与f具有相同函数体和作用域的函数,但是在这个新函数中,this将永久地被绑定到bind的第一个参数,无论这个函数是如何被调用的。
function f() {
return this.a
}
var g = f.bind({
a: 'stone'
})
console.log(g()) // stone
var o = {
a: 28,
f: f,
g: g
}
console.log(o.f(), o.g()) // 28, stone
DOM事件处理函数中的this
一般来讲,当函数使用addEventListener,被用作事件处理函数时,它的this指向触发事件的元素
test
但在ID浏览器中,当函数使用attachEvent,被当作事件处理函数时,它的this却指向window
test
内联事件处理函数中的this
当代码被内敛处理函数调用时,它的this指向监听器所在的DOM元素。
上面alert会显示button,注意只有外层代码中的this是这样设置的。如果this被包含在匿名函数中,则友事另一种情况了
在这种情况下,this被包含在匿名函数中,相当于处于全局上下文中,所以它指向window对象。
练习题
// 挑战一
function func1() {
function func2() {
console.log(this)
}
return func2;
}
func1()(); // ???
// 挑战二
scope = "stone";
function Func() {
var scope = "sophie";
function inner() {
console.log(scope);
}
return inner;
}
var ret = Func();
ret(); // ???
// 挑战三
scope = "stone";
function Func() {
var scope = "sophie";
function inner() {
console.log(scope);
}
scope = "tommy";
return inner;
}
var ret = Func();
ret(); // ???
// 挑战四
scope = "stone";
function Bar() {
console.log(scope);
}
function Func() {
var scope = "sophie";
return Bar;
}
var ret = Func();
ret(); // ???
// 挑战五
var name = "The Window";
var object = {
name: "My Object",
getNameFunc: function() {
return function() {
return this.name;
};
}
};
console.log(object.getNameFunc()()); // ???
// 挑战六
var name = "The Window";
var object = {
name: "My Object",
getNameFunc: function() {
var that = this;
return function() {
return that.name;
};
}
};
console.log(object.getNameFunc()()); // ???
简言之:
a.如果是一般函数,this指向全局对象window;
b.在严格模式下"use strict",为undefined.
c.对象的方法里调用,this指向调用该方法的对象.
d.构造函数里的this,指向创建出来的实例.
3.原型及原型链
原型链是一种机制,指的是JS每个对象都有一个内置的porto属性指向创建它的构造函数的prototype(原型)属性。原型链的作用是为了实现对象的继承。