1、作用域
作用域就是变量、函数生效的区域
一般将作用域分为:
全局作用域
函数作用域
块级作用域
1、全局作用域
任何不在函数或者花括号里面声明的变量,都是全局变量
var a = 1;
let b = 2;
const c = 3;
console.log(a);
console.log(b);
console.log(c);
{
console.log(a);
console.log(b);
console.log(c);
}
(function haha() {
console.log(a);
console.log(b);
console.log(c);
})()
输出:1,2,3,1,2,3,1,2,3
2、函数作用域
如果说一个变量在函数内部声明,那么就不能在函数外部去访问他。
(function haha() {
var a = 1;
let b = 2;
const c = 3;
})()
console.log(a);
console.log(b);
console.log(c);
输出:Uncaught ReferenceError: a is not defined
3、块级作用域
在花括号中使用let
和const
声明的变量存在于块级作用域中。外部无法访问到。由{}
包裹起来。var
声明的变量是个例外。
{
var a = 1;
let b = 2;
const c = 3;
}
console.log(a);
console.log(b);
console.log(c);
输出1 Uncaught ReferenceError: b is not defined
2、语法作用域
变量被创建时就确定好了,而非执行阶段确定的。也就是说我们写好代码时它的作用域就确定了。
变量会被初始化为undefined(var声明的情况下)和保持uninitialized(未初始化状态)(使用let和const声明的情况下)
正是因为有了语法作用域,因此才会导致下面的错误
var a = 1;
function hah() {
console.log(a)
}
hah()
输出:1
但是如果改为如下语句,就会报错。
var a = 1;
function hah() {
console.log(a)
let a;
}
hah()
输出:Cannot access 'a' before initialization
原因:就是因为存在语法作用域,使得,在变量创建的时候,重新指定了a的作用域,又因为属于语句在创建语句之前,因此才会报错。
3、原型
每个对象都有一个原型对象
当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾
function hah() { }
console.log(hah.prototype)
其中的constructor,这个属性指向该函数。
4、原型链
原型对象也会拥有原型,从中继承方法以及属性。这就是原型链。
对象实例以及他的构造器之间也建立了一个连接(__proto__)属性,是从构造函数的prototype属性中派生出来的。
function Person(name) {
this.name = name;
}
let zhangsan = new Person('张三');
console.log(zhangsan.__proto__)
console.log(zhangsan.__proto__ === Person.prototype)
function Person(name) {
this.name = name;
}
let zhangsan = new Person('张三');
console.log(zhangsan.__proto__)
console.log(zhangsan.__proto__ === Person.prototype)//true
console.log(Person.prototype.__proto__ === Object.prototype)//true
console.log(Object.prototype.__proto__ === null)//true
- 一切对象都是继承自Object对象,Object对象直接继承根源对象null
注意:数组的遍历方法for...in...
,会遍历整个原型链。
function Person(name) {
this.name = name;
}
let zhangsan = new Person('张三');
Object.prototype.age = 12
for (item in zhangsan) {
console.log(item)
}
输出:name、age