1. 函数中的作用域
js的每一个函数都会产生独有的作用域气泡,作用域内部的函数定义或者变量只能在其内部能够访问
函数作用域的含义是指,属于这个函数的全部变量都可以在整个函数的范围内使用及复用(事实上在嵌套的作用域中也可以使用)。这种设计方案是非常有用的,能够充分利用JavaScript变量可以根据需要改变值类型的“动态特性”
详细见解: https://www.jianshu.com/p/da213392de21
js动态特性:在对象创建出来之后为对象添加新的属性或者方法
- js支持在对象定义之后,动态的 添加 / 修改 / 删除 / 查询
- 添加:如果当前的属性不存在,那么就是添加
- 修改:如果当前访问的属性存在,那么在设置的时候就是修改。
- 删除:使用关键字 delete 语法:delete 对象.属性
2. 隐藏内部实现
- 函数先声明,在向里面添加代码。
- 从代码块里面选择出一些片段,用函数声明进行包装,实际上实现了代码的“隐藏”
(实际上所产生的影响是:在这个代码块周围创建了一个作用域气泡,也就是说这段代码中的任何声明都将绑定在这个新创建的包装函数的作用域中,而不是先前所在的作用域中,换句话说,可以把变量和函数包裹在一个函数的作用域中,然后用这个作用域来隐藏它们)
详解:https://blog.csdn.net/qq_36936155/article/details/77017834
最小授权或最小暴露原则:在软件设计中最小限度的暴露必要内容,而将其他内容隐藏起来。
如果一个函数仅为另外一个函数所调用,则应该封装到另外一个函数内部,避免被超过范围的调用,封装之后,功能性和最终效果都不会受到影响,但是设计上将具体内容私有化了,符合软件设计良好的规范
规避冲突
隐藏作用域中的变量和函数所带来的另一个好处就是可以避免同名标识符之间的冲突,命名冲突或者变量值被意外覆盖
全局命名空间
第三方库通常会在全局作用域中声明一个名字足够独特的变量,所有需要暴露给外界的功能都会成为这个对象(命名空间)的属性,而不是将自己的标识符暴露在顶级的词法作用域中。
模块管理
另外的避免冲突的办法和现代的模块机制非常的相似,从众多模块管理器中挑选一个来使用,使用这些工具,任何库都无需将标识符加入到全局变量作用域中,而是通过依赖管理器的机制将库的标识符显示的导入到另外一个特定的作用域中
3. 函数作用域
在任意代码片段外部添加包装函数,可以将内部的变量和函数定义“隐藏起来”,外部作用域无法访问包装函数内部的任何内容
但其实,包装函数本身也污染了所在的作用域,必须通过函数名的显式调用才能执行函数
如果函数不需要函数名,并且能够自动运行就好了
(function foo(){
var a = 3
console.log(a) //3
})()
console.log(a) //undefined
//foo函数被当成函数表达式直接被执行,不在被当成标准的函数声明
//区分:如果function是声明中的第一个词,就是一个函数声明,否则就是一个表达式
匿名和具名
匿名函数表达式
setTimeout(function(){
console.log("I waited a second!")
}, 1000)
//function()没有名称标识符,函数表达式可以是匿名的,但函数声明则不可以省略函数名
//优点:简单快捷
//缺点:不利于栈追踪和调试;当函数需要引用自身只能通过arguments.callee引用;不利于代码可读性
立即执行函数表达式
IIFE(Immediately Invoked Function Expression)
var a = 2
(function foo(){
var a = 3
console.log(a) //3
})()
console.log(a) //2
//第一个()将函数变成表达式,第二个()执行了这个函数
//优点:使用了具名函数,但拥有匿名函数的所有优点
//另外一种形式是:(function foo(){...}()),两种形式在功能上是一致的
//传入参数的进阶用法
var a = 2
(function IIFE(global){
var a = 3
console.log(a) //3
console.log(global.a) //2
})(window)
console.log(a) //2
(function IIFE(def){
def(window)
})(function def(global){
var a = 3
console.log(a)
console.log(global.a)
})
4. 块作用域
for(var i = 0; i < 10; i++){
console.log(i)
}
//如果这个for循环被放在全局作用域中,那么i这个变量也将会影响到全局作用域,成为一个全局变量
var foo = true
if(foo){
var bar = foo * 2
bar = something(bar)
console.log(bar)
}
//bar仅在if内部使用,但是使用var声明时,bar属于外部作用域变量
//块作用域是一个用来对之前的最小授权原则进行扩展的工具,将代码从函数的隐藏信息扩展为在块中的隐藏信息
//用法可能会造成混乱
with
用with从对象中创建的作用域仅在with声明中而非外部作用域中有效
try/catch
js的ES3规范中规定catch会创建一个块作用域,声明的变量仅在catch中有效
try{
undefined()
}
catch(err){
console.log(err)
}
console.log(err) //ReferenceError: err is not found
let
ES6引入,除了var以外的另一种变量声明方式
let所声明的变量隐式的存在于所在的块作用域
var foo = true
if(foo){
let bar = foo * 2
bar = something(bar)
console.log(bar)
}
console.log(bar) //ReferenceError
//任何地方使用{...}来为let创建一个用于绑定的块
//关于提升问题
//提升是指声明会被视为存在于其所出现的作用域的整个范围内,但是用let进行的声明不会在作用域范围内进行提升
{
console.log(bar) //ReferenceError
let bar = 2
}
垃圾回收机制
块作用域可以让引擎清楚的知道哪些变量不在需要被保留,为变量显示声明块作用域有利于优化
let循环
for(let i = 0; i < 10; i++){
console.log(i)
}
console.log(i) //ReferenceError
//for循环中let将i重新绑定到了循环的每一个迭代中,确保使用上一个循环迭代结束时的值重新进行赋值
//let声明并不属于当前的函数作用域,也不是全局作用域,而是一个新的作用域
//当代码中存在对函数作用域中var声明的隐式依赖时,就会有很多隐藏的陷阱
const
ES6引入const, 同样可以用来创建块作用域变量,但其值是固定的常量,之后任何试图的修改都会引发错误。
var foo = true
if(foo){
var a = 2
const b = 3 //包含在if中的块作用域常量
a = 3 //正常
b = 4 //错误
}
console.log(a) //3
console.log(b) //ReferenceError
- 写博客的理由是希望获得坚持学习的勇气,希望能一直坚持下去
- 能为其他小伙伴提供便利
- 如果文章涉及侵权或者其他,请评论或者留言指出删除此文章
- 文章中出现的一些见解是我自己认为重要的部分,欢迎讨论交流,互相努力
- 有前端的小伙伴有一起学习的愿望,可以添加qq2454459210