JS深入-作用域和上下文栈

作用域

作用域确定了当前执行代码对变量的访问权限。

静态作用域

静态作用域在函数定义的时候就决定了,如果当前作用域没有,会查找上一层代码。而动态作用域是在运行的时候确定的,如果当前作用域没有,会查找调用函数的作用域。

js采用的是静态作用域。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var scope = "global scope";
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f();
}
checkscope();
var scope = "global scope";
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f;
}
checkscope()();

上面两段代码都会打印 local scope,因为函数的作用域基于函数创建的位置,js函数的执行用到了作用域链,这个作用域链是在函数定义的时候创建的。

上下文栈

js引擎并非一行一行的分析和执行,而是一段一段执行。当执行一段代码时,会进行变量提升、函数提升等准备操作。

js中可执行代码类型有 全局代码、函数代码、eval代码。

js维护一个执行上下文栈,最先入栈的是全局代码上下文,当执行一个函数时,就会创建一个执行上下文入栈,当函数执行完毕,就会出栈。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function foo() {

console.log('foo1');

}

foo(); // foo2

function foo() {

console.log('foo2');

}

foo(); // foo2

对于每个执行上下文都有三个重要属性: 变量对象、作用域链、this

JS深入-原型和class

原型

ES5中通过构造函数创建对象

Person就是一个构造函数,使用new创建一个对象实例。

1
2
3
4
5
6
7
function Person() {

}

var person = new Person();
person.name = 'gao';
console.log(person.name);

prototype

每个函数都有一个prototype属性,该属性指向了该函数的原型。每一个js对象在创建的时候都会关联一个原型,对象从原型继承属性和方法。

proto

每个js对象都有一个属性proto,该属性指向了其关联的原型。

1
2
3
person.__proto__ === Person.prototype === Object.getPrototypeOf(person)

构造函数通过prototype属性关联原型,而对象通过__proto__属性关联原型

该属性一般不直接使用,一般通过Object.getPrototypeOf(person)来获取。

constructor

原型通过constructor属性关联构造函数

1
Person === Person.prototype.constructor

原型链

如果实例中找不到该属性,就会查找该实例关联的原型中的属性,如果原型中找不到,则会查找原型的原型,直到找到Object构造函数的原型。

1
2
3
1. 查找person中属性
2. 查找Person.prototype中属性
3. 查找Object.prototype中属性

ES6中的class

ES6中通过class创建对象

class的类型就是函数,类本身就指向构造函数。类的所有方法都定义在prototype上面,在类的实例上调用方法,就是调用原型上的方法。

但是类内部定义的方法,是不能枚举的。

1
2
3
4
5
6
7
8
9
10
11
12
13
Person === Person.prototype.constructor

class Person {
constructor(name) {
this.name = name;
}

print() {
console.log(this.name)
}
}

new Person('gao zhen').print();

constructor方法默认返回实例对象this。

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×