原型和原型链
原型
原型概念:每个函数都有一个prototype属性,它默认指向一个object空对象,这个对象就称为原型对象,也就是原型。
一般我们会给原型对象添加属性和方法,这样函数的所有实例对象自动拥有原型中的属性,例如:
function demo () {}
demo.prototype.test = function() {
console.log('test')
}
// 实例对象可以访问
let f = new demo();
f.test();
原型对象中有一个属性constructor,它指向函数对象本身
原型有分类,分为显示原型(prototype)和隐式原型__proto__
显示原型prototype:每个函数function都有一个prototype属性,他就是显示原型属性,函数的prototype属性是在定义函数时自动添加的,默认值是一个空Object对象
隐式原型__proto__:每个实例对象都有一个__proto__,他就是隐式原型(属性)」,对象的__proto__属性是在创建对象时自动添加的,默认值是构造函数的prototype属性,对象隐式原型的值为其对应的构造函数的显示原型的值,共同指向原型对象
原型链
原型链概念:它是实现继承的主要方法,其基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法,刚才提到每个构造函数都有一个prototype属性,指向原型对象,原型对象都包含一个指向构造函数的指针constructor
它的作用就是用来查找对象的属性(方法),看描述不太好理解,举个例子:
如果我们要访问一个对象的属性,它的查找顺序是什么?
- 先在自身的属性中查找,找到则返回
- 如果自身属性中没有,再沿着__proto__这条链向上查找,同样是找到则返回
- 如果最终找到原型链顶端都没找到,则返回undefined
执行上下文和执行上下文栈
执行上下文
当代码运行时,会产生一个对应的执行环境,在这个环境中,所有变量会被事先提出来(变量提升),有的直接赋值,有的为默认值 undefined,代码从上往下开始执行,就叫做执行上下文。
执行上下文可分为为:全局执行上下文 和 函数执行上下文。
在执行全局代码前,window 为全局执行上下文
在调用函数准备执行函数之前,创建对应的函数执行上下文
对全局数据进通常进行如下预处理,处理完后开始执行全局代码。
- var定义的全局变量==> undefined,添加为window的属性
- function 声明的全局函数==>赋值(fun),添加为window的方法
- this ==>赋值(window)
执行上下文栈
执行上线文栈也叫调用栈,具有 LIFO(后进先出)结构,用于存储在代码执行期间创建的所有执行上下文
代码执行过程中执行栈的变化过程如下:
- 在全局代码执行之前,JS引擎会创建一个栈来存储管理虽有的执行上下文对象
- 在全局执行上下文(window)确定后,将其添加到栈中(压栈)
- 在函数执行上下文创建后,将其添加到栈中(压栈)
- 在当前函数执行完后,将栈顶的对象移除(出栈)
- 当所有的代码执行完后,栈中只剩下window
作用域和作用域链
作用域
作用域就是代码的执行环境,全局执行环境就是全局作用域,函数的执行环境就是私有作用域,它们都是栈内存。它是静态的(相对于上下文对象), 在编写代码时就确定了。
注意里面的几个关键词:区域、静态的、在编写代码时就确定了。 主要分为 :全局作用域、函数作用域、ES6之后的块级作用域
它的主要作用是 :隔离变量,不同作用域下同名变量不会有冲突。
变量查找,现在自己作用域所在的执行上下文中查找,如果没有在一层层的向外层作用域的执行上下文中查找!
那作用域与执行上下文的区别什么?
主要分为两个方面:
首先,作用域是静态的, 只要函数定义好了就一直存在, 且不会再变化。执行上下文环境是动态的, 调用函数时创建, 函数调用结束时上下文环境就会被自动释放。
作用域链
当代码在一个环境中执行时,会创建变量对象的一个作用域链(即作用域形成的链条),它的方向是从下向上的(从内到外),查找变量时就是沿着作用域链来查找的,内部环境可以通过作用域链访问所有外部环境,但外部环境不能访问内部环境的任何变量和函数。
闭包
一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时, 就产生了闭包
var func = function(){
var a = 'linxin';
var func1 = function(){
a += ' a';
console.log(a);
}
return func1;
}
var func2 = func();
func2(); // linxin a
func2(); // linxin a a
func2(); // linxin a a a