javascript之原型与原型链、执行上下文与执行上下文栈
来源:互联网 发布:淘宝五折换购 编辑:程序博客网 时间:2024/06/05 11:31
## 原型与原型链* 所有函数都有一个特别的属性: * `prototype` : 显式原型属性* 所有实例对象都有一个特别的属性: * `__proto__` : 隐式原型属性* 显式原型与隐式原型的关系 * 函数的prototype: 定义函数时被自动赋值, 值默认为{}, 即用为原型对象 * 实例对象的__proto__: 在创建实例对象时被自动添加, 并赋值为构造函数的prototype值 * 原型对象即为当前实例对象的父对象* 原型链 * 所有的实例对象都有__proto__属性, 它指向的就是原型对象 * 这样通过__proto__属性就形成了一个链的结构---->原型链 * 当查找对象内部的属性/方法时, js引擎自动沿着这个原型链查找 * 当给对象属性赋值时不会使用原型链, 而只是在当前对象中进行操作## 执行上下文与执行上下文栈* 变量提升与函数提升 * 变量提升: 在变量定义语句之前, 就可以访问到这个变量(undefined) * 函数提升: 在函数定义语句之前, 就执行该函数 * 先有变量提升, 再有函数提升* 理解 * 执行上下文: 由js引擎自动创建的对象, 包含对应作用域中的所有变量属性 * 执行上下文栈: 用来管理产生的多个执行上下文* 分类: * 全局: window * 函数: 对程序员来说是透明的* 生命周期 * 全局 : 准备执行全局代码前产生, 当页面刷新/关闭页面时死亡 * 函数 : 调用函数时产生, 函数执行完时死亡* 包含哪些属性: * 全局 : * 用var定义的全局变量 ==>undefined * 使用function声明的函数 ===>function * this ===>window * 函数 * 用var定义的局部变量 ==>undefined * 使用function声明的函数 ===>function * this ===> 调用函数的对象, 如果没有指定就是window * 形参变量 ===>对应实参值 * arguments ===>实参列表的伪数组* 执行上下文创建和初始化的过程 * 全局: * 在全局代码执行前最先创建一个全局执行上下文(window) * 收集一些全局变量, 并初始化 * 将这些变量设置为window的属性 * 函数: * 在调用函数时, 在执行函数体之前先创建一个函数执行上下文 * 收集一些局部变量, 并初始化 * 将这些变量设置为执行上下文的属性
======================
原型:
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>01_原型(prototype)</title></head><body><!--1. 函数的prototype属性(图) * 每个函数都有一个prototype属性, 它默认指向一个Object空对象(即称为: 原型对象) * 原型对象中有一个属性constructor, 它指向函数对象2. 给原型对象添加属性(一般都是方法) * 作用: 函数的所有实例对象自动拥有原型中的属性(方法)--><script type="text/javascript"> // 每个函数都有一个prototype属性, 它默认指向一个Object空对象(即称为: 原型对象) console.log(Date.prototype, typeof Date.prototype) function Fun () {//alt + shift +r(重命名rename) } console.log(Fun.prototype) // 默认指向一个Object空对象(没有我们的属性) // 原型对象中有一个属性constructor, 它指向函数对象 console.log(Date.prototype.constructor===Date) console.log(Fun.prototype.constructor===Fun) //给原型对象添加属性(一般是方法) ===>实例对象可以访问 Fun.prototype.test = function () { console.log('test()') } var fun = new Fun() fun.test()</script></body></html>
结果:
显示原型与隐式原型:
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>02_显式原型与隐式原型</title></head><body><!--1. 每个函数function都有一个prototype,即显式原型(属性)2. 每个实例对象都有一个__proto__,可称为隐式原型(属性)3. 对象的隐式原型的值为其对应构造函数的显式原型的值4. 内存结构(图)5. 总结: * 函数的prototype属性: 在定义函数时自动添加的, 默认值是一个空Object对象 * 对象的__proto__属性: 创建对象时自动添加的, 默认值为构造函数的prototype属性值 * 程序员能直接操作显式原型, 但不能直接操作隐式原型(ES6之前)--><script type="text/javascript"> //定义构造函数 function Fn() { // 内部语句: this.prototype = {} } // 1. 每个函数function都有一个prototype,即显式原型属性, 默认指向一个空的Object对象 console.log(Fn.prototype) // 2. 每个实例对象都有一个__proto__,可称为隐式原型 //创建实例对象 var fn = new Fn() // 内部语句: this.__proto__ = Fn.prototype console.log(fn.__proto__) // 3. 对象的隐式原型的值为其对应构造函数的显式原型的值 console.log(Fn.prototype===fn.__proto__) // true //给原型添加方法 Fn.prototype.test = function () { console.log('test()') } //通过实例调用原型的方法 fn.test()</script></body></html>
结果:
显式原型与隐式原型分析:
原型链:
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>03_原型链</title></head><body><!--1. 原型链(图解) * 访问一个对象的属性时, * 先在自身属性中查找,找到返回 * 如果没有, 再沿着__proto__这条链向上查找, 找到返回 * 如果最终没找到, 返回undefined * 别名: 隐式原型链 * 作用: 查找对象的属性(方法)2. 构造函数/原型/实体对象的关系(图解)3. 构造函数/原型/实体对象的关系2(图解)--><script type="text/javascript"> // console.log(Object) //console.log(Object.prototype) console.log(Object.prototype.__proto__) function Fn() { this.test1 = function () { console.log('test1()') } } console.log(Fn.prototype) Fn.prototype.test2 = function () { console.log('test2()') } var fn = new Fn() fn.test1() fn.test2() console.log(fn.toString()) console.log(fn.test3) // fn.test3() /* 1. 函数的显示原型指向的对象默认是空Object实例对象(但Object不满足) */ console.log(Fn.prototype instanceof Object) // true console.log(Object.prototype instanceof Object) // false console.log(Function.prototype instanceof Object) // true /* 2. 所有函数都是Function的实例(包含Function) */ console.log(Function.__proto__===Function.prototype) /* 3. Object的原型对象是原型链尽头 */ console.log(Object.prototype.__proto__) // null</script></body></html>
结果:
原型链分析:
构造函数、原型、实例对象的关系:
var o1 = new Object();
var o2 = {};
图解:
function Foo(){ }
图解:
原型链属性问题:
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>04_原型链_属性问题</title></head><body><!--1. 读取对象的属性值时: 会自动到原型链中查找2. 设置对象的属性值时: 不会查找原型链, 如果当前对象中没有此属性, 直接添加此属性并设置其值3. 方法一般定义在原型中, 属性一般通过构造函数定义在对象本身上--><script type="text/javascript"> function Fn() { } Fn.prototype.a = 'xxx' var fn1 = new Fn() console.log(fn1.a, fn1) var fn2 = new Fn() fn2.a = 'yyy' console.log(fn1.a, fn2.a, fn2) function Person(name, age) { this.name = name this.age = age } Person.prototype.setName = function (name) { this.name = name } var p1 = new Person('Tom', 12) p1.setName('Bob') console.log(p1) var p2 = new Person('Jack', 12) p2.setName('Cat') console.log(p2) console.log(p1.__proto__===p2.__proto__) // true</script></body></html>
结果:
探索instanceof:
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>05_探索instanceof</title></head><body><!--1. instanceof是如何判断的? * 表达式: A instanceof B --A是实例对象,含有隐式原型属性,,沿着隐式原型链最终到达object原型对象; --B是构造函数,含有显示原型属性,默认指向一个空的Object实例对象。 * 如果B函数的显式原型对象在A对象的原型链上, 返回true, 否则返回false2. Function是通过new自己产生的实例--><script type="text/javascript"> /* 案例1 */ function Foo() { } var f1 = new Foo() console.log(f1 instanceof Foo) // true console.log(f1 instanceof Object) // true /* 案例2 */ console.log(Object instanceof Function) // true console.log(Object instanceof Object) // true console.log(Function instanceof Function) // true --Function是new自己产生的 console.log(Function instanceof Object) // true function Foo() {} console.log(Object instanceof Foo) // false</script></body></html>
结果:
图解:
案例一:
案例二:
======================
面试题:
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>06_面试题</title></head><body><script type="text/javascript"> /* 测试题1 */ function A () { } A.prototype.n = 1 var b = new A() //b可以看到n属性,是因为new A对象之后有一个隐式原型属性 A.prototype = { //A的原型属性指向一个新的对象 n: 2, m: 3 } var c = new A() console.log(b.n, b.m, c.n, c.m) /* 测试题2 */ function F (){} Object.prototype.a = function(){ console.log('a()') } Function.prototype.b = function(){ console.log('b()') } var f = new F() f.a() // f.b() F.a() F.b() console.log(f) console.log(Object.prototype) console.log(Function.prototype) console.log(fn33()) function fn33(){}</script></body></html>
结果:
==========================
执行上下文与执行上下文栈:
变量提升与函数升:
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>01_变量提升与函数提升</title></head><body><!--1. 变量声明提升 * 通过var定义(声明)的变量, 在定义语句之前就可以访问到 * 值: undefined2. 函数声明提升 * 通过function声明的函数, 在之前就可以直接调用 * 值: 函数定义(对象)3. 问题: 变量提升和函数提升是如何产生的?--><script type="text/javascript"> console.log('-----') /* 面试题 : 输出 undefined */ var a = 3 function fn () { console.log(a) var a = 4 } fn() console.log(b) //undefined 变量提升 fn2() //可调用 函数提升 // fn3() //不能 变量提升 var b = 3 function fn2() { console.log('fn2()') } var fn3 = function () { console.log('fn3()') }</script></body></html>
结果:
执行上下文:
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>02_执行上下文</title></head><body><!--1. 代码分类(位置) * 全局代码 * 函数(局部)代码2. 全局执行上下文 * 在执行全局代码前将window确定为全局执行上下文 * 对全局数据进行预处理 * var定义的全局变量==>undefined, 添加为window的属性 * function声明的全局函数==>赋值(fun), 添加为window的方法 * this==>赋值(window) * 开始执行全局代码3. 函数执行上下文 * 在调用函数, 准备执行函数体之前, 创建对应的函数执行上下文对象(虚拟的, 存在于栈中) * 对局部数据进行预处理 * 形参变量==>赋值(实参)==>添加为执行上下文的属性 * arguments==>赋值(实参列表), 添加为执行上下文的属性 * var定义的局部变量==>undefined, 添加为执行上下文的属性 * function声明的函数 ==>赋值(fun), 添加为执行上下文的方法 * this==>赋值(调用函数的对象) * 开始执行函数体代码--><script type="text/javascript"> console.log(a1, window.a1) window.a2() console.log(this) var a1 = 3 function a2() { console.log('a2()') } console.log(a1)</script></body></html>
结果:
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>03_执行上下文栈</title></head><body><!--1. 在全局代码执行前, JS引擎就会创建一个栈来存储管理所有的执行上下文对象2. 在全局执行上下文(window)确定后, 将其添加到栈中(压栈) --栈:后进先出,进出口不一样;队列:先进先出,进口出口不一样3. 在函数执行上下文创建后, 将其添加到栈中(压栈)4. 在当前函数执行完后,将栈顶的对象移除(出栈)5. 当所有的代码执行完后, 栈中只剩下window--><script type="text/javascript"> var a = 10 var bar = function (x) { var b = 5 foo(x + b) } var foo = function (y) { var c = 5 console.log(a + c + y) } bar(10) // bar(10)</script></body></html>
结果:
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>04_执行上下文栈2</title></head><body><!--1. 依次输出什么? gb: undefined fb: 1 fb: 2 fb: 3 fe: 3 fe: 2 fe: 1 ge: 12. 整个过程中产生了几个执行上下文? 5--><script type="text/javascript"> 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)</script></body></html>
结果:
流程分析:
图一:
图二:
图三:
面试题:
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>05_面试题</title> <link rel="stylesheet" href="xxx.css"> <style> </style></head><body><div style=""></div><script type="text/javascript"> // 测试题1: 先执行变量提升, 再执行函数提升 function a() { } var a console.log(typeof a) // 'function' //测试题2: if (!(b in window)) { var b = 1 } console.log(b) // undefined //测试题3: var c = 1 function c(c) { console.log(c) var c = 3 } c(2) // 报错</script></body></html>
结果:
阅读全文
0 0
- javascript之原型与原型链、执行上下文与执行上下文栈
- JavaScript核心:对象 原型链 构造函数 执行上下文栈 执行上下文 变量对象 活动对象 作用域链 闭包 This 总结
- JavaScript执行上下文,执行上下文栈
- javascript------探索instanceof执行原理(原型与原型链)
- 作用域链(执行上下文) 原型链(对象)
- 理解js作用域原型链和执行上下文
- 深入理解javascript原型和闭包(11)——执行上下文栈
- 深入理解javascript原型和闭包(11)——执行上下文栈
- 深入理解javascript原型和闭包(11)——执行上下文栈
- 深入理解javascript原型和闭包(11)——执行上下文栈
- 深入理解javascript原型和闭包(11)——执行上下文栈
- 深入理解javascript原型和闭包(11)——执行上下文栈
- 深入理解javascript原型和闭包(11)——执行上下文栈
- 深入理解javascript原型和闭包(11)——执行上下文栈
- 深入理解javascript原型和闭包(11)——执行上下文栈
- 深入理解javascript原型和闭包(11)——执行上下文栈
- 深入理解javascript原型和闭包(11)——执行上下文栈
- 深入理解javascript原型和闭包(11)——执行上下文栈
- 底层jdbc查询操作
- C++项目总四之内存溢出造成的诡异函数调用
- 图论作业10.9
- 单客户端与服务器通信
- smoj2075:匹配数(状压dp/广义容斥原理)
- javascript之原型与原型链、执行上下文与执行上下文栈
- 我的JS经典题目合集
- Jupyter Notebook 27绝技
- [NOIP模拟][好题]分玩具
- Hololens开发之项目一: Holograms 100
- HDU 2059 龟兔赛跑(线性DP)
- 软件架构模式
- 个性化推荐的十大挑战
- Android studio中打包apk出现"XXX" is translated here but not found in default locale的解决问题的方法