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
原创粉丝点击