1.原型和原型链
先上一段代码
function Person(){}
const person = new Person()
Person.prototype.name = "pub"
console.log(person.__proto__ === Person.prototype)
console.log(Person.prototype.constructor === Person)
console.log(Object.getPrototypeOf(person) === Person.prototype)
console.log(person.name)
上面的代码中通过new关键字创建了一个构造函数Persond的实例对象.然后打印了一些内容,其中出现的关键字有prototype
, __proto__
, constructor
1.1 prototype
、 __proto__
、constructor
每一个函数都会有一个属性 prototype
,而这个 prototype 就是原型对象。普通的对象是没有prototype
的。
每一个对象都会有一个[[Prototype]]
内置属性。它的作用就是指向该对象的关联对象.Object.getPrototypeOf()
静态方法返回指定对象的原型(即内部 [[Prototype]]
属性的值)。所以Object.getPrototypeOf(person) === Person.prototype
为true
而当我们访问对象的__proto__
时,就会从这个内置属性中去查找。而这个__proto__
就是其构造函数的原型对象prototype
。
所以在代码中的打印 person.__proto__ === Person.prototype
为true。
每个对象都会有个constructor
,它始终指向的是该对象的构造函数
。所以代码中的Person.prototype.constructor === Person
是true,person.__proto__.constructor === Person
也是true
1.2 原型链(链式查找)
当需要查找对象上的属性时,如果没有该属性,则会从原型对象上去查找,如果原型对象上没有,那么就继续去原型对象的原型对象上去查找。直到查找到null才结束。这样就构成了一个原型链
所以上面的代码中console.log(person.name)
就是pub
2.作用域
2.1 什么是作用域?
作用域:代码中的程序定义变量的区域。作用域中定义了如何找到对应变量,在执行代码中(运行在作用域中),获取变量的访问权限。
2.2 词法作用域
作用域又分为词法作用域
(静态作用域)和动态作用域
- 词法(静态)作用域:作用域在定义时就确定的
- 动态作用域:作用域是在调用时决定的
const name = 'pub'
function foo() {
console.log(name)
}
function bar(){
const name = '张三'
foo()
}
bar()
JS是词法作用域,在定义的时候就确定了。所以当执行到foo的时候,先从当前函数体内查找name,没有在继续查找父级查找变量,所以打印结果是: pub
3.变量提升
4.执行上下文
JS中可执行代码分为三种:全局代码
、函数代码
、eval代码
。
而当程序执行到三种代码的时候,都会生成一个执行上下文。并且会通过一个栈
的方式去管理,又叫执行上下文栈
function fun3() {
console.log('fun3')
}
function fun2() {
fun3();
}
function fun1() {
fun2();
}
fun1();
上面的代码用图的方式表示出对应的执行上下文栈如下:
执行完之后再出栈
执行上下文 有三个重要的属性:
- 变量对象(VO)
- 作用域链
- this
4.1.变量对象
就是在执行上下文中变量或函数的声明。 在不同的上下文中,VO是什么
- 全局上下文中的变量对象(variable object 简称VO)
// 全局上下文中
console.log(this) // window
- 函数上下文中变量对象又被叫做活动对象:activiation object (简称AO)。因为函数中的变量对象只有在被调用的时候才会被激活。
function foo(a) {
var b = 2
function c(){}
var d = function(){}
b=3
}
foo(1)
上面的代码在执行上下文的分析阶段
它的AO,这个阶段就执行了变量的初始化、变量提升
AO = {
arguments: {
0: 1,
length: 1
},
a: 1,
b: undefined,
c: reference to function c(){},
d: undefined
}
在执行阶段
它的AO如下
AO = {
arguments: {
0: 1,
length: 1
},
a: 1,
b: 2,
c: reference to function c(){},
d: reference to FunctionExpression d
}
面试题1:
function foo(){
console.log(a)
a = 1
}
foo()
上面的代码会报错:a is not defined。首先会生成一个foo的执行上下文,执行打印,当前上下文没有a,去全局VO中查找a仍然没找到,所以会报错
面试题2:
function foo(){
console.log(a)
var a = 1
}
foo()
打印 undefined
4.2.作用域链
先从当前上下文中找,找不到,到词法作用域的上一级找,直到找到全局上下文中的变量对象为止
4.3 this
ECMAScript 的类型分为语言类型和规范类型: Reference 规范类型中的一种。 Reference 构成: 1. base value 2. referenced name 3. strict reference
它是JavaScript 引擎在访问变量或属性时的底层机制
var foo = 1
// 上面的foo定义时,转化为规范类型就是这种形式
fooReference = {
base: 'EnvironmentRecord',
name: 'foo',// 属性名称
strict: false,// 是否严格模式
}
var foo = {
bar: function(){
return this
}
}
foo.bar()
barReference = {
base: 'foo',
name: 'bar',
strict: false
}
5.闭包
能够访问自由变量的函数。
自由变量:在函数中使用的,但是不是函数的参数,也不是函数内部的局部变量的这些变量
闭包 = 函数 + 能访问自由变量
var a = 'hello'
function foo(){
console.log(a)
}
foo()
分析一下上面的代码,foo 中访问了a,但是a既不是函数内部定义的变量,也不是函数的参数,所以a就是一个自由变量,理论上讲这也是一个闭包。
var scope = "global scope";
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f;
}
var foo = checkscope();
foo();