原型對象和原型鏈在前端的工做中雖然不怎麼顯式的使用到,可是也會隱式的使用了,好比使用的jquery,vue等啦。在進入正題的時候,咱們仍是須要明白什麼是__proto__
,prototype
等知識點,主要講解構造函數,這篇博文大可能是問答形式進行...javascript
原文請戳這裏css
也許你會說出工廠模式、構造函數模式、原型模式、組合使用構造函數和原型模式、動態原型模式、寄生構造函數模式和穩妥構造函數
這些,可是咱們能夠對他們進行如下歸類--屬於函數建立對象。html
咱們能夠簡單的將建立對象的方式分爲三種:函數建立對象、字面量建立、Object建立
。固然,也能夠只是分紅兩類:函數建立對象和字面量建立對象
,由於new Object()
中的Object是自己就是一個函數。前端
Object // f Object(){ [native code] }
複製代碼
function
(注意是function哦)定義的對象有一個prototype屬性,prototype屬性又指向了一個prototype對象,注意prototype屬性與prototype對象是兩個不一樣的東西,要注意區別。用僞代碼表示以下:vue
var function{
prototype: prototype{} // function的prototype屬性指向prototype對象
}
複製代碼
注意上面說的是function
裏面纔會有prototype屬性,而咱們new出來的對象裏面是沒有的哦。java
# function
function Fun(name){
this.name = name;
}
Fun.prototype // {constructor:f}
var fun = new Fun('嘉明');
fun.prototype // undefined
# Object
Object.prototype // {constructor:f,__defineGetter__:f,...}
var object = new Object();
object.prototype // undefined
# 字面量,字面量能夠理解沒有prototype啦
var jack = {};
jack.prototype // undefined
複製代碼
在官方的es5種,定義了一個名叫作[[prototype]]的屬性,每一個對象(除了null)都擁有這樣一個屬性,這個屬性是一個指針,它指向一個名叫作原型對象的內存堆。而原型對象也是一個對象,所以又含有本身的[[prototype]]屬性,又指向下一個原型對象,終點指向咱們的Object.prototype
對象。jquery
注意⚠️ 這裏使用的是[[prototype]],而並不是__proto__。但是他們是同一個東西哈:[[prototype]]是官方所定義的屬性,而__proto__是瀏覽器(就是任性)本身對[[prototype]]所作的實現。編程
分三種狀況來講對象內部的__proto__
:canvas
包含Object()啦
狀況一:{}瀏覽器
var foo = {};
foo.__proto__; // {}
foo.__proto__ === Object.prototype; // true
foo.hasOwnProperty('prototype'); // false 函數纔有prototype屬性
foo.hasOwnProperty('__proto__'); // false
Object.prototype.hasOwnProperty('__proto__'); // true
複製代碼
代碼的最後一行,一個是返回了false,另外一個是true。⚠️由於它並不存在於foo對象(foo.__proto__)或者Foo.prototype(Foo.prototype.__proto__)或者Foo(Foo.__proto__)中【下面狀況二和三會有代碼驗證】
,實際上,它是來自於Object.prototype,與其說是一個屬性,不如說是一個getter/setter。
狀況二:function Foo(){}
1. function Foo(){};
2. Foo.__proto__; // [Function]
3. Foo.__proto__ === Object.prototype; // false
4. Foo.__proto__.__proto__ === Object.prototype; // true
5. Foo.prototype.__proto__ === Object.prototype; // true 函數的原型對象指向
6. Foo.__proto__ == Foo.prototype; //false
7. Foo.hasOwnProperty('__proto__'); // false
8. Foo.hasOwnProperty('prototype'); // true
複製代碼
在函數中,經過上面代碼2,3,4,5
能夠知道Foo.__proto__能夠理解爲指向了Foo.prototype(廣義上理解),而實際上兩個又有差異(狹義上,第6點能夠說明,歡迎補充)。而後就是每一個函數都有一個默認的prototype
屬性,其指向函數的原型對象。
狀況三:對象實例 new Foo()
function Foo(){};
var foo = new Foo();
foo.__proto__; // Foo {}
foo.__proto__ === Foo.prototype ; true
foo.hasOwnProperty('prototype'); false
foo.hasOwnProperty('__proto__'); false
複製代碼
上面可知,實例中是沒有prototype
這個屬性的,對比上面的三種狀況,也說明了只有在函數中才默認建立了prototype
屬性,並且指向了相應的函數原型對象。
在javascript語言中,constructor屬性是專門爲function而設計的,它存在於每個function的prototype屬性中,這個constructor保存了指向function的一個引用。
function F(){
// some code
}
# javascript內部會執行以下的動做
# 1.爲該函數添加一個原型(即prototype)屬性
# 2.爲prototype對象額外添加一個constructor屬性,而且該屬性保存指向函數F的一個引用
複製代碼
對象的實例中也有一個constructor屬性(從prototype那裏獲取的),每個對象實例均可以經過constructor對象訪問它的構造函數,以下:
var f = new F();
f.constructor === F; // true
f.constructor === F.prototype.constructor; // true
複製代碼
既然能夠訪問實例的類型f.constructor
,那麼咱們就能夠對實例進行特殊的處理啦:
if(f.constructor == F){
// some code
}
複製代碼
不過別這樣操做,由於constructor是不穩定的(見下文對象中的constructor的做用是什麼呢?),通常不會採起上面的這種操做,而是經過instanceof
:
if(f instanceof F){
// some code
}
複製代碼
這裏推薦賀師俊前輩的回答,原文複製以下:
constructor屬性不影響任何javascript的內部屬性。instanceof檢測對象的原型鏈,一般你是沒法修改的(不過某些引擎經過私有的__proto__屬性暴露出來)。
constructor其實沒有什麼用,只是javascript語言設計的歷史遺留物。因爲constructor屬性是能夠變動的,因此未必真的指向對象的構造函數,只是一個提示。不過,從編程習慣上,咱們應該儘可能讓對象的constructor指向其構造函數,以維持這種習慣。
例子解析:
delete Object.prototype.constructor; // true
({}).constructor; // undefined
({}) instanceof Object; // true
複製代碼
《javascript高級程序設計》中有說到全部函數的默認原型都是Object的實例,所以默認原型都會包含一個內部指針,指向Object.prototype。
那麼原型的最高指向就是Object了嘛?你能夠理解是Object,但是我認爲是null。
Object.prototype.__proto__; // null
Object.prototype.__proto__===null ; // true
複製代碼
最高指向是Object/null,無傷大雅。
當讀取實例的屬性時,若是找不到實例的屬性,就會查找與對象關聯的原型的屬性,若是仍是查找不到,就查找原型的原型,一直到頂級爲止。
function Person(){
}
Person.prototype.name = "嘉明";
var person = new Person();
person.name = "jiaming";
console.log(person.name); // "jiaming"
// 刪除實例的屬性後
delete person.name;
console.log(person.name); // "嘉明"
// 追加一個疑問 在__proto__中加屬性會覆蓋原來的嘛
person.__proto__.name = "嘉";
console.log(person.name); // "嘉" 證實成功,不建議這樣修改,畢竟__proto__是瀏覽器廠商實現的,非標準的
// 再追加一個疑問 __proto__添加的屬性或者方法是放在對象的原型上的嘛
var another_person = new Person();
console.log(another_person.name); // "嘉" 證實是放在對象的原型上的
複製代碼
屬性或者方法在本身的原型上沒有找到的話,那就要跑到原型上去找啦。以前有提到過全部函數的默認原型都是Object的實例,所以默認原型都會包含一個內部指針,指向Object.prototype。
那麼一個構造函數function Person(){}
就存在這樣的一個關係,實例出來的var person = new Person()
person經過__proto__指向構造函數的原型Person.prototype
,而後構造函數的原型指向Object的原型,便是Person.prototype.__proto__
指向Object.prototype
。
嗯,仍是針對構造函數來講哈,將上面提到的知識點彙總一下啦。上面都是純文字說明,下面就配上圖片好好理解下。
先上相關代碼:
function Person(name){
this.name = name;
}
Person.prototype.sayName = function(){
console.log(this.name);
}
var person = new Person("嘉明");
person.age = 12;
person.sayName(); // "嘉明"
console.log(person.name.toString()); // "嘉明"
var another_person = new Person("jiaming");
another_person.sayName();
複製代碼
上面代碼中,相關的關係以下圖
小demo是使用canvas畫出小星光,代碼以下:
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>canvas</title>
<style> body{ margin: 0; padding: 0; position: relative; } #myCanvas{ position: absolute; left: 50%; top: 50%; background: #000; margin-left: -300px; margin-top: -150px; } </style>
</head>
<body>
<canvas id="myCanvas" width="600" height="300" style="border: 1px solid #000;">
</canvas>
<script src="path/to/canvas.js"></script>
</body>
</html>
複製代碼
window.onload = function(){
var c = document.getElementById('myCanvas');
var grd = ""; // 漸變的顏色
// 上下文
var context = c.getContext("2d");
if(context){
// x,y,r 座標和半徑
function Star(x,y,r){
this.x = x;
this.y = y;
this.r = r;
this.init(this.x,this.y,this.r);
}
// 繪製星星
Star.prototype.init = function(x,y,r){
context.beginPath();
// 漸變顏色
grd = context.createRadialGradient(x,y,r-2,x,y,r+2)
grd.addColorStop(0, 'white');
grd.addColorStop(1, 'yellow');
context.fillStyle=grd;
// 畫圓
context.arc(x,y,r,0,2*Math.PI);
// 填充顏色
context.fill();
context.closePath();
}
// 建立星星
for(var i = 0; i < 300; i++){
var x = Math.floor(Math.random()*600);
var y = Math.floor(Math.random()*300);
var r = Math.floor(Math.random()*3)+2;
new Star(x,y,r)
}
}else{
var div = document.createElement("div");
div.innerHTML = "您的瀏覽器不支持canvas,請升級瀏覽器!";
document.getElementsByTagName("body")[0].appendChild(div);
}
}
複製代碼
實現的簡單效果以下圖哈(ps,您可自行驗證哈,改善啥的):
原文請戳這裏