小弟我最近重新找回丟了很久的javascript,卻偏偏遇到很的弱弱的問題?
Javascript函數的傳值與傳址的比較,eg:
var a="alic";
var aobj = new String("alic"); //傳遞method為CBR
document.write(a==aobj); //其結果為true
var bobj = new String("alic");
document.write(aobj==bobj); //其結果為false,兩個字串物件對比時用的CBR.
為甚麼兩個值相同的字串物件相比較時其結果為false?
為甚麼一個字串與一個字串物件比較時其結果為true?
請大大們賜教,謝謝.
aobj==bobj false 沒錯.
物件是比位置.
我以C#作範例.
object a=new object();
object b=new object();
if(a==b)這會是false;
如果
object a=new object();
object b=a;
if(a==b)//這才會==true;
String與""並不是相同的東西,前者是Object後者是primitive value。這部份的規則請參見ECMA-262 Edition 3的11.9.3節:
當x與y做比較運算時,如果x與y參考到不同的物件,則結果為false。(規則13)所以:
<pre class="c" name="code">
var a = String('abc');
var b = String('abc');
if (a == b) {
alert('true');
} else {
alert('false');
}
結果是'false'。
如果x是物件而y是型別為String的primitive value,則將x轉換為primitive value後比較。(規則20)所以:
<pre class="c" name="code">
var a = 'abc';
var b = new String('abc');
if (a == b) {
alert('true');
} else {
alert('false');
}
結果是'true'。
我這樣講還是有點問題...
Javascript有幾種type:Undefined、Null、Boolean、String、Number、Object(另外有三種內部type:Reference、List、Completion)所以:
typeof undefined => 結果顯示'undefined'
typeof null => 結果顯示'null'
typeof true, typeof false => 結果顯示'boolean'
typeof 'abc' => 結果顯示'string'
type 3 => 結果顯示'number'
typeof window, typeof new Date(), typeof new String('def') => 結果顯示'object'
這幾種type中,Undefined、Null、Boolean、Number、String叫做primitive value,因為他們是直接用語言的低階實作來代表的資料。
其他的東西都衍生自object type。另外,如果用typeof Date=>結果顯示'function',因為Date是Date物件的constructor function。(javascript還真有許多例外要注意)
所以我的回答應該修正為:
如果typeof x是object而typeof y是string,則會把x轉換為primitive value再做一次比較。因為轉換為primitive value的動作是透過String物件的valueOf函數,所以可以這樣子做欺騙:
var a = new String('def');
var b = 'abc';
a.valueOf = function(){return 'abc';};
alert(a==b);
結果竟然會是'true'喔。
更激進一點:
var a = {};
var b = 'abc';
a.valueOf = function(){return 'abc';};
alert(a==b);
結果也是'true'喔。
最後一點補充,所以想要精確比較的話,就用===吧。
對了,還有一件事情,在javascript中,一切都是reference喔,沒有傳值這回事。primitive value也是用reference傳的,保存在變數內的也只是一個reference。(Reference這個內部type就是拿來實作這個的,不過規格裡要求的只是要運作起來像是有Reference,底層要如何實作是另一回事。不過用了Reference要做Garbage Collection應該會比較容易就是了)
謝謝pantc328,fillano兩位大大,fillano大大您分析得太精闢了,不過我需要理解的時間,還有個問題請教一下您:a.valueOf = function(){return 'abc';};按照您這樣好像在IE8裡面出現錯誤!firefox裡面沒有任何訊息.
恩?我用Firefox3.5、Google Chrome3、IE7、Safari4、Opera10測過都ok。我還沒裝IE8...
不過這本來就可以算是一種「漏洞」,不能這樣做也算是合理的。
請教fillano:
<pre class="c" name="code">function f(x) { x++; return x; }
var a = 1;
f(a) => 2;
print a => 1;
這樣算不算傳值呢?
看你怎麼定義傳值,但是這樣對Javascript來說不精確。
在函數執行時,他會進行identifier解析,第一步是處理函數的參數,把傳給他的參數assign給函數定義的參數,接下來是做local變數,如果沒有用var定義,就會沿著scope往外尋找,然後把找到的identifier assign給local變數。
assign這個動作,可以把他看作就是變數值的copy,問題在於,對於非primitive value,變數值其實是一個reference物件。
所以有一個狀況就有傳參考的效果:
function f(x) {x.show = function(){alert('you change me.');};}
var a = {};
f(a);
a.show();
在f函數裡面,是對被reference的物件屬性做操作,而不是去改動他的reference使他reference到別的東西。f函數執行完後,變數a所reference的物件已經被加上一個show的屬性,所以可以執行a.show()。
或是像我之前舉的String物件的例子:
function f(x) {x.valueOf = function(){return "ttt"};}
var a = new String("aaa");
var b = "ttt";
f(a);
alert(a==b);
結果也是true喔。
是可以這樣理解,但是不精確啦。
我上面回應的部份是根據ECMA-262 Edition 3的[11.3.1 Simple Assign Operator (=)] 及 [8.7.1 GetValue(V)]。但是精確地說,在函數這一部份的動作是定義在[10 Execution Contexts],尤其是[10.1.6 Activation Object] -> [10.1.3 Variable Instantiation] -> [10.1.4 Scope Chain and Identifier Resolution] -> [10.1.8 Arguments Object] -> [10.2 Entering An Execution Context] -> [10.2.3 Function Code] (可以依照這個順序看)。
上面的定義簡單地說就是:函數參數是assign給函數執行時execution context的Variable Object的properties,變數也是。identifier到底他的值是什麼,最先是到Variable Object的properties中找的。
規格是這樣定義啦,不過實作只要運作的看起來是依照這樣定義在運作就可以。
以物件導向概念易言之:
var aobj = new String("alic");
此意涵為新開系統記憶體空間 此行建立了兩個string物件
所以:
var aobj = new String("alic");
var bobj = new String("alic");
等於建立了三個string物件
以下列規則:
a==b (判斷是否為同一物件)
a.equals(b) (判斷值是否相同)
便可知道:
document.write(a==aobj); //其結果為true → 因為為同一物件 "alic"
document.write(aobj==bobj); //其結果為false → 因為兩者本來就是不同物件
其實沒什麼差。
傳值先,傳址後。
如果傳值,效率差,改傳址。
如果傳值,結果不對,改傳址。
<pre class="c" name="code">
var a="alic";
var aobj = new String("alic");
document.write(a==aobj); //兩個等於(==)會對被判別的變數做轉換型別的動作(coercion又稱為implicit type conversion)
document.write("<br>");
var bobj = new String("alic");
document.write(aobj==bobj); //兩個物件實體化為不同物件
document.write("<br>");
document.write(aobj.toString()==bobj.toString()); //除非將其轉string來比較
document.write("<br>");
document.write(typeof(a)); //this is string
document.write("<br>");
document.write(typeof(aobj)); //this is object
document.write("<br>");
document.write(typeof(bobj)); //this is object
document.write("<br>");
document.write(a===aobj); //三個等於比較準確,無轉換型別的動作