1.作用域链
JavaScript 在执⾏过程中会创建一个个的可执⾏上下⽂。 (每个函数执行都会创建这么一个可执行上下文)
每个可执⾏上下⽂的词法环境中包含了对外部词法环境的引⽤,可通过该引⽤来获取外部词法环境中的变量和声明等。
这些引⽤串联起来,⼀直指向全局的词法环境,形成一个链式结构,被称为作⽤域链。
简而言之: 函数内部 可以访问到 函数外部作用域的变量, 而外部函数还可以访问到全局作用域的变量,
这样的变量作用域访问的链式结构, 被称之为作用域链
let num = 1 function fn () { let a = 100 function inner () { console.log(a) console.log(num) } inner() } fn()
下图为由多个可执行上下文组成的调用栈:
- 栈最底部为
全局可执行上下文
全局可执行上下文
之上有多个函数可执行上下文
- 每个可执行上下文中包含了指向外部其他可执行上下文的引用,直到
全局可执行上下文
时它指向null
js全局有全局可执行上下文, 每个函数调用时, 有着函数的可执行上下文, 会入js调用栈
每个可执行上下文, 都有者对于外部上下文词法作用域的引用, 外部上下文也有着对于再外部的上下文词法作用域的引用
=> 就形成了作用域链
2.原型链
要讲清楚这个问题,主要着重这几个方面:
- 什么是原型对象
- 构造函数, 原型对象, 实例的三角关系图
- 原型链如何形成
原型对象
在 JavaScript 中,除去一部分内建函数,绝大多数的函数都会包含有一个叫做
prototype
的属性,指向原型对象,基于构造函数创建出来的实例, 都可以共享访问原型对象的属性。
例如我们的
hasOwnProperty
,toString
⽅法等其实是 Obejct 原型对象的方法,它可以被任何对象当做⾃⼰的⽅法来使⽤。
hasOwnProperty
用于判断, 某个属性, 是不是自己的 (还是原型链上的)
来看一段代码:
let person = { name: "Tom", age: 18, job: "student" } console.log(person.hasOwnProperty("name")) // true console.log(person.hasOwnProperty("hasOwnProperty")) // false console.log(Object.prototype.hasOwnProperty("hasOwnProperty")) // true
可以看到,hasOwnProperty
并不是 person
对象的属性,但是 person
却能调用它。
那么 person
对象是如何找到 Object 原型中的 hasOwnProperty
的呢?这就要靠原型链的能力了。
原型链
在 JavaScript 中,每个对象中都有一个 __proto__ 属性,这个属性指向了当前对象的构造函数的原型。
对象可以通过自身的 __proto__属性与它的构造函数的原型对象连接起来,
而因为它的原型对象也有 __proto__,因此这样就串联形成一个链式结构,也就是我们称为的原型链。