JavaScript重难点解析3(原型与原型链、执行上下文与执行上下文栈)
JavaScript重难点解析3(原型与原型链、执行上下文与执行上下文栈)
- 原型与原型链
- 原型(prototype)
- 显示原型与隐式原型
- 原型链
- instanceof是如何判断
- 执行上下文与执行上下文栈
- 变量提升与函数提升
- 执行上下文
- 全局执行上下文
- 函数执行上下文
- 执行上下文栈
原型与原型链
原型(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,即显式原型(属性)
每个实例对象都有一个__proto__,可称为隐式原型(属性)
对象的隐式原型的值为其对应构造函数的显式原型的值
function Fn() { // 内部语句: this.prototype = {}
}
//创建实例对象
var fn = new Fn() // 内部语句: this.__proto__ = Fn.prototype
console.log(Fn.prototype===fn.__proto__) // trueFn.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) // trueconsole.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) //undefinedvar a = 4
}
fn()
根据变量提升和函数提升的概念以上程序可以翻译为:
var a
function fn () {var aconsole.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)
执行上下文栈如图:
- window入栈,由于变量提升,程序开始i被定义为undifined,所以第一个打印console.log打印gb:undifined。打印完下一行对i赋值1。
- 程序执行到第三行foo(1)函数入栈,形参i=1不为4,直接执行console.log(‘fb:’ + i)打印fb:1。
- foo(i + 1)处一个新的foo(2)函数入栈,形参i=2,重复2的操作打印fb:2。
- foo(i + 1)处一个新的foo(3)函数入栈,形参i=3,重复2的操作打印fb:3。
- foo(i + 1)处一个新的foo(4)函数入栈,形参i=4返回,foo(4)出栈。
- foo(3)继续执行,到console.log(‘fe:’ + i)打印fe:3,foo(3)执行结束出栈。
- foo(2)继续执行,到console.log(‘fe:’ + i)打印fe:2,foo(2)执行结束出栈。
- foo(1)继续执行,到console.log(‘fe:’ + i)打印fe:1,foo(1)执行结束出栈。
- window继续执行,此时i为1,console.log('ge: ’ + i)处打印ge:1。
JavaScript重难点解析3(原型与原型链、执行上下文与执行上下文栈)相关推荐
- JavaScript重难点解析5(对象高级、浏览器内核与事件循环模型(js异步机制))
JavaScript重难点解析5(对象高级.浏览器内核与事件循环模型(js异步机制) 对象高级 对象创建模式 Object构造函数模式 对象字面量模式 工厂模式 自定义构造函数模式 构造函数+原型的组 ...
- JavaScript重难点解析6(Promise)
JavaScript重难点解析6(Promise 概念 为什么要使用Promise Promise 的状态 Promise 对象的值 Promise工作流程 基本用法 Promise其他方法 asyn ...
- JavaScript重难点解析4(作用域与作用域链、闭包详解)
JavaScript重难点解析4(作用域与作用域链.闭包详解) 作用域与作用域链 作用域 作用域与执行上下文 作用域链 闭包 闭包理解 将函数作为另一个函数的返回值 将函数作为实参传递给另一个函数调用 ...
- JavaScript重难点解析2(立即执行函数IIFE,this关键字)
JavaScript重难点解析2(立即执行函数IIFE,this关键字) 立即执行函数 this关键字 立即执行函数 也叫做匿名函数自调用,可以在定义一段函数之后直接让其执行. ;(function ...
- JavaScript重难点解析1(数据类型——var、let、const区别,类型补充,“===”、“typeof”、“instanceof”区别,Symbol数据类型)
JavaScript重难点解析1(数据类型) var.let.const区别: 类型补充 "==="."typeof"."instanceof&quo ...
- 快速排序的难点_数据结构考研重难点解析:快速排序
数据结构是计算机专业考研重点内容,大部分院校都是考到了数据结构,其中快速排序是其中的重点难点内容,因此中公考研计算机教研室为大家整理的"数据结构考研重难点解析:快速排序",希望对大 ...
- 跨考408计算机学科专业基础综合,考研北京航空航天大学计算机学科专业基础综合(408)重难点解析.doc...
2010年考研北京航空航天大学计算机学科专业基础综合(408)重难点解析 跨考专业课特别奉献,为广大考研学子加油助力! 1.操作系统 今天我们来解析一下计算统考大纲操作系统部分的知识点.操作系统的研发 ...
- 计算机学科专业基础综合简称,2010年考研北京大学计算机学科专业基础综合(408)重难点解析...
2010年考研北京大学计算机学科专业基础综合(408)重难点解析 考研全程辅导专家 2010年考研北京大学计算机学科专业基础综合(408)重难点解析 跨考专业课特别奉献,为广大考研学子加油助力! 计算 ...
- 一次解析系统_消防稳压泵的流量、压力、选型以及配套气压罐的重难点解析
稳压泵 1.稳压泵宜采用离心泵,并宜符合下列规定: 1)宜采用单吸单级或单吸多级离心泵: 2)泵外壳和叶轮等主要部件的材质宜采用不锈钢. [重点.难点解析]稳压泵经常启动,为了延长它的使用寿命,防止频 ...
最新文章
- 转行数据分析师后悔了?脱颖而出才是关键!
- 腾讯副总裁姚星:腾讯AI Lab将致力打造通往AGI之路
- Ambari离线部署Hadoop集群踩到的坑
- 根据序列选择自回归模型(AR、ARMA、VAR、VMA、VECH)
- A1136 | 字符串处理、大整数运算
- Spring MVC PathVariable
- 34.Linux/Unix 系统编程手册(下) -- 进程组,会话和作业控制
- HIbernate学习笔记5 之 查询
- Cascade:自动化测试“旅程”
- 神州行省内流量套餐6元500M申请,发送BLSN6到10086即可
- 基于51单片机DTH11温湿度测量仪protues仿真设计_LCD显示
- 计算机网络mac地址作用是什么,路由器中MAC地址克隆的用途是什么
- AcWing - 寒假每日一题2023(DAY 11——DAY 15)
- 文件或目录损坏其无法读取问题解决方法2022-9-15
- arm服务器安装docker及docker-compose
- jar包是干什么用的
- Microsoft Excel 教程:如何在 Excel 单元格中设置文本格式?
- 数据库逻辑设计之 三大范式 及 反范式化 优缺点
- 洞泾镇科大智能机器人_【企业风采】G60科创走廊的标志性项目洞泾镇商会副会长企业科大智能总部园区,预计明年初全部落成...
- 踩坑篇,多达21页的,超级详细的Oracle安装和配置教程,没有之一