作用域
作用域基本可以分为三种:全局作用域,函数作用域,块作用域
下面我们一个一个的进行理解,按等级划分来理解。
全局作用域
全局作用域就是在你写的代码里,哪里都可以访问,哪里都可以使用。
a是在全局作用域的变量,所以不管是在函数内部或者任何一个位置,都可以对他进行引用、更改。但b不是全局作用域变量,只能在特定的位置被访问,也就有了局限性。而fun()
函数也是在全局作用域中的,所以他在人和地方也可以被访问。
我们把全局作用域等级划分为1,等级最低,任何人都可以对它进行操作。
只要没被函数包裹,等级都是1.
函数作用域
函数作用域顾名思义,也就是只能在函数代码块中起作用的。
我们把函数作用域等级划分为2,等级比1高,可以对1进行操作,但1却不可以
对它操作。但函数作用域自己本身也会有等级的高低之分,比如函数的嵌套,越嵌
套等级越高。
为什么等级2是从fun里面开始划分的?
因为fun()
函数是在全局作用域中的,里面的代码才是在函数作用域的。
fun2()
是在fun()
的作用域中,fun2()
的作用域要比fun()
等级高,等级高的作用域可以使用等级低的作用域里的变量、函数,反过来不行。
块作用域
块作用域使用let 和const
命令进行声明,所声明的变量在指定块的作用域外无法被访问。
什么是块作用域?它只能在函数内部或花括号内部被访问。
块作用域好像和函数作用域没有什么区别,但块作用域多了个代码块(花括号)内部。函数作用域仅仅指函数内部,块作用域=函数作用域+if/else/switch/for…这种的花括号。
let
声明的变量不会被变量提升,就算我在fun2()
中重新声明了b=2
,但输出还是5 。如果使用var
定义b
,在fun2()
中会把b=2
提升到最前面,首先执行,输出为2 。
我把带有let/const
声明的代码块也设为一个等级。
作用域链
拿我说的等级来说,作用域链就是等级链,等级高的在上层,全局作用域是最下层。
当我们想要访问一个变量,首先在他所在的等级(作用域)中寻找,如果没有,就找他的父层作用域,即向下寻找等级低的看看有没有,直到最下层。
闭包
闭包就是能够读取其他函数内部变量的函数。
inner()
函数就是一个闭包,因为它要访问变量a
,但在他的作用域中没有找到a
,它的等级是3,去等级为2的作用域中找,找到了变量a
,而a
是不属于它的函数的内部变量,属于outer()
函数,所以它读取了其他函数内部的变量。
原型链
原型
- 每个对象都有_proto_属性,并且指向它的原型对象
- 每个构造函数都有它的prototype原型对象
- prototype原型对象里的constructor指向它的构造函数
- new一个构造函数会形成它的实例对象
原型对象就是一个公共容器,把一些常用的属性和方法储存起来,而构造函数的实例会继承原型对象中的所有属性和方法。
function Cat(name){
this.name = name;
}
Cat.sex = '女';
Cat.prototype.age = '12';
var cat = new Cat('大黄');
alert(cat.age);//12
alert(cat.sex);//undefine 也就是说明实例只能继承构造函数原型上的属性和方法
原型链
当我们查找特定属性的时候,我们先去这个对象里去找,如果没有的话就去它的原型对象里面去,如果还是没有的话再去原型对象的原型对象里去寻找… 这个操作就是被委托在整个原型链上。
参考:https://blog.csdn.net/zhang5476499/article/details/90273226
话说原型这里总让我想起之前学java的时候,讲的多态,感觉有点相似。
不过JavaScript也是比较简单的轻量的java,有兴趣的可以再去看看:
java多态