本文主要跟大家分享JS原型鏈詳解,函數(Function)才有prototype屬性,物件(除Object)擁有__proto__。希望能幫助大家。
#所謂原型鏈,指的就是圖中的proto 這條指標鏈!
原型鏈的頂層就是Object.prototype,而這個物件的是沒有原型物件的。
可在chrome的控制台裡面輸入:
#輸出是:
<br/>
原型鏈,如此而已。
原型是一個對象,其他物件可以透過它實現屬性繼承。
一、prototype與__proto__的差異 <br/>
<br/>
var a = {};
console.log(a.prototype); //undefined
console.log(a.__proto__); //Object {}
var b = function(){}
console.log(b.prototype); //b {}
console.log(b.__proto__); //function() {} 登入後複製
<br/>
/*1、字面量方式*/
var a = {};
console.log(a.__proto__); //Object {}
console.log(a.__proto__ === a.constructor.prototype); //true
/*2、构造器方式*/
var A = function(){};
var a = new A();
console.log(a.__proto__); //A {}
console.log(a.__proto__ === a.constructor.prototype); //true
/*3、Object.create()方式*/
var a1 = {a:1}
var a2 = Object.create(a1);
console.log(a2.__proto__); //Object {a: 1}
console.log(a.__proto__ === a.constructor.prototype); //false(此处即为图1中的例外情况) 登入後複製
<br/>
var A = function(){};
var a = new A();
console.log(a.__proto__); //A {}(即构造器function A 的原型对象)
console.log(a.__proto__.__proto__); //Object {}(即构造器function Object 的原型对象)
console.log(a.__proto__.__proto__.__proto__); //null 登入後複製
我在寫一篇圖解prototype和__proto__的區別時,搜資料搜到了一個有意思的現象,下面這兩個運算返回的結果是一樣的:
<br/>
Function instanceof Object;//true
Object instanceof Function;//true 登入後複製
這個是怎麼一回事呢?要從運算子instanceof說起。
一、instanceof究竟是運算什麼的?
我曾经简单理解instanceof只是检测一个对象是否是另个对象new出来的实例(例如var a = new Object(),a instanceof Object返回true),但实际instanceof的运算规则上比这个更复杂。
首先w3c上有官方解释(传送门,有兴趣的同学可以去看看),但是一如既往地让人无法一目了然地看懂……
知乎上有同学把这个解释翻译成人能读懂的语言(传送门),看起来似乎明白一些了:
<br/>
//假设instanceof运算符左边是L,右边是R
L instanceof R
//instanceof运算时,通过判断L的原型链上是否存在R.prototype
L.__proto__.__proto__ ..... === R.prototype ?
//如果存在返回true 否则返回false 登入後複製
注意:instanceof运算时会递归查找L的原型链,即L.__proto__.__proto__.__proto__.__proto__...直到找到了或者找到顶层为止。
所以一句话理解instanceof的运算规则为:
instanceof检测左侧的__proto__原型链上,是否存在右侧的prototype原型。
二、图解构造器Function和Object的关系
<br/>
<br/>
我们再配合代码来看一下就明白了: <br/>
//①构造器Function的构造器是它自身
Function.constructor=== Function;//true
//②构造器Object的构造器是Function(由此可知所有构造器的constructor都指向Function)
Object.constructor === Function;//true
//③构造器Function的__proto__是一个特殊的匿名函数function() {}
console.log(Function.__proto__);//function() {}
//④这个特殊的匿名函数的__proto__指向Object的prototype原型。
Function.__proto__.__proto__ === Object.prototype//true
//⑤Object的__proto__指向Function的prototype,也就是上面③中所述的特殊匿名函数
Object.__proto__ === Function.prototype;//true
Function.prototype === Function.__proto__;//true 登入後複製
三、当构造器Object和Function遇到instanceof
我们回过头来看第一部分那个“奇怪的现象”,从上面那个图中我们可以看到:
<br/>
Function.__proto__.__proto__ === Object.prototype;//true
Object.__proto__ === Function.prototype;//true 登入後複製
所以再看回第一点中我们说的instanceof的运算规则,Function instanceof Object 和 Object instanceof Function运算的结果当然都是true啦!
如果看完以上,你还觉得上面的关系看晕了的话,只需要记住下面两个最重要的关系,其他关系就可以推导出来了:
1、所有的构造器的constructor都指向Function
2、Function的prototype指向一个特殊匿名函数,而这个特殊匿名函数的__proto__指向Object.prototype
至於prototype和__proto__的關係如何推導,可以參考我寫的上一篇部落格《三張圖搞懂JavaScript的原型物件與原型鏈》
本文試著闡述Js中原型(prototype)、原型鏈(prototype chain)等概念及其作用機制。上一篇文章(圖解Javascript上下文與作用域)介紹了Js中變數作用域的相關概念,實際上關注的一個核心問題是:“在執行當前這行程式碼時Js解釋器可以獲得哪些變數”,而原型與原型鏈其實還是關於這個問題。
我們知道,在Js中一切皆為物件(Object),但Js中並沒有類別(class);Js是基於原型(prototype-based)來實現的物件導向(OOP)的程式設計範式的,但並不是所有的物件都有prototype
這一屬性:
<br/>
<br/>
1
2
3
4
5
6
7
8
<br/>
var
a = {};
console.log(a.prototype);
##
#var#
#b =
function##(){};
console.log(b.prototype); //=> {}
# #c =
'你好'
##;
console.log(c.prototype);
//=>不明確的##########
prototype
是每個function
定義時自帶的屬性,但Js中function
本身也是對象,我們先來看看下面幾個概念的差異:
1. function
、Function
、Object
和{}
<br/>
<br/>
function是Js的一個關鍵字,用來定義函數類型的變量,有兩種語法形式:
#################### #############1######2######3######4#######5######6# #####7######8####9
<br/>
函數
#f1(){
#
console.log(
'這是函數f1!'
);
}
#### ## #######typeof######(f1); ######//=> '功能'#########var
# #f2 =
function
(){
console .log(
'這是函數f2!'
);
##}
typeof
(f2);
//=> '功能'##########
如果用更物件導向的方法定義函數,可以用Function
:
<br/>
<br/>
1
2
3
#4
##5
<br/>
var
# #f3 =
new
# Function(
"console.log('這是函數f3! '); “
);
f3();
typeof
(f3);
(函數);
就是一個用來建構函式類型變數的類,或者說是函數型別實例的建構子(constructor);與之相似的Object
或String
、Number
等,都是Js內建型別實例的建構子。比較特殊的是Object
,它用來產生物件類型,其簡寫形式為{}
:
<br/>
<br/>
1
2
#3
4
5
6
7
<br/>
var
# #o1 =
new
# #Object();
typeof
(o1);
#var
# o2 = {};
typeof
(o2);
(物件);
VS __proto__
#清楚了上面的概念之後再來看prototype
:
<br/>
Each function has two properties: length<br/> and
prototypeprototype
和length
是每一個函數類型自帶的兩個屬性,而其它非函數類型並沒有(開頭的例子已經說明),這一點之所以比較容易被忽略或誤解,是因為所有類型的構造函數本身也是函數,所以它們自帶了prototype
屬性:
<br/>
###############1#### ##2######3######4###################
console.log(Object.prototype);
console.log(Function.prototype);
console.log(String.prototype);
之外,Js中的所有物件(undefined
、null
等特殊情況除外)都有一個內建的[[ Prototype]]
屬性,指向它「父類」的prototype
,這個內建屬性在ECMA標準中並沒有給出明確的獲取方式,但是許多Js的實作(如Node、大部分瀏覽器等)都提供了一個__proto__
屬性來指稱這一[[Prototype]]
,我們透過下面的例子來說明實例中的__proto__
#是如何指向建構函式的prototype
的:
<br/>
<br/>
1
2
3
4
5
##6
7
# 8
9
10
11
<br/>
人=
函數
(){};
Person.prototype.type =
'Person'
;
Person.prototype.maxAge = 100;
##
var
# #p =
new
## #Person();
console.log(p.maxAge);
p.name =
'下雨天'
#;
#
# Person.prototype.constructor === Person;
console.log(p.prototype);
是一個函數類型的變量,因此自帶了prototype
屬性,prototype
屬性中的constructor
又指向Person
本身;透過new
關鍵字產生的Person
類別的實例p1
,透過__proto__
屬性指向了Person
的原型。這裡的__proto__
只是為了說明實例p1
在內部實現的時候與父類之間存在的關聯(指向父類的原型),在實際操作過程中實例可以直接通過.
取得父類別原型中的屬性,從而實現了繼承的功能。
3. 原型鏈 清楚了prototype
與__proto__
的概念與關係之後我們會對「Js中一切皆為物件」這句話有更加深刻的理解。進而我們會想到,既然__proto__
是(幾乎)所有物件都內建的屬性,而且指向父類別的原型,那是不是意味著我們可以「逆流而上」一直找到源頭呢?我們來看下面的範例:
<br/>
<br/>
1
## 2
3
4
5
6
7
8
#9
10
##11
12
13
14
15
#16
17
<br/>
var
# #Obj =
function
##(){};
var
o =
#new#
Obj();
o.__proto__ === Obj.prototype;
//=>真的 ######
o.__proto__.constructor === Obj;
##Obj.__proto__ === Function.prototype;
//=> true
Obj.__proto__.constructor === 函數;
//=> true##
#Function.__proto__ === Function.prototype; //=> true
Object.__proto__ === Object.prototype; //=>錯誤的 ######<p class="line number12 index11 alt1" style="margin:0px;padding:0px 1em;background-image:none;border:0px;float:none;line-height:1.8em;vertical-align:baseline;font-size:12px;min-height:auto;white-space:nowrap;"><code class="javascript plain" style="margin:0px;padding:0px;background:none;border:0px;float:none;line-height:1.8em;vertical-align:baseline;font-family:Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace;min-height:auto;color:rgb(0,0,0);">Object.__proto__ === Function.prototype;
##Function.__proto__.constructor === Function;
//=> true
Function.__proto__.__proto__;
//=> {}
Function.__proto__.__proto__ === o.__proto__.__proto__;
//=>真的 ######
o.__proto__.__proto__.__proto__ ===
null
;
物件也有__proto__
屬性,向上回溯一直到null
。
new
關鍵字的作用是完成上圖所示實例與父類別原型之間關係的串接,並建立一個新的物件;instanceof
關鍵字的作用也可以從上圖看出,其實就是判斷__proto__
(以及__proto__.__proto__
...)所指向是否父類別的原型:
<br/>
<br/>
#1
2
3
4
5
6
7
8
9
10
<br/>
#var
Obj =
函數
(){};
#var
# o =
new
# Obj();
o
實例
Obj;
o
instanceof
# Object;
o
instanceof
# Function;
o.__proto__.__proto__ === Object.prototype;
o.__proto__.__proto__ === 函數;
<br/>如此往復,直到直到Object.prototype 還沒有, 那麼就回傳undefind.
如果是呼叫方法就包錯, 該xxxx 不是一個函數<br/>
原型鏈結構圖 <br/>建構函數物件原型鏈結構圖
function Person (){}; var p = new Person();
<br/> #{} 物件原型鏈結構圖
#[] 陣列原型鏈結構圖
############Object.prototype### 對應的建構子##################p 對應的建構函數############p -> pTag.prototype( 就是o ) -> Object.prototype -> null#### var o = {
appendTo: function ( dom ) {
}
};
function pTag() {}
pTag.prototype = o;
var p = new pTag(); 登入後複製
函数的构造函数 Function 在 js 中 使用 Function 可以实例化函数对象. 也就是说在 js 中函数与普通对象一样, 也是一个对象类型( 非常特殊 )
函数是对象, 就可以使用对象的动态特性
函数是对象, 就有构造函数创建函数
函数是函数, 可以创建其他对象(函数的构造函数也是函数)
函数是唯一可以限定变量作用域的结构
函数是 Function 的实例 new Function( arg0, arg1, arg2, ..., argN, body ); 登入後複製
Function 中的参数全部是字符串
该构造函数的作用是将 参数链接起来组成函数
创建一个打印一句话的函数 // 传统的
function foo () {
console.log( '你好' );
}
// Function
var func = new Function( 'console.log( "你好" );' );
// 功能上, 这里 foo 与 func 等价 登入後複製
创建一个空函数 // 传统
function foo () {}
// Function
var func = new Function(); 登入後複製
传入函数内一个数字, 打印该数字 // 传统
function foo ( num ) {
console.log( num );
}
// Function
var func = new Function ( "num" ,"console.log( num );" );
func(); 登入後複製
利用 Function 创建一个函数, 要求传入两个数字, 打印其和 var func = new Function( 'num1', 'num2', 'console.log( num1 + num2 );' ); 登入後複製
练习: 利用 Function 创建一个函数, 要求允许函数调用时传入任意个数参数, 并且函数返回这些数字中最大的数字.<br/>练习: 利用 Function 创建一个求三个数中最大数的函数.
// 传统
function foo ( a, b, c ) {
var res = a > b ? a : b;
res = res > c ? res : c;
return res;
}
// Function
var func = new Function( 'a', 'b', 'c', 'var res = a > b ? a : b;res = res > c ? res : c;return res;' ) 登入後複製
解决代码太长的办法:
利用 加法 连接字符串
var func = new Function( 'a', 'b', 'c',
'var res = a > b ? a : b;' +
'res = res > c ? res : c;' +
'return res;' ); 登入後複製
利用字符串特性( 刚学 )
function foo ( a, b, c ) {
var res = a > b ? a : b;
res = res > c ? res : c;
return res;
}
var func = new Function( 'a', 'b', 'c', 'return foo( a, b, c );' ); 登入後複製
ES6 的语法( 少浏览器实现 )
(最终)利用 DOM 的特性完成该方法
arguments 对象 arguments 是一个伪数组对象. 它表示在函数调用的过程中传入的所有参数的集合.<br/>在函数调用过程中没有规定参数的个数与类型, 因此函数调用就具有灵活的特性, 那么为了方便使用,<br/>在 每一个函数调用的过程中, 函数代码体内有一个默认的对象 arguments, 它存储着实际传入的所有参数.
js 中函数并没有规定必须如何传参
定义函数的时候不写参数, 一样可以调用时传递参数
定义的时候写了参数, 调用的时候可以不传参
定义的时候写了一参数, 调用的时候可以随意的传递多个而参数
在代码设计中, 如果需要函数带有任意个参数的时候, 一般就不带任何参数, 所有的 参数利用 arguments 来获取.<br/>一般的函数定义语法, 可以写成:
function foo ( /* ... */ ) {
} 登入後複製
利用 Function 创建一个函数, 要求允许函数调用时传入任意个数参数, 并且函数返回这些数字中最大的数字. function foo ( ) {
// 所有的参数都在 arguments 中. 将其当做数组使用
// 问题而已转换成在有一个数组中求最大值
var args = arguments;
var max = args[ 0 ];
for ( var i = 1; i < args.length; i++ ) {
if ( max < args[ i ] ) {
max = args[ i ];
}
}
return max;
} 登入後複製
练习: 利用 Function 写一个函数, 要求传入任意个数字 求和
函数的原型链结构 任意的一个函数, 都是相当于 Function 的实例. 类似于 {} 与 new Object() 的关系
function foo () {};
// 告诉解释器, 有一个对象叫 foo, 它是一个函数
// 相当于 new Function() 得到一个 函数对象 登入後複製
函数有 __proto__
属性
函数的构造函数是 Function
函数应该继承自 Function.prototype
Fucntion.prototype
继承自 Object.protoype
构造函数有prototype, 实例对象才有__proto__指向原型, 构造函数的原型才有 constructor 指向构造函数
intanceof
array instanceof Array
判断 构造函数 Array 的原型 是否在 实例对象 array 的原型链存在
相关推荐:
关于js原型链的7篇文章推荐
js原型链继承的几个细节问题
js原型链原理看图说明_javascript技巧
以上是JS原型鏈詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
Win11管理員權限取得詳解
Mar 08, 2024 pm 03:06 PM
Windows作業系統是全球最受歡迎的作業系統之一,其新版本Win11備受矚目。在Win11系統中,管理員權限的取得是一個重要的操作,管理員權限可以讓使用者對系統進行更多的操作和設定。本文將詳細介紹在Win11系統中如何取得管理員權限,以及如何有效地管理權限。在Win11系統中,管理員權限分為本機管理員和網域管理員兩種。本機管理員是指具有對本機電腦的完全管理權限
Oracle SQL中的除法運算詳解
Mar 10, 2024 am 09:51 AM
OracleSQL中的除法運算詳解在OracleSQL中,除法運算是一種常見且重要的數學運算運算,用來計算兩個數相除的結果。除法在資料庫查詢中經常用到,因此了解OracleSQL中的除法運算及其用法是資料庫開發人員必備的技能之一。本文將詳細討論OracleSQL中除法運算的相關知識,並提供具體的程式碼範例供讀者參考。一、OracleSQL中的除法運算
PHP模運算子的作用及用法詳解
Mar 19, 2024 pm 04:33 PM
PHP中的模運算子(%)是用來取得兩個數值相除的餘數的。在本文中,我們將詳細討論模運算子的作用及用法,並提供具體的程式碼範例來幫助讀者更好地理解。 1.模運算子的作用在數學中,當我們將一個整數除以另一個整數時,就會得到一個商和一個餘數。例如,當我們將10除以3時,商數為3,餘數為1。模運算子就是用來取得這個餘數的。 2.模運算子的用法在PHP中,使用%符號來表示模
linux系統呼叫system()函數詳解
Feb 22, 2024 pm 08:21 PM
Linux系統呼叫system()函數詳解系統呼叫是Linux作業系統中非常重要的一部分,它提供了一種與系統核心互動的方式。其中,system()函數是常用的系統呼叫函數之一。本文將詳細介紹system()函數的使用方法,並提供對應的程式碼範例。系統呼叫的基本概念系統呼叫是使用者程式與作業系統核心互動的一種方式。使用者程式透過呼叫系統呼叫函數來請求作業系統
簡易JavaScript教學:取得HTTP狀態碼的方法
Jan 05, 2024 pm 06:08 PM
JavaScript教學:如何取得HTTP狀態碼,需要具體程式碼範例前言:在Web開發中,經常會涉及到與伺服器進行資料互動的場景。在與伺服器進行通訊時,我們經常需要取得傳回的HTTP狀態碼來判斷操作是否成功,並根據不同的狀態碼來進行對應的處理。本篇文章將教你如何使用JavaScript來取得HTTP狀態碼,並提供一些實用的程式碼範例。使用XMLHttpRequest
Linux的curl指令詳解
Feb 21, 2024 pm 10:33 PM
Linux的curl命令詳解摘要:curl是一種強大的命令列工具,用於與伺服器進行資料通訊。本文將介紹curl指令的基本用法,並提供實際的程式碼範例,幫助讀者更好地理解和應用該指令。一、curl是什麼? curl是命令列工具,用於發送和接收各種網路請求。它支援多種協議,如HTTP、FTP、TELNET等,並提供了豐富的功能,如檔案上傳、檔案下載、資料傳輸、代
深入了解Promise.resolve()
Feb 18, 2024 pm 07:13 PM
Promise.resolve()詳解,需要具體程式碼範例Promise是JavaScript中一種用來處理非同步操作的機制。在實際開發中,常常需要處理一些需要依序執行的非同步任務,而Promise.resolve()方法就是用來傳回一個已經Fulfilled狀態的Promise物件。 Promise.resolve()是Promise類別的靜態方法,它接受一個
numpy版本查詢方法詳解
Jan 19, 2024 am 08:20 AM
Numpy是一款Python科學計算庫,提供了豐富的陣列操作函數與工具。升級Numpy版本時需要查詢目前版本以確保相容性,本文將詳細介紹Numpy版本查詢的方法,並提供具體的程式碼範例。方法一:使用Python程式碼查詢Numpy版本使用Python程式碼可以輕鬆查詢Numpy的版本,以下是實作方法和範例程式碼:importnumpyasnpprint(np
See all articles