前端基础 | 理解原型-作用域-执行上下文

用自己的话记录下最近学到的几个知识点:理解原型-作用域-执行上下文

用自己的话记录下最近学到的几个知识点

一、原型与原型链

基础代码:

1
2
3
4
5
6
7
// 使用构造函数创建一个对象
function Person() {}

Person.prototype.name = 'Brand'

let person = new Person()
console.log(person.name) // Brand

prototype

每个函数都有一个 prototype 属性。

prototype 是函数才会有的属性。

函数的 prototype 属性是指通过调用构造函数而创建的对象实例的原型,即上面代码中person 的原型。

proto

每一个JavaScript对象(除了 null )都具有的一个属性,叫__proto__,这个属性会指向该对象的原型。

1
2
3
function Person() {}
var person = new Person();
console.log(person.__proto__ === Person.prototype); // true

⚠️注意:__proto__ 存在于 实例与构造函数的原型之间,而不是存在于实例与构造函数之间

可通过Object.getPrototypeOf()获取对象的原型

1
Object.getPrototypeOf(person) === Person.prototype // true

constructor

constructor 是原型的一个属性,指向该原型所对应的构造函数,所有对象都会自动获得这个属性

1
Person.prototype.constructor === Person // true

读取实例的属性时,如果在实例中找不到,就会去找原型上的该属性,如果还找不到,会继续去原型的原型上查找,直到找到最顶层为止.

原型链

由相互关联的原型组成的链状结构就是原型链,即通过 __proto__ 把原型连起来的链状结构
例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function Person() {}

Person.prototype.name = 'Hello';

var person = new Person();

person.name = 'Hi';
console.log(person.name)
// 输出:Hi
// 因为在 person 上能找到 name 属性,值为 Hi

delete person.name; // 删除 person 的 name 属性
console.log(person.name)
// 此时,在 person 上找不到 name 属性
// 所以去 person 的原型上查找,即 person.__proto__ 上查找 name 属性
// 又因为 person.__proto__ === Person.prototype
// 所以 person.__proto__.name === Person.prototype.name
// 输出:Hello

Object.prototype 是最顶层原型,它的原型是 null,即

1
Obkect.prototype.__proto__ === null

图中红线所组成的连接就叫原型链

prototype.png

二、词法作用域和动态作用域

JavaScript 采用的是词法作用域,或者叫静态作用域

因为 js是采用的词法作用域,所以函数的作用域在函数定义的时候就决定了

但在动态作用域中,函数的作用域在函数调用的时候决定

1
2
3
4
5
6
7
8
9
10
11
12
var value = 1;

function foo() {
console.log(value);
}

function bar() {
var value = 2;
foo();
}

bar(); // 1

上述代码:

  • 如果 js 是动态作用域,则执行 bar() 后,应输出 2
  • 如果 js 是词法作用域,则执行 bar() 后,应输出 1

在浏览器中执行上面代码可以知道输出结果为 1。验证了 js 是词法作用域。

再看两个例子:

1
2
3
4
5
6
7
8
9
var scope = "global scope";
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f();
}
checkscope();
1
2
3
4
5
6
7
8
9
var scope = "global scope";
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f;
}
checkscope()();

输出结果都为:local scope

三、执行上下文

执行上下文就是当前 JavaScript 代码被解析和执行时所在环境抽象概念, JavaScript 中运行任何的代码都是在执行上下文中运行。

总共有三种类型:

  • 全局执行上下文: 只有一个,浏览器中的全局对象就是 window 对象,this 指向这个全局对象。
  • 函数执行上下文: 存在无数个,只有在函数被调用的时候才会被创建,每次调用函数都会创建一个新的执行上下文。
  • Eval 函数执行上下文: 指的是运行在 eval 函数中的代码,很少用而且不建议使用。

执行栈,也被叫做调用栈,具有 LIFO(后进先出)结构

JavaScript 引擎首次读取你的脚本时,它会创建一个全局执行上下文并将其推入当前的执行栈。

执行栈的底部始终有个全局执行上下文。

每发生一个函数调用就会创建一个执行上下文,并推进执行栈中,执行完后,从栈顶删除。

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let a = 'Hello World!';

function first() {
console.log('Inside first function');
second();
console.log('Again inside first function');
}

function second() {
console.log('Inside second function');
}

first();
console.log('Inside Global Execution Context');

ExecutionContext.png

js执行过程,可以看看我之前总结的这一篇文章:
http://www.brandhuang.com/article/1576067877012

执行上下文相关推荐文章:

执行上下文中文翻译

执行上下文英文原版

大佬理解总结版

先就记录这几个知识点吧,多了一次性也记不住,大概率你也不会来看第二遍