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 檢查 (一)編譯型語言和解釋型語言 首先我們區分這樣一個概念,編譯型語言和解釋型語言。我們編程用的都是高級型語言(寫匯編和機器語言的除外),計算機只理解和運行機器語言,所以必須把高級語言翻譯成機器語言
6、C_宏定義與預處理、函數與函數庫
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表作為路由表的一種精簡形式出現,通常只記錄常用的表項。當需要選