1.構造函數:
通常構造函數首字母需要大寫,主要是為了區別ECMAScript的其它函數。(高程三 P145)
構造函數與其他函數的唯一區別,就在於調用它們的方式不同。只要通過new來調用,任何函數都是構造函數;而任何函數,如果不通過new來調用,那么它和普通函數也沒有任何區別。(P146)
所謂"構造函數",其實就是一個普通函數,但是內部使用了this變量。對構造函數使用new運算符,就能生成實例,並且this變量會綁定在實例對象上。
(就是一個普通的函數,與其他函數沒有任何區別,可以理解為 函數==構造函數,它只是概念上的一個定義,使用它用來實例化對象。)
對於JavaScript的內置對象,Object、Array、Date等等這些都是構造函數。
2. prototype(prototype屬性)和 _proto_
阮一峰 繼承機制的設計思想 __proto__ VS. prototype in JavaScript 深入理解JavaScript系列(10):JavaScript核心(晉級高手必讀篇)
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_是所有對象(包括函數)都有的,它才叫做對象的原型,原型鏈就是靠它形成的。(如果不是實在沒有別的辦法,是非常不建議在代碼中使用的。)
所有構造器/函數(函數也是對象)的__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。
2.1 prototype
Object.prototype The Object.prototype property (是對象的屬性)represents the Object prototype object. (prototype對象)
Javascript規定,每一個構造函數都有一個prototype屬性,指向另一個對象。這個對象的所有屬性和方法,都會被構造函數的實例繼承。
這意味着,我們可以把那些不變的屬性和方法,直接定義在prototype對象上。
這個屬性包含一個對象(以下簡稱"prototype對象"),所有實例對象需要共享的屬性和方法,都放在這個對象里面;那些不需要共享的屬性和方法,就放在構造函數里面。
實例對象一旦創建,將自動引用prototype對象的屬性和方法。也就是說,實例對象的屬性和方法,分成兩種,一種是本地的,另一種是引用(prototype對象)的。
2.2 _proto_
參考資料 JavaScript中__proto__與prototype的關系 理解js中的原型鏈,prototype與__proto__的關系 參考資料三
原型鏈如圖所示:

3. constructor:(constructor屬性)
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構造函數
4. apply(),call(),bind()
每個函數都包含兩個非繼承而來的方法: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()
5. this關鍵字
在函數代碼中使用this時很有趣,這種情況很難且會導致很多問題。
這種類型的代碼中,this值的首要特點(或許是最主要的)是它不是靜態的綁定到一個函數。
正如我們上面曾提到的那樣,this是進入上下文時確定,在一個函數代碼中,這個值在每一次完全不同。
不管怎樣,在代碼運行時的this值是不變的,也就是說,因為它不是一個變量,就不可能為其分配一個新值(相反,在Python編程語言中,它明確的定義為對象本身,在運行期間可以不斷改變)。
this 只是一個引用別名(referencing alias) - 這個別名只知道當前指向的那個對象, 而這也是最棘手的地方。
this 是一個特殊的標識符關鍵字 —— 在每個 function 中自動根據作用域(scope) 確定, 指向的是此次調用的 “所有者,owner” (即 那個對象)
this 是如何創建的?每調用一次 JavaScript 函數時,都會創建一個新的對象, 其中的信息包括: 傳入了哪些參數, 函數是如何調用(invoked)的, 函數是在哪里被調用(called)的,等等。該對象中還有一個重要的屬性是 this 引用, 函數是哪個對象的方法,this 就會自動綁定到該對象。
分析一下call的用法
- Step1: 把第二個到最后一個參數作為函數執行時要傳入的參數
- Step2: 把函數執行時的this指向第一個參數
- Step3: 在上面這個特殊的上下文中執行函數
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)。- 在最外層的代碼中, 不在任何 function 里面
- 不是對象方法(method)的函數(method)里面
- 不是構造函數(constructor)的函數里面
- 當函數通過
call()、apply()或者bind()調用時,this指向的是傳遞給這些方法的第一個參數。如果第一個參數是null或者不是一個對象, 那么this指向的是全局對象。 - 在使用
new操作符來調用一個函數時,this指向的是新創建的這個對象。 - 在 ECMAScript 6 中使用箭頭函數時,
this根據所處的語法作用域指向上級對象(parent object)。
了解了這些簡單直接的規則,我們就可以很容易地看出 this 指向的是哪個對象, 如果指向不正確, 那可以用學過的這些黑科技來搞定。
new operator(new 操作符)
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 操作符生成一個實例,這個實例是用戶自定義的“對象類型”或是內建的對象類型的實例。
Description:
Creating a user-defined object requires two steps:
- Define the object type by writing a function.通過一個函數(構造函數)來定義一種對象類型
- Create an instance of the object with
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.
new操作符的原理
When the code new Foo(...) is executed, the following things happen:
- A new object is created, inheriting(一個新的對象生成,繼承自構造函數的prototype屬性對象) from
Foo.prototype. - The constructor function
Foois called with the specified arguments, and withthisbound to the newly created object(構造函數被調用,並且把this關鍵字綁定到新生成的對象上).new Foois equivalent tonewFoo(), i.e. if no argument list is specified,Foois called without arguments. - The object returned by the constructor function becomes the result of the whole
newexpression. 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
