深入理解JavaScript之2
文章目录
1.闭包函数
//闭包函数
function foo(){
var a=2
function bar(){
console.log(a)
}
return bar
}
var baz=foo()
baz()//2
/*对于闭包函数,由于闭包具有foo内部所有访问的作用域,因此,在调用foo()函数时,foo内的变量本该被
释放回收,但是由于内部闭包函数的存在,导致依然存在,从而在baz()调用时,变量a并没有被销毁!!
此外,对于闭包函数,由于其控制着其所在函数的作用域,只有所在函数执行完毕,得到最终的结果,并保存不销毁
之后才会开始执行闭包函数的调用,等闭包函数调用结束,才会释放内存!!
闭包函数的缺点:内部函数可以引用外部函数的参数和变量,参数和变量不会被垃圾回收机制收回
*/
for(let i=1;i<=5;++i){
setTimeout(function timer(){
console.log(i)
},i*1000)
}
//执行机制为:
{
let i
for(i=1;i<=5;++i){
setTimeout(function timer(){
console.log(i)
},i*1000)
}
}//由于闭包函数的特性,每次会引用外部函数的变量,从而使得外部函数变量不能够被释放
//这里使用let声明,依次调用set1,set2...set5,每次调用都会拿到相应的内部let i值,所以输出结果为
/*
1s 1
2s 2
3s 3
4s 4
5s 5
*/
//对比另一种写法:
for(var i=1;i<=5;++i){
setTimeout(function timer(){
console.log(i)
},i*1000)
}
var i
{
for(i=1;i<=5;++i){
setTimeout(function timer(){
console.log(i)
},i*1000)
}
}//这里使用var进行变量声明,由于在内部块区域没有i,所以闭包函数必须要等到外部函数执行结束之后才能
//拿到最终的变量i值,即6
/*
输出结果为:
1s 6
2s 6
3s 6
4s 6
5s 6
*/
//虽然JavaScript的闭包函数会导致外部函数的参数和变量不能够被自动回收,常驻于内存中,但是就是因为这
//种特性,才能够满足JavaScript的事件的监听的运行,如果函数一旦执行结束就将内存释放,那么事件监听
//也就失去了意义!!!
2.JavaScript的模块
/*
即函数的模块化,通过将闭包函数返回的方式,实现外部调用闭包函数API,访问模块函数内部数据!!
所以模块函数必须具备以下条件:
1、必须被调用一次,创建一个模块实例
2、模块函数必须至少返回一个闭包函数API,通过实例调用闭包函数访问修改内部数据!
3.如果只需要一个实例,可以改进为单例模式
*/
//多例模式,可以多次执行创建实例
function CoolModule(){
var something="cool"
var another=[1,2,3]
function doSomething(){
console.log(something)
}
function doAnother(){
console.log(another.join("!"))
}
return {
doSomething:doSomething,
doAnother:doAnother
}
}
var foo=CoolModule()
foo.doSomething()//cool
foo.doAnother()//1!2!3
//单例模式
var foo=(function CoolModule(){
var something="cool"
var another=[1,2,3]
function doSomething(){
console.log(something)
}
function doAnother(){
console.log(another.join("!"))
}
return {
doSomething:doSomething,
doAnother:doAnother
}
})()
//ES6的模块机制
import export
3.注意词法作用域和动态作用域的区别
function foo(){
console.log(a)
}
function bar(){
var a=3
foo()
}
var a=2
bar()
//由于词法作用域的作用,将输出2而非3,因为词法作用域是从代码声明的作用域往上查找而不是在调用处进行
//查找,如果是动态作用域,则是从调用处开始查找,将输出3
//词法作用域关注的是函数在何处被声明,而动态作用域关注的是函数何处被调用,this也是动态作用域!!
4.函数对象的call及apply方法
//每一个函数对象都有一个call及apply方法,如果使用call及apply对函数进行调用,则当前this对象都变为所指定的对象!!
var some_obj={
name:'Ninja',
say:function(who){
return 'Haya ' + who + ', I am a '+ this.name;
}
}
var my_obj={name: 'Scripting guru'}
some_obj.say('Dude')//this指向some_obj对象
some_obj.say.call(my_obj,'Dude')//this指向my_obj对象
some_obj.say.apply(my_obj,['Dude'])
/*
Haya Dude, I am a Ninja
Haya Dude, I am a Scripting guru
Haya Dude, I am a Scripting guru
*/
5.this引用和词法引用
/*
this引用取决于调用的位置而非声明的位置,词法引用则取决于声明的位置而非调用的位置
this的绑定规则:
1.默认绑定在当前调用环境对象!在严格模式下,与调用位置无关将默认绑定在undefined
2.显示绑定call及apply方法
3.使用箭头函数,绑定在函数对象名,且无法被修改!
*/
function baz(){//调用位置为全局作用域,this默认绑定在window对象
console.log(this.a)
bar()
}
function bar(){//调用位置为baz
console.log("bar")
foo()
}
function foo(){//调用位置为bar
console.log("foo")
}
var a=1
baz()
6.JavaScript的数据类型
- 基本数据类型有string number boolean null undefined object字面量
- object对象子类型:String Number Boolean Object Function Array Date RegExp Error对象
注:在JavaScript,字面量和对象会自动进行转换!对象具有属性和方法,字面量没有,千万注意别混淆
7.JavaScript访问对象的属性值的方法
- 通过.操作符进行属性值访问
- ["…"]通过属性标识符字符串的形式进行访问
var myObject={
a:2
}
myObject.a//2
myObject["a"]//2 更加灵活
index="a"
myObject[index]//2
//如果使用string字面量以外的其他值作为属性名,首先会转换为一个字符串,数字也不例外
myObject[true]="true_2"
myObject[3]="fff_3"
//相当于
myObject.true="true_2"
myObject.3="fff_3"//但是这种是非法的
var prefix="foo"
var myObject={
[prefix+"bar"]:"hello",
[prefix+"baz"]:"world"
}//通过计算属性进行对象属性的定义
//区别数组对象
var myArray=["foo",42,"bar"]
myArray[0]//foo
myArray["3"]=4
myArray[3]//4