1. 程式人生 > >javascript之原型與原型鏈、執行上下文與執行上下文棧

javascript之原型與原型鏈、執行上下文與執行上下文棧

## 原型與原型鏈
* 所有函式都有一個特別的屬性:
  * `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, 否則返回false
2. 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定義(宣告)的變數, 在定義語句之前就可以訪問到
  * 值: undefined
2. 函式宣告提升
  * 通過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: 1
2. 整個過程中產生了幾個執行上下文?  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>

結果:
這裡寫圖片描述

相關推薦

JavaScript繼承(原型

call() 的人 spa 環境 isp 而且 依賴 .html console   我們知道繼承是oo語言中不可缺少的一部分,對於JavaScript也是如此。一般的繼承有兩種方式:其一,接口繼承,只繼承方法的簽名;其二,實現繼承,繼承實際的方法。JavaScript不支

區塊鼻祖比特幣9:挖礦礦池比特幣的產生

挖礦       前面我們已經提到過比特幣如何通過數字簽名和交易鏈轉移,交易順序又是如何受到區塊鏈保護。那麼你可能就會有疑問,想付款,你必須參照到先前的支付,那麼比特幣又是怎麼產生的呢?一個緩慢,隨機並讓比特幣流通的方式是解開區塊者會得到比特幣作為獎賞,這既是為什麼解開區塊被

JavaScript 資料結構演算法美 - 記憶體堆記憶體 淺拷貝深拷貝

前言 想寫好前端,先練好內功。 棧記憶體與堆記憶體 、淺拷貝與深拷貝,可以說是前端程式設計師的內功,要知其然,知其所以然。 筆者寫的 JavaScript 資料結構與演算法之美 系列用的語言是 JavaScript ,旨在入門資料結構與演算法和方便以後複習。 棧 定義 後進者先出,先進者後出,簡

JavaScript路(十四)HTML5 中class屬性相關的擴充

mov html5 表示 方法 popu dom add data- token 1. getElementByClassName() :支持getElementsByClassName()方法的瀏覽器有IE 9+、Firefox 3+、Safari

統計分析引數檢驗非引數檢驗匹配樣本獨立樣本2樣本K樣本介紹----附SPSS操作指南

       最近幾天博主需要做一些計算生物學分析,重新溫習了一遍統計學的知識。由於博主此次使用的是非引數檢驗,將重點介紹非引數檢驗相關內容,仍然是深入淺出的風格,先放一些概念,再總結實際使用的技巧。寫在這裡,供大家參考學習。    

總結:JavaScript非同步事件迴圈訊息佇列微任務巨集任務

本人正在努力學習前端,內容僅供參考。由於各種原因(不喜歡部落格園的UI),大家可以移步我的github閱讀體驗更佳:傳送門,喜歡就點個star咯,或者我的部落格:https://blog.tangzhengwei.me 掘金:傳送門,segmentfault:傳送門 前言 Phili

(轉)總結:JavaScript非同步事件迴圈訊息佇列微任務巨集任務

前言 Philip Roberts 在演講 great talk at JSConf on the event loop 中說:要是用一句話來形容 JavaScript,我可能會這樣: “JavaScript 是單執行緒、非同步、非阻塞、解釋型指令碼語言。”

Java路:截尾舍入提升

截尾和舍入 在執行窄化轉換時,必須注意截尾與舍入的問題。例如:如果將一個浮點數轉換為整型值,Java會如何處理呢?如果將29.7轉換為int,結果是30還是29? public class Cast { public static void main(String[] args)

Spring 學習筆記(六)AOP 思想概念和作用JDK代理Cglib子類代理

概念 AOP為Aspect Oriented Programming的縮寫,意味:面向切面程式設計。 與IOC一樣,是一種思想。 作用 對業務邏輯進行分離,降低耦合度,提高程式的重用性,提高開發效率。 JDK動態代理(介面代理) 弱點:JDK動態代理

JavaScript ------ 函式(一般函式動態函式匿名函式)

函式 一、一般函式 1、格式: function  函式名(形式引數...) { 執行語句; return 返回值; } 函式是多條執行語句的封裝體,只有被呼叫才會被執行。 注意:呼叫有引數的函式,但沒有給其傳值,函式一樣可以執行,或者呼叫沒有引數的函式,給其傳值,該函

匿名具名函式函式宣告函式表示式立即執行函式(已完結)

匿名與具名函式 這屬於常識性問題,但是還是有必要說說 沒有函式名的函式就叫匿名函式,有函式名的函式就叫具名函式 setInterval(function(){//匿名函式 ... },1000); va

.NET中那些所謂的新語法二:匿名類匿名方法擴充套件方法

開篇:在上一篇中,我們瞭解了自動屬性、隱式型別、自動初始化器等所謂的新語法,這一篇我們繼續征程,看看匿名類、匿名方法以及常用的擴充套件方法。雖然,都是很常見的東西,但是未必我們都明白其中蘊含的奧妙。所以,跟著本篇的步伐,繼續來圍觀。 /* 新語法索引 */ 一、匿名類:[ C# 3.0/.NET 3.

執行緒基礎學習十二:概念瞭解-重入鎖獨佔鎖共享鎖公平鎖非公平鎖

前面瞭解了J.U.C中兩個重要的類或介面,其它的實現類都是以此為基礎的,現在還需要了解一下一些鎖的概念,有助於後面學習一些實現類或介面。 重入鎖 在併發中,無論是synchronized還是lock也好,內部都有重入的特性,而且特性的含義是一樣的。 重入(

JavaScript閉包的實現閉包中的this物件

閉包 函式物件可以通過作用域鏈關聯起來,函式體內的變數可以儲存在作用域中,這種特性稱“閉包”。 要理解閉包,首先要理解巢狀函式的詞法作用域規則:先看下列一段程式碼:var a = "Tom"; //全域性變數 function curr () { var a =

編譯型解釋型動態語言靜態語言強類型語言弱類型語言的區別

動態語言 動態 java語言 不同 編譯型 效率 編譯過程 .exe 檢查 (一)編譯型語言和解釋型語言   首先我們區分這樣一個概念,編譯型語言和解釋型語言。我們編程用的都是高級型語言(寫匯編和機器語言的除外),計算機只理解和運行機器語言,所以必須把高級語言翻譯成機器語言

6C_宏定義預處理函數函數庫

a10 使用 不可 find 字符串比較 pos cde 文件包含 mnt C語言預處理理論 由源碼到可執行程序的過程 源碼.c->(編譯)->elf可執行程序 源碼.c->(編譯)->目標文件.o->(鏈接)->elf可執行程

大數據區塊的聯系區別

基於 class 保護 特定 關系 科研 指針 .com 基礎   進入大數據時代,雲計算成為大數據基礎設施,也使得大數據的核心思想和雲計算一脈相承。大數據和區塊鏈兩者之間有個共同的關鍵詞:分布式,代表了一種從技術權威壟斷到去中心化的轉變。   (去中心化:在一個分布

聯合概率聯合分佈條件概率條件分佈邊緣概率邊緣分佈貝葉斯定理生成模型(Generative Model)和判別模型(Discriminative Model)的區別

在看生成模型和判別模型之前,我們必須先了解聯合概率與聯合分佈、條件概率與條件分佈、邊緣概率與邊緣分佈、貝葉斯定理的概念。 聯合概率與聯合概率分佈: 假設有隨機變數X與Y, 此時,P(X=a,Y=b)用於表示X=a且Y=b的概率。這類包含多個條件且所有條件同時成立的概率稱為聯合概率。聯合概

字元型別char字串字元陣列字元陣列資料陣列區別

字元型別是以ASCII碼值運算的:小寫字母比相應的大寫字母大32,其中A=65,a=97 Esc鍵 27(十進位制)、'\x1B'(十六進位制)、'\33'(八進位制) 轉義字元:\0 空字元\n 換行\r 回車\t 水平製表符(Tab鍵)\' 單引號\" 雙引號\\ 反斜槓 char c='M';

RIB表FIB表ARP表FDB表

1.RIB與FIB的區別: RIB:路由表 FIB:轉發資訊表 FIB表更多是出現在需要快速轉發的路由器上,這種路由器上的路由表條目通常都達成千上萬條,如果按照傳統的檢索路由表進行轉發的方式,其轉發效率很低,FIB表作為路由表的一種精簡形式出現,通常只記錄常用的表項。當需要選