本文主要跟大家分享JS原型鏈詳解,函數(Function)才有prototype屬性,物件(除Object)擁有__proto__。希望能幫助大家。
#所謂原型鏈,指的就是圖中的proto 這條指標鏈!
原型鏈的頂層就是Object.prototype,而這個物件的是沒有原型物件的。
可在chrome的控制台裡面輸入:
#輸出是:
<br/>
原型鏈,如此而已。
原型是一個對象,其他物件可以透過它實現屬性繼承。
一、prototype與__proto__的差異 <br/>
<br/>
1
2
3
4
5
6
7
var
a = {};
console.log(a.prototype);
console.log(a.__proto__);
var
b =
function
(){}
console.log(b.prototype);
console.log(b.__proto__);
登入後複製
<br/>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var
a = {};
console.log(a.__proto__);
console.log(a.__proto__ === a.constructor.prototype);
var
A =
function
(){};
var
a =
new
A();
console.log(a.__proto__);
console.log(a.__proto__ === a.constructor.prototype);
var
a1 = {a:1}
var
a2 = Object.create(a1);
console.log(a2.__proto__);
console.log(a.__proto__ === a.constructor.prototype);
登入後複製
<br/>
1
2
3
4
5
var
A =
function
(){};
var
a =
new
A();
console.log(a.__proto__);
console.log(a.__proto__.__proto__);
console.log(a.__proto__.__proto__.__proto__);
登入後複製
我在寫一篇圖解prototype和__proto__的區別時,搜資料搜到了一個有意思的現象,下面這兩個運算返回的結果是一樣的:
<br/>
1
2
Function
instanceof
Object;
Object
instanceof
Function;
登入後複製
這個是怎麼一回事呢?要從運算子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/>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Function.constructor=== Function;
Object.constructor === Function;
console.log(Function.__proto__);
Function.__proto__.__proto__ === Object.prototype
Object.__proto__ === Function.prototype;
Function.prototype === Function.__proto__;
登入後複製
三、当构造器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
作者最新文章
1970-01-01 08:00:00
1970-01-01 08:00:00
2023-03-19 18:36:02
2023-03-21 07:36:01
1970-01-01 08:00:00
1970-01-01 08:00:00
1970-01-01 08:00:00
2023-03-20 14:32:01
1970-01-01 08:00:00
1970-01-01 08:00:00
最新問題
2025-03-19 09:10:13
2025-03-18 15:17:30
2025-03-18 15:16:33
2025-03-18 15:14:33
2025-03-18 15:12:30