JS原型函數相關基礎知識

函數對象和普通對象

 
 
 
 
//普通對象 var oo1 = {}, oo2 = new Object(), oo3 = []; console.log(typeof oo1 + ',' + typeof oo2 + ',' + typeof oo3); //object,object,object //函數對象 function ff1() {} var ff2 = function () {} var ff3 = new Function(); console.log(typeof ff1 + ',' + typeof ff2 + ',' + typeof ff3); //function,function,function console.log(typeof Object + ',' + typeof Function + ',' + typeof Array + ',' + typeof Date); //function,function,function,function

如上typeof返回值爲object的認爲普通對象,typeof返回值爲function的認爲函數對象,其主要差異以下:javascript

  1. 每個函數對象都有一個顯示的prototype屬性,它表明了對象的原型(Function.prototype函數對象是個例外,沒有prototype屬性)。每一個對象都有一個名爲proto的內部隱藏屬性,指向於它所對應的原型對象(chrome、firefox中名稱爲proto,而且能夠被訪問到)。原型鏈正是基於proto才得以造成(函數對象也有proto屬性)。
  2. 函數對象能夠做爲對象構造器,經過new操做符來返回一個對象。

即函數對象是特殊的對象,它擁有prototype屬性,可做爲對象構造器。JS提供的對象如Function、Object等都是函數對象。(注意Function是特殊的函數對象,new Fucntion()返回的是一個函數對象,而其它函數對象如Object等經過new操做符——new Object()返回的是一個普通對象)。html

prototype、proto和constructor

prototype是函數對象的一個屬性(每一個函數都有一個prototype屬性),這個屬性是一個指針,指向一個對象。它是顯示修改對象的原型的屬性。
proto是一個對象擁有的內置屬性(請注意:prototype是函數的內置屬性,proto是對象的內置屬性),是JS內部使用尋找原型鏈的屬性。
每一個函數對象都有名爲「prototype」的屬性(上面提到過Function.prototype函數對象是個例外,沒有prototype屬性),用於引用原型對象。此原型對象又有名爲「constructor」的屬性,它反過來引用函數自己。這是一種循環引用(F4.prototype.constructor===F4)。java

 
 
 
 
var F4 = function () { this.a = 'a'; return this.a; } var o4 = new F4(); console.log(o4.a); //'a' console.log(F4.prototype === o4.__proto__); //true console.log(F4.prototype === F4.__proto__); //false console.log(F4.prototype === Function.prototype); //false console.log(F4.__proto__ === Function.prototype); //true console.log(o4.constructor === F4); //true console.log(o4.constructor === F4.prototype); //false console.log(o4.constructor === F4.prototype.constructor); //true console.log(F4.constructor === Function); //true console.log(F4.constructor === Function.prototype.constructor); //true console.log(Function === Function.prototype.constructor); //true console.log(F4 === F4.prototype.constructor); //true console.log(Object.constructor === Function); //true console.log(Function.prototype === Function.__proto__); //true console.log(typeof Object + ',' + typeof Object.prototype + ',' + Object.prototype.__proto__ + ',' + Object.prototype.prototype); //function,object,null,undefined console.log(typeof Function + ',' + typeof Function.prototype + ',' + Function.prototype.__proto__ + ',' + Function.prototype.prototype); //function,function,[Object],undefined console.log(typeof F4 + ',' + typeof F4.prototype + ',' + F4.prototype.__proto__ + ',' + F4.prototype.prototype); //function,function,[Object],undefined console.log(typeof o4 + ',' + typeof o4.__proto__ + ',' + o4.__proto__.__proto__ + ',' + o4.__proto__.prototype); //object,object,[Object],undefined


