函数声明与函数表达式的区别:
函数声明提升:就是使用函数声明时,函数会被解析器先读取,以供执行环境使用,所以函数声明的位置可以在函数调用的后面
函数表达式则不可以在函数调用的前面,必须先定义函数表达式,才可以调用,不然浏览器会报错。
函数表达式:
var sayHi = function(name){
console.log(name+",hi");
}
sayHi("john")
函数表达式创建的是一个匿名函数。然后将匿名函数赋值给一个变量,通过变量来调用。
(function(name){
console.log(name+",hi");
})("john")
(function(name){
console.log(name+",hi");
}("john"))
这两种执行方式效果是相同的,是立即执行的意思,定义完之后直接执行,而不需要通过一个变量+()来调用。
闭包:
按照高程里面的描述就是:一个函数可以访问另外一个函数作用域里面的变量。
function outerSum(a,b){
var sum = function(){
return a+b
}
return sum;
}
var result = outerSum(1,2)
result() // 3
函数调用时发生的事情:
首先会创建一个执行环境和相应的作用域链,然后使用arguments和其他命名参数的值来初始化函数的活动对象。然后外部函数的活动对象始终是第二个。
这是最简单的闭包,sum内部函数可以获取outerSum外部函数的活动变量。当outerSum执行完毕之后,它的执行环境被销毁了,但是它的活动对象依然在内存中,没有被销毁,因为匿名函数依然在引用着这些变量。直到匿名函数被销毁,它的活动对象才会被销毁。
闭包的缺点:
function a() {
var arr = []
for(var i = 0;i < 10; i++){
arr[i] = function() {
console.log(i)
}
}
return arr
}
var b = a()
b[0]() //10
b[1]() //10
b[2]() //10
闭包只能取得包含函数中任何变量的最后一个值。比如这个例子。程序中先执行a(),这个函数,但是b()并没有被调用。直到执行到return arr这条语句,a()已经执行完成,然后接下来继续往下执行,就是b[0]()这个方法,但此时i=10,所以打印出来的都是10。可以把匿名函数设置为立即执行函数,这样就可以解决这个问题了。
function a() {
var arr = []
for(var i = 0;i < 10; i++){
arr[i] = function(num) {
return function(){
console.log(num)
}
}(i)
}
return arr
}
var b = a()
b[0]() //0
b[1]() //1
b[2]() //2
关于this对象
var name = 'The window'
var object = {
name: "My Object",
getNameFunc: function(){
return function(){
return this.name
}
}
}
console.log(object.getNameFunc()()) //The Window
这里相当于:
var a = object.getNameFunc()
console.log(a())
这里的a()是在全局作用域下调用的,并且不是dot调用,所以this就指向window。只要函数中指定this的值就可以了。
var name = 'The window'
var object = {
name: "My Object",
getNameFunc: function(){
var that = this
return function(){
return that.name
}
}
}
console.log(object.getNameFunc()()) // My Object
var name = 'The window'
var object = {
name: "My Object",
getNameFunc: function(){
return function(){
return this.name
}.bind(object)()
}
}
console.log(object.getNameFunc())
console.log(object.getNameFunc().call(object))
console.log(object.getNameFunc().apply(object))
只要修改this的值是指向object就可以返回object里面的值。
模仿块级作用域:
(function () {
//块级作用域
}) ()
这是一个立即执行函数,模仿块级作用域,其他作用域无法访问这个作用域。
function() {
} () // 错误的写法
这是个函数声明,函数声明后面加括号是错误的做法,函数声明后面不能加圆括号。只有函数表达式才可以。我们只要给函数声明用圆括号括起来,那么就可以转换为函数表达式。就像上面的形式那样。
优点:模仿块级作用域可以减少闭包占用的内存问题,因为没有指向匿名函数的引用,只要函数执行完毕,就可以立即销毁其作用域链。
模块模式:
为单例创建私有变量和特权方法。所谓的单例,指的就是只有一个实例的对象。
var singleton = function() {
//私有属性和方法
var privateVariable = 10;
function privateFunction(){
return privateVariable;
}
return {
//公有方法和属性
publicVariable: true,
publicMethods:function() {
privateVariable++;
return privateFunction()
}
}
}();
如果必须创建一个对象并以某些数据对其进行初始化,同时还要公开一些能够访问这些私有数据的方法,那么就可以使用模块模式。