JavaScript 構造函數 prototype屬性和_proto_和原型鏈 constructor屬性 apply(),call()和bind() 關鍵字this new操做符

1.構造函數:

一般構造函數首字母須要大寫,主要是爲了區別ECMAScript的其它函數。(高程三 P145)javascript

構造函數與其餘函數的惟一區別,就在於調用它們的方式不一樣。只要經過new來調用,任何函數都是構造函數;而任何函數,若是不經過new來調用,那麼它和普通函數也沒有任何區別。(P146)html

所謂"構造函數",其實就是一個普通函數,可是內部使用了this變量。對構造函數使用new運算符,就能生成實例,而且this變量會綁定在實例對象上。java

(就是一個普通的函數,與其餘函數沒有任何區別,能夠理解爲 函數==構造函數,它只是概念上的一個定義,使用它用來實例化對象。)web

 對於JavaScript的內置對象,Object、Array、Date等等這些都是構造函數。express

 this 上下文只存在兩種語義,一種是被看成方法調用,this 指向調用它的對象;一種是做爲函數調用,指向 Global 對象(嚴格模式下爲 undefined)。 

2.  prototype(prototype屬性)和 _proto_

 阮一峯 繼承機制的設計思想   __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。

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關鍵字  

 參考資料,學會JS的this這一篇就夠了,根本不用記  JavaScript中this指針指向的完全理解  javascript的this用法   掌握js中的this(一)    掌握js中的this(二)  

在函數代碼中使用this時頗有趣,這種狀況很難且會致使不少問題。

這種類型的代碼中,this值的首要特色(或許是最主要的)是它不是靜態的綁定到一個函數。

正如咱們上面曾提到的那樣,this是進入上下文時肯定,在一個函數代碼中,這個值在每一次徹底不一樣。

無論怎樣,在代碼運行時的this值是不變的,也就是說,由於它不是一個變量,就不可能爲其分配一個新值(相反,在Python編程語言中,它明確的定義爲對象自己,在運行期間能夠不斷改變)。

總的原則,那就是this指的是,調用函數的那個對象。
通俗點講, 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)。

狀況一:純粹的函數調用
這是函數的最一般用法,屬於全局性調用,所以this就表明全局對象Global。

  function test(){

    this.x = 1;

    alert(this.x);

  }

  test(); // 1

狀況二:做爲對象方法的調用
 函數還能夠做爲某個對象的方法調用,這時this就指這個上級對象。
 
 狀況三 做爲構造函數調用
所謂構造函數,就是經過這個函數生成一個新對象(object)。這時,this就指這個新對象。
 
狀況四 apply調用
apply()是函數對象的一個方法,它的做用是改變函數的調用對象,它的第一個參數就表示改變後的調用這個函數的對象。所以,this指的就是這第一個參數。

其實祕密花園裏面總結的不錯(與上面的四點是一一對應)
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 操做符)

mdn  

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:

  1. Define the object type by writing a function.經過一個函數(構造函數)來定義一種對象類型
  2. 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:

  1. A new object is created, inheriting(一個新的對象生成,繼承自構造函數的prototype屬性對象) from Foo.prototype.
  2. The constructor function 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.
  3. 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

相關文章
相關標籤/搜索