上圖中o爲普通對象,F爲普通函數對象,橢圓表示函數對象,矩形表示函數對象,從中能夠看出:chrome

  1. 全部對象,包括函數對象的原型鏈最終都指向了Object.prototype,而Object.prototype.proto===null,原型鏈至此結束。
  2. Function.prototype是一個函數對象,可是Function.prototype卻沒有prototype屬性,即Function.prototype.prototype===undefined,因此Function.prototype函數對象是一個特例,沒有prototype屬性。除Function.prototype外,其它函數對象如Object.prototype
  3. Object.constructor===Function;說明Object是Function函數構造出來的。編程

    函數對象的特殊屬性

    arguments

    arguments其主要用途是用來保存函數參數。 數組

       
       
       
       
    arguments.length//值來判斷實參的個數 arguments[0]//表示傳遞的第一個元素 arguments[1]//表示傳遞的第二個元素

    arguments(不是Array的實例)對象還有一個屬性:callee,指向擁有arguments對象的函數,在編寫遞歸函數時經常使用。app

       
       
       
       
    function factorial(num){if(num<=1){ return 1;}else{ //return num*factorial(num-1); //一個實例化的函數對象能夠擁有不少個名字,不該限定函數名必須是factorial return num*arguments.callee(num-1);}}

    length

    length表示每一個函數準備接收的命名參數的個數,即形參的個數,區別於內部對象arguments是實參的個數。 函數

       
       
       
       
    function sum(num1,num2,num3){ alert(arguments.callee.length);//方法2:內部對象調用}sum(1,2)alert(sum.length);//方法1:函數調用

    this對象

    this指向調用當前函數的對象,當在網頁全局做用域中調用時,this對象引用的就是window。性能

       
       
       
       
    function hello(){ console.log('hello '+this.val);}var a={"val":"a","hello":hello},b={"val":"b","hello":hello};hello();//hello undefineda.hello();//hello ab.hello();//hello b

    call,apply, bind

    apply方法有兩個參數,1個是在其中運行函數的做用域(即要調用函數的對象),另外一個是參數數組。
    call方法與apply方法的做用同樣,可是接收參數的方式不一樣,其參數必須逐個例出來。
    bind方法新建立一個函數對象,這個函數對象的this值綁定參數傳入的對象。
    call 和 apply差異:參數的樣子不同,另外一個就是性能不同。(apply的性能要差不少,可到 JSPerf 上去跑跑看看)
    注意,在bind後,call、apply方法也不能改變this指針,以下面的示例this

       
       
       
       
    hello.call(a,'pa');//hello a pahello.apply(a,['pa']);//hello a pahello.call(b,'pb');//hello b pbhello.apply(b,['pb']);//hello b pbvar h = hello.bind(a);hello();//hello undefined undefinedh();//hello a undefinedh.call(b,'pb');//hello a pbh.apply(b,['pb']);//hello a pb

    new關鍵字

       
       
       
       
    var Base=function(){}var obj=new Base();//上面中new操做符幹了三件事情//1.初始化一個空對象obj var obj = {}; //2.將這個空對象的__proto__成員指向了Base函數對象prototype成員對象obj.__proto__ = Base.prototype;//3.將Base函數對象的this指針替換成obj,而後再調用Base函數,初始化objBase.call(obj);

    但要注意但函數自己返回值爲對象時new操做符不起做用。

    若是函數返回值爲常規意義上的值類型(Number、String、Boolean)時,new函數將會返回一個該函數的實例對象,而若是函數返回一個引用類型(Object、Array、Function),則new函數與直接調用函數產生的結果等同。

 
 
 
 
var F3 = function () { this.a = 'a'; var o = { 'name' : 'name' }; return o; } var o3 = new F3(); console.log(o3.a); //undefined console.log(o3.__proto__ === F3.prototype); //false console.log(F3.prototype == F3.__proto__); //false console.log(F3.__proto__ == Function.prototype); //true console.log(o3.constructor === F3); //false; var F4 = function () { this.a = 'a'; return this.a; } var o4 = new F4(); console.log(o4.a); //'a' console.log(F4.prototype === o4.__proto__); //true console.log(F4.prototype === F4.__proto__); //false console.log(F4.prototype === Function.prototype); //false console.log(F4.__proto__ === Function.prototype); //true console.log(o4.constructor === F4); //true console.log(o4.constructor === F4.prototype); //false console.log(o4.constructor === F4.prototype.constructor); //true

JS面向對象編程時類函數定義方式

 
 
 
 
var F2 = function () { var inner='inner'; this.o1 = 'o1'; this.f1 = function () { console.log('f1 ' + this.o1+' '+ inner); } function f2() { console.log('f2 ' + this.o1+' '+ inner); } } F2.prototype.f3 = function () { //console.log('f3' + this.o1+' '+ inner);失敗,找不到inner console.log('f3' + this.o1); } var o2 = new F2(); o2.f1(); //o2.f2();失敗,找不到f2 o2.f3();

js面向對象編程時類定義函數用prototype方式和this方式區別:this方式定義的函數如f1能夠訪問構造函數內部的變量如上例inner,prototype方式定義的函數f3則不能訪問內部變量; 但若是全部函數都採用this方式定義則每次實例化都要執行,浪費內存,也不合理。
this方式定義方法叫作特權方法,主要是爲了訪問內部的私有字段,這樣就能夠控制對某些字段的訪問。prototype方式定義的函數能訪問這些特權方法,進而間接訪問私有字段。

所以,若是要直接訪問私有字段,應該使用特權方法,也就是this定義的方法,應該定義在構造函數內部。相反,若是不須要直接訪問私有字段,應該使用prototype定義的方法,定義在構造函數外部。

參考:
Js中Prototype、proto、Constructor、Object、Function關係介紹
Js New一個函數和直接調用的區別



相關文章
相關標籤/搜索