with 為什麼JavaScript是一個被認為是構造函數和對象的函數?




js prototype create object (7)

只有當你使用new關鍵字實例化時,函數才能作為構造函數。

結果是一個可以使用“this”關鍵字來訪問成員屬性的對象。 該方法中的this關鍵字在函數以任何其他方式使用時沒有任何意義。

我近來一直在做很多的研究,但還沒有得到一個非常好的答案。 我在某處讀到,當JavaScript引擎遇到一個函數語句時,會創建一個新的Function()對象,這會導致我相信它可能是一個對象的子對象(從而成為一個對象)。 所以我發郵件給道格拉斯克羅克福德,他的回答是:

不完全是因為函數語句不會調用編譯器。

但它產生了類似的結果。

此外,據我所知,除非已經將實例化為新對象,否則不能在函數構造函數上調用成員。 所以這不起作用:

function myFunction(){
    this.myProperty = "Am I an object!";
}
myFunction.myProperty; // myFunction is not a function
myFunction().myProperty; // myFunction has no properties

但是,這將工作:

function myFunction(){
    this.myProperty = "Am I an object!";
}
var myFunctionVar = new myFunction();
myFunctionVar.myProperty;

這只是一個語義問題...在整個編程世界中,什麼時候一個對象真正成為一個對象,以及如何映射到JavaScript?


你的理解是錯誤的:

myFunction().myProperty; // myFunction has no properties

它不起作用的原因是因為“.myProperty”被應用到“myFunction()”的返回值,而不是對象“myFunction”。 以機智:

$ js
js> function a() { this.b=1;return {b: 2};}
js> a().b
2
js> 

請記住,“()”是一個操作符。 “myFunction”與“myFunction()”不一樣。 用新的時候你不需要“返回”

js> function a() { this.b=1;}
js> d = new a();
[object Object]
js> d.b;
1

