JavaScript重难点解析3(原型与原型链、执行上下文与执行上下文栈)

原型与原型链

原型(prototype)

每个函数都有一个prototype属性, 它默认指向一个Object空对象(即称为: 原型对象)
原型对象中有一个属性constructor, 它指向函数对象

function A() {
 }
 console.log(A.prototype) // 打印一个空对象
 console.log(A.prototype.constructor===A) //true

给原型添加的方法可以直接调用

function Aaa() {
}
Aaa.prototype.test = function () {
  console.log(' Aaa')
}
var aaa = new  Aaa()
 aaa.test() //打印Aaa

prototype和constructor的关系

显示原型与隐式原型

每个构造函数都有一个prototype,即显式原型(属性)
每个实例对象都有一个__proto__,可称为隐式原型(属性)
对象的隐式原型的值为其对应构造函数的显式原型的值

function Fn() {   // 内部语句: this.prototype = {}
}
//创建实例对象
var fn = new Fn()  // 内部语句: this.__proto__ = Fn.prototype
console.log(Fn.prototype===fn.__proto__) // true
  Fn.prototype.test = function () {
  console.log('test()')
}
  fn.test()

内存结构图
显示原型与隐式原型

原型链

在访问一个对象的属性时,首先在自身属性中查找,如果没有找到, 再沿着__proto__这条链向上查找, 如果还是没有, 则返回undefined。

function Fn() {
}
console.log(Fn.prototype)
Fn.prototype.test = function () {
  console.log('test')
}

var fn = new Fn()

fn.test() //调用自己的test1
fn.toString()  //调用Object上的toString方法

原型链
这里对该结构做一个简单介绍,在所有代码执行之前JS引擎会自动创建Object原型对象,并自动生成Object构造方法(相当于定义Function Object() {…}),Object构造方法的prototype属性指向Object原型对象,还会生成函数构造方法(相当于定义Function Function() {…})和一个函数原型对象(也是一个Object对象)Function()的prototype和__proto__都指向函数原型对象,当我们代码执行到 function Fn() 时,堆中会创建一个Function的实例对象,Function对象的prototype指向函数原型对象。当执行到 Fn.prototype.test= function () {…}时,在空object对象上创建了test方法,当调用fn.test时,fn会在自身属性中查找,发现没有然后沿着__proto__查找,找到后执行。
最后做几点特殊说明:

  • 函数的显示原型指向的对象默认是空Object实例对象(但Object不满足)
  • 所有函数都是Function的实例(包含Function)
  • Object的原型对象是原型链尽头

instanceof是如何判断

表达式: A instanceof B
如果B函数的显式原型对象在A对象的原型链上, 返回true, 否则返回false
简单来说,看上例中画的图,如果A沿着__proto__走可以走到B的prototype上,那么就是true,不然就是false。
大家可以按照上例中的图对这几个例子画一画:

function Foo() {  }
var f1 = new Foo()
console.log(f1 instanceof Foo) // true
console.log(f1 instanceof Object) // true
  console.log(Object instanceof Function) // true
console.log(Object instanceof Object) // true
console.log(Function instanceof Function) // true
console.log(Function instanceof Object) // true
console.log(Object instanceof  Foo) // false

原型链是JS的一大难点,不好理解,如果大家看完感觉不解的化可以根据自己的代码画图看看。多画几遍比多听要强很多。

执行上下文与执行上下文栈

变量提升与函数提升

变量声明提升

  • 通过var定义(声明)的变量, 在定义语句之前就可以访问到
  • 值: undefined

函数声明提升

  • 通过function声明的函数, 在之前就可以直接调用
  • 值: 函数定义(对象)
var a = 3
function fn () {
  console.log(a) //undefined
  var a = 4
}
fn()

根据变量提升和函数提升的概念以上程序可以翻译为:

var a
function fn () {
 	var a
	console.log(a) 
	a = 4
}
var a = 3
fn()

所以a为undifined。
在这里可以说明一个两种函数定义方法的区别:

fn2() //可调用  函数提升
fn3() //不能  变量提升

function fn2() {
 console.log('fn2()')
}

var fn3 = function () {
 console.log('fn3()')
}

执行上下文

全局执行上下文

在执行全局代码前将window确定为全局执行上下文
对全局数据进行预处理

  • var定义的全局变量==>undefined, 添加为window的属性
  • function声明的全局函数==>赋值(fun), 添加为window的方法
  • this==>赋值(window)
  • 开始执行全局代码

函数执行上下文

在调用函数, 准备执行函数体之前, 创建对应的函数执行上下文对象(虚拟的, 存在于栈中)

  • 形参变量==>赋值(实参)==>添加为执行上下文的属性
  • arguments==>赋值(实参列表), 添加为执行上下文的属性
  • var定义的局部变量==>undefined, 添加为执行上下文的属性
  • function声明的函数 ==>赋值(fun), 添加为执行上下文的方法
  • this==>赋值(调用函数的对象)
  • 开始执行函数体代码

执行上下文栈

在全局代码执行前, JS引擎就会创建一个栈来存储管理所有的执行上下文对象
在全局执行上下文(window)确定后, 将其添加到栈中(压栈)
在函数执行上下文创建后, 将其添加到栈中(压栈)
在当前函数执行完后,将栈顶的对象移除(出栈)
当所有的代码执行完后, 栈中只剩下window

以一道面试题为例:

console.log('gb: '+ i)
var i = 1
foo(1)
function foo(i) {
 if (i == 4) {
   return
 }
 console.log('fb:' + i)
 foo(i + 1) //递归调用: 在函数内部调用自己
 console.log('fe:' + i)
}
console.log('ge: ' + i)

执行上下文栈如图:
执行上下文栈

  1. window入栈,由于变量提升,程序开始i被定义为undifined,所以第一个打印console.log打印gb:undifined。打印完下一行对i赋值1。
  2. 程序执行到第三行foo(1)函数入栈,形参i=1不为4,直接执行console.log(‘fb:’ + i)打印fb:1。
  3. foo(i + 1)处一个新的foo(2)函数入栈,形参i=2,重复2的操作打印fb:2。
  4. foo(i + 1)处一个新的foo(3)函数入栈,形参i=3,重复2的操作打印fb:3。
  5. foo(i + 1)处一个新的foo(4)函数入栈,形参i=4返回,foo(4)出栈。
  6. foo(3)继续执行,到console.log(‘fe:’ + i)打印fe:3,foo(3)执行结束出栈。
  7. foo(2)继续执行,到console.log(‘fe:’ + i)打印fe:2,foo(2)执行结束出栈。
  8. foo(1)继续执行,到console.log(‘fe:’ + i)打印fe:1,foo(1)执行结束出栈。
  9. window继续执行,此时i为1,console.log('ge: ’ + i)处打印ge:1。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值