一般構造函數首字母須要大寫,主要是爲了區別ECMAScript的其它函數。(高程三 P145)javascript
構造函數與其餘函數的惟一區別,就在於調用它們的方式不一樣。只要經過new來調用,任何函數都是構造函數;而任何函數,若是不經過new來調用,那麼它和普通函數也沒有任何區別。(P146)html
所謂"構造函數",其實就是一個普通函數,可是內部使用了this變量。對構造函數使用new運算符,就能生成實例,而且this變量會綁定在實例對象上。java
(就是一個普通的函數,與其餘函數沒有任何區別,能夠理解爲 函數==構造函數,它只是概念上的一個定義,使用它用來實例化對象。)web
對於JavaScript的內置對象,Object、Array、Date等等這些都是構造函數。express
阮一峯 繼承機制的設計思想 __proto__ VS. prototype in JavaScript 深刻理解JavaScript系列(10):JavaScript核心(晉級高手必讀篇)編程
_proto_segmentfault
The use of __proto__
is controversial, and has been discouraged(有爭議,不被鼓勵的). It was never originally included in the EcmaScript language spec, but modern browsers decided to implement it anyway. Only recently, the __proto__
property has been standardized in the ECMAScript 2015 language specification for web browsers to ensure compatibility, so will be supported into the future. 數據結構
_proto_
是全部對象(包括函數)都有的,它才叫作對象的原型,原型鏈就是靠它造成的。(若是不是實在沒有別的辦法,是很是不建議在代碼中使用的。)app
全部構造器/函數(函數也是對象)的__proto__都指向Function.prototype,它是一個空函數(Empty function)編程語言
Number.__proto__ === Function.prototype // true
Boolean.__proto__ === Function.prototype // true
String.__proto__ === Function.prototype // true
Object.__proto__ === Function.prototype // true
Function.__proto__ === Function.prototype // true
Array.__proto__ === Function.prototype // true
RegExp.__proto__ === Function.prototype // true
Error.__proto__ === Function.prototype // true
Date.__proto__ === Function.prototype // true
JavaScript中有內置(build-in)構造器/對象共計12個(ES5中新加了JSON),這裏列舉了可訪問的8個構造器。剩下如Global不能直接訪問,Arguments僅在函數調用時由JS引擎建立,Math,JSON是以對象形式存在的,無需new。它們的__proto__是Object.prototype。以下
Math.__proto__ === Object.prototype // true
JSON.__proto__ === Object.prototype // true
上面說的「全部構造器/函數」固然包括自定義的。以下
// 函數聲明
function Person() {}
// 函數表達式
var Man = function() {}
console.log(Person.__proto__ === Function.prototype) // true
console.log(Man.__proto__ === Function.prototype) // true
函數也是對象,構造函數也是對象,能夠理解爲:構造函數是由「Function構造函數「實例化出來的函數對象。
這說明什麼呢?
全部的構造器都來自於Function.prototype,甚至包括根構造器Object及Function自身。全部構造器都繼承了Function.prototype的屬性及方法。如length、call、apply、bind(ES5)。
知道了全部構造器(含內置及自定義)的__proto__都是Function.prototype,那Function.prototype的__proto__是誰呢?
相信都據說過JavaScript中函數也是一等公民,那從哪能體現呢?以下
console.log(Function.prototype.__proto__ === Object.prototype)
這說明全部的構造器也都是一個普通JS對象,能夠給構造器添加/刪除屬性等。同時它也繼承了Object.prototype上的全部方法:toString、valueOf、hasOwnProperty等。
最後Object.prototype的__proto__是誰?
Object.prototype.__proto__ ===
null
// true
已經到頂了,爲null。
實例是沒有prototype屬性的,prototype
只有函數(準確地說是構造函數)纔有的。它跟原型鏈沒有半毛錢關係。
它的做用是:構造函數new對象的時候,告訴構造函數新建立的對象的原型是誰。是的,只在new一個對象的時候才起做用。當你new完獲得這個對象後,隨便你怎麼改構造函數的prototype
屬性,都不會影響已建立的對象的原型鏈。
function Father(){ // Code goes here
} var obj1 = new Father(); // 此時,
Father.prototype = { name:"hhhhh", last:"wwwww", sayName:function(){ alert(this.name); } }
obj1.sayName() // error
// 由於,構造函數的prototype屬性被重寫了,js的對象都是不相等的
調用構造函數時會爲實例添加一個指向最初原型的[[prototype]]指針,而把原型修改成另一個對象就等於切斷了構造函數與最初原型之間的聯繫。實例中的指針僅指向原型,而不指向構造函數。
不過通常來講,咱們會使用__<內部屬性名>__ 下劃線來代替雙括號,例如__proto__(這是某些腳本引擎好比SpiderMonkey的對於原型概念的具體實現,儘管並不是標準).
若是一個對象的prototype沒有顯示的聲明過或定義過,那麼__proto__的默認值就是object.prototype, 而object.prototype也會有一個__proto__, 這個就是原型鏈的終點了,被設置爲null。
Object.prototype The Object.prototype
property (是對象的屬性)represents the Object
prototype object. (prototype對象)
Javascript規定,每個構造函數都有一個prototype屬性,指向另外一個對象。這個對象的全部屬性和方法,都會被構造函數的實例繼承。
這意味着,咱們能夠把那些不變的屬性和方法,直接定義在prototype對象上。
這個屬性包含一個對象(如下簡稱"prototype對象"),全部實例對象須要共享的屬性和方法,都放在這個對象裏面;那些不須要共享的屬性和方法,就放在構造函數裏面。
實例對象一旦建立,將自動引用prototype對象的屬性和方法。也就是說,實例對象的屬性和方法,分紅兩種,一種是本地的,另外一種是引用(prototype對象)的。
參考資料 JavaScript中__proto__與prototype的關係 理解js中的原型鏈,prototype與__proto__的關係 參考資料三
原型鏈如圖所示:
MDN Object.prototype.constructor
Returns a reference to the Object
constructor function that created the instance object(返回生成該實例的構造函數). Note that the value of this property is a reference to the function itself, not a string containing the function's name.
constructor是部署在Object.prototype上的屬性,因此位於原型鏈的最高一層(再高一層是null),任何對象(包括函數,都有constructor屬性)
任何一個prototype對象都有一個constructor屬性,指向它的構造函數。
eg: Cat.prototype.constructor = Cat;
每個實例也有一個constructor屬性(原型包含constructor屬性,所以能夠經過對象實例訪問),默認調用prototype對象的constructor屬性,即也指向它的構造函數.
eg: var new cat = Cat(){};
cat.constructor = Cat;
修改:添加刪除屬性仍是在原來的prototype對象上作的修改。
重寫:直接就用新的字面量對象來替換了,而字面量對象的constructor就是Object構造函數
每一個函數都包含兩個非繼承而來的方法:call() 和 apply()
這兩個函數等於設置函數體內this對象的值
call和apply是Function的方法,第一個參數是this,第二個是Function的參數。好比函數裏寫了this,普通調用這個方法這個this多是window。而若是使用了call()或是apply(),第一個參數寫啥,裏面的this就是啥。(即改變了this的指向)
call 和 apply 都是爲了改變某個函數運行時的 context 即上下文而存在的,換句話說,就是爲了改變函數體內部 this 的指向。由於 JavaScript 的函數存在「定義時上下文」和「運行時上下文」以及「上下文是能夠改變的」這樣的概念。
普通的函數調用 是隱式的傳入 this,call 和 apply 能夠顯式指定它。
call() 和apply() 真正強大的地方是可以擴充函數賴以運行的做用域,並且好處在於對象不須要與方法有任何的耦合關係。
bind()
在函數代碼中使用this時頗有趣,這種狀況很難且會致使不少問題。
這種類型的代碼中,this值的首要特色(或許是最主要的)是它不是靜態的綁定到一個函數。
正如咱們上面曾提到的那樣,this是進入上下文時肯定,在一個函數代碼中,這個值在每一次徹底不一樣。
無論怎樣,在代碼運行時的this值是不變的,也就是說,由於它不是一個變量,就不可能爲其分配一個新值(相反,在Python編程語言中,它明確的定義爲對象自己,在運行期間能夠不斷改變)。
this
只是一個引用別名(referencing alias) - 這個別名只知道當前指向的那個對象, 而這也是最棘手的地方。
this
是一個特殊的標識符關鍵字 —— 在每一個 function 中自動根據做用域(scope) 肯定, 指向的是這次調用的 「全部者,owner」 (即 那個對象)
this
是如何建立的?每調用一次 JavaScript 函數時,都會建立一個新的對象, 其中的信息包括: 傳入了哪些參數, 函數是如何調用(invoked)的, 函數是在哪裏被調用(called)的,等等。該對象中還有一個重要的屬性是 this
引用, 函數是哪一個對象的方法,this
就會自動綁定到該對象。
分析一下call的用法
function say(word) { console.log(world); } say("Hello world"); // 二者是
say.call(window, "Hello world"); // 等效的
每次看見functionName(xxx)的時候,你須要立刻在腦海中把它替換爲functionName.call(window,xxxx),
this
的做用域(scope) 與函數定義的位置沒有關係, 而是取決於函數在哪裏被調用( where they are called from ;i.e. the context)。
function test(){
this.x = 1;
alert(this.x);
}
test(); // 1
其實祕密花園裏面總結的不錯(與上面的四點是一一對應)
1.當在所有範圍內使用 this,它將會指向全局對象。
2.函數調用時,這裏 this 也會指向全局對象。
3.方法調用 test.foo(); 這個例子中,this 指向 test 對象。
4.調用構造函數 new foo(); 若是函數傾向於和 new 關鍵詞一塊使用,則咱們稱這個函數是構造函數。在函數內部,this 指向新建立的對象。
this
的知識點和其餘機制同樣, this
關鍵字也遵循一些簡單的規則, 若是你瞭解了這些規則,那就能夠用得順暢一些。下面快速回顧這兩節中所學的知識點:
this
指向的是全局對象: this
指向的是父對象(parent object)。
call()
、 apply()
或者 bind()
調用時, this
指向的是傳遞給這些方法的第一個參數。若是第一個參數是 null
或者不是一個對象, 那麼 this
指向的是全局對象。new
操做符來調用一個函數時, this
指向的是新建立的這個對象。this
根據所處的語法做用域指向上級對象(parent object)。瞭解了這些簡單直接的規則,咱們就能夠很容易地看出 this
指向的是哪一個對象, 若是指向不正確, 那能夠用學過的這些黑科技來搞定。
The new
operator creates an instance of a user-defined object type(?對象類型,仍是理解爲不一樣的數據結構?) or of one of the built-in object types that has a constructor function.
new 操做符生成一個實例,這個實例是用戶自定義的「對象類型」或是內建的對象類型的實例。
Creating a user-defined object requires two steps:
new
.用new操做符生成一個這種對象類型的實例To define an object type, create a function for the object type that specifies its name and properties. An object can have a property that is itself another object.
When the code new Foo(...)
is executed, the following things happen:
Foo.prototype
.Foo
is called with the specified arguments, and with this
bound to the newly created object(構造函數被調用,而且把this關鍵字綁定到新生成的對象上). new Foo
is equivalent to new
Foo
()
, i.e. if no argument list is specified, Foo
is called without arguments.new
expression. If the constructor function doesn't explicitly return an object(若構造函數沒有顯示的返回對象,則第一步生成的對象就會被返回), the object created in step 1 is used instead. (Normally constructors don't return a value, but they can choose to do so if they want to override the normal object creation process.) (高程三,建立對象,new操做符的做用)
test