功能和構造函數沒有什麼神奇的。 JavaScript中的所有對像都是...好的,對象。 但有些對像比其他對象更為特殊:即內置對象。 區別主要在於以下幾個方面:

  1. 物體的一般處理。 例子:
    • 數字和字符串是不可變的(⇒常量)。 沒有方法被定義為在內部改變它們 - 總是產生新的對像作為結果。 雖然他們有一些先天的方法,但不能改變它們,或者添加新的方法。 任何嘗試這樣做都將被忽略。
    • nullundefined是特殊對象。 任何嘗試在這些對像上使用方法或定義新方法都會導致異常。
  2. 適用的運營商 JavaScript不允許(重新)定義運算符,所以我們堅持使用可用的。
    • 數字有算術運算符的特殊方式: +-*/
    • 字符串有一個特殊的方式來處理連接運算符: +
    • 函數有一個特殊的方法來處理“調用”操作符:( ()new操作符。 後者俱有關於如何使用構造函數的prototype屬性的內在知識,構造一個具有適當的內部鏈接到對象的對象,並調用構造函數正確地設置它。

如果你看一下ECMAScript標準( PDF ),你會發現所有這些“額外的”功能都被定義為方法和屬性,但是其中的許多功能並沒有直接提供給程序員。 其中一些將暴露在標準ES3.1(2008年12月15日起草稿: PDF格式 )的新版本中。 一個屬性( __proto__已經在Firefox中公開

現在我們可以直接回答你的問題。 是的,一個函數對象具有屬性,我們可以隨意添加/刪除它們:

var fun = function(){/* ... */};
fun.foo = 2;
console.log(fun.foo);  // 2
fun.bar = "Ha!";
console.log(fun.bar);  // Ha!

這個功能實際上並不重要,因為我們不叫它! 現在讓我們來定義它:

fun = function(){ this.life = 42; };

它本身不是一個構造函數,它是一個在其上下文中運行的函數。 我們可以很容易地提供它:

var context = {ford: "perfect"};

// now let's call our function on our context
fun.call(context);

// it didn't create new object, it modified the context:
console.log(context.ford);           // perfect
console.log(context.life);           // 42
console.log(context instanceof fun); // false

正如你所看到的,它增加了一個屬性到已經存在的對象。

為了使用我們的函數作為構造函數,我們必須使用new運算符:

var baz = new fun();

// new empty object was created, and fun() was executed on it:
console.log(baz.life);           // 42
console.log(baz instanceof fun); // true

正如你可以看到, new的功能是一個構造函數。 以下行動是由new

  1. 新建空對象( {} )已創建。
  2. 其內部原型屬性設置為fun.prototype 。 在我們的情況下,它將是一個空對象( {} ),因為我們沒有以任何方式修改它。
  3. fun()被這個新對像作為上下文調用。

這是由我們的功能來修改新的對象。 通常它設置對象的屬性,但它可以做任何喜歡的事情。

有趣的瑣事:

  • 因為構造函數只是一個對象,我們可以計算它:

    var A = function(val){ this.a = val; };
    var B = function(val){ this.b = val; };
    var C = function(flag){ return flag ? A : B; };
    
    // now let's create an object:
    var x = new (C(true))(42);
    
    // what kind of object is that?
    console.log(x instanceof C); // false
    console.log(x instanceof B); // false
    console.log(x instanceof A); // true
    // it is of A
    
    // let's inspect it
    console.log(x.a); // 42
    console.log(x.b); // undefined
    
    // now let's create another object:
    var y = new (C(false))(33);
    
    // what kind of object is that?
    console.log(y instanceof C); // false
    console.log(y instanceof B); // true
    console.log(y instanceof A); // false
    // it is of B
    
    // let's inspect it
    console.log(y.a); // undefined
    console.log(y.b); // 33
    
    // cool, heh?
  • 構造函數可以返回一個覆蓋新創建的對象的值:

    var A = function(flag){
      if(flag){
        // let's return something completely different
        return {ford: "perfect"};
      }
      // let's modify the object
      this.life = 42;
    };
    
    // now let's create two objects:
    var x = new A(false);
    var y = new A(true);
    
    // let's inspect x
    console.log(x instanceof A); // true
    console.log(x.ford);         // undefined
    console.log(x.life);         // 42
    
    // let's inspect y
    console.log(y instanceof A); // false
    console.log(y.ford);         // perfect
    console.log(y.life);         // undefined

    正如你所看到的, x是原型和全部,而y是我們從構造函數返回的“裸”對象。


首先,JavaScript不像C ++ / Java那樣執行與對象相同的操作,所以您需要將這些想法從窗口中移出,以便了解JavaScript是如何工作的。

當這條線執行時:

var myFunctionVar = new myFunction();

那麼myFunction()this內部就是指你正在創建的這個新對象 - myFunctionVar 。 因此這行代碼:

 this.myProperty = "Am I an object!";

本質上是有結果的

 myFunctionVar.myProperty = "Am I an object!";

這可能會幫助您查看new操作員的一些文檔。 在JS中, new運算符基本上允許你從一個函數中創建一個對象 - 任何普通的舊函數。 對於使用new運算符的函數沒有什麼特別之處,它將它標記為構造函數,就像在C ++或Java中一樣。 正如文檔所述:

創建一個用戶定義的對像類型需要兩個步驟:

  1. 通過編寫函數來定義對像類型。
  2. 用new創建對象的一個實例。

那麼你對代碼做了什麼

function myFunction(){
    this.myProperty = "Am I an object!";
}

是創建一個將作為構造函數有用的函數。 代碼myFunction.myProperty失敗的原因是沒有名為myFunction 引用


要回答你的具體問題,技術上的功能總是對象。

例如,你總是可以這樣做:

function foo(){
  return 0;
}
foo.bar = 1;
alert(foo.bar); // shows "1"

當使用this指針時,Javascript函數的行為有點像其他OOP語言中的類。 可以使用new關鍵字將它們實例化為對象:

function Foo(){
  this.bar = 1;
}
var foo = new Foo();
alert(foo.bar); // shows "1"

現在,從其他OOP語言到Javascript的映射將很快失敗。 例如,實際上沒有Javascript中的類 - 對象使用原型鏈進行繼承。

如果你打算在JavaScript中做任何重要的編程,我強烈推薦Javascript: Crockford 的Good Parts ,那個你通過電子郵件發送的人。


事實上,職能是“一等公民”:他們是一個對象。

每個對像都有一個Prototype,但是只有一個函數的原型可以被直接引用。 當使用函數對像作為參數調用new時,將使用函數對象的原型作為原型構造一個新對象,並在輸入該函數之前將其設置為新對象。

所以你可以調用每個函數一個構造函數,即使它離開this一個單獨的。

在構造函數,原型等方面有非常好的教程...我個人從JavaScript面向對象編程中學到了很多東西。 它顯示了“繼承”其原型的函數的等價性,但是使用this來填充新對象的屬性以及使用特定原型的函數對象:

function newA() { this.prop1 = "one"; } // constructs a function object called newA
function newA_Too() {} // constructs a function object called newA_Too
newA_Too.prototype.prop1 = "one";

var A1 = new newA();
var A2 = new newA_Too();
// here A1 equals A2.

JavaScript基於ECMA腳本。 它的規範使用原型模型來做OOP。 ECMA腳本如何不執行嚴格的數據類型。 該對象需要實例化的原因,ECMA腳本需要一個“新”的調用,將為該屬性分配內存,否則它將保持一個函數,你可以調用它,如果你喜歡,在這種情況下,該屬性將初始化然後在功能結束時被銷毀。







constructor