//普通對象 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
即函數對象是特殊的對象,它擁有prototype屬性,可做爲對象構造器。JS提供的對象如Function、Object等都是函數對象。(注意Function是特殊的函數對象,new Fucntion()返回的是一個函數對象,而其它函數對象如Object等經過new操做符——new Object()返回的是一個普通對象)。html
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
Object.constructor===Function;說明Object是Function函數構造出來的。編程
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表示每一個函數準備接收的命名參數的個數,即形參的個數,區別於內部對象arguments是實參的個數。 函數
function sum(num1,num2,num3){ alert(arguments.callee.length);//方法2:內部對象調用}sum(1,2)alert(sum.length);//方法1:函數調用
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
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
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
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一個函數和直接調用的區別