iT邦幫忙

0

Javascript函數的傳值與傳址的比較

blanse 2009-10-30 19:25:4114367 瀏覽

小弟我最近重新找回丟了很久的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?
請大大們賜教,謝謝.

12
pantc328
iT邦研究生 1 級 ‧ 2009-10-30 20:28:57

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;

12
fillano
iT邦超人 1 級 ‧ 2009-10-30 20:59:21

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'。

看更多先前的回應...收起先前的回應...
fillano iT邦超人 1 級 ‧ 2009-10-30 21:26:20 檢舉

我這樣講還是有點問題...

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還真有許多例外要注意)

fillano iT邦超人 1 級 ‧ 2009-10-30 21:57:42 檢舉

所以我的回答應該修正為:

如果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'喔。

fillano iT邦超人 1 級 ‧ 2009-10-30 22:00:22 檢舉

更激進一點:

var a = {};
var b = 'abc';
a.valueOf = function(){return 'abc';};
alert(a==b);

結果也是'true'喔。

fillano iT邦超人 1 級 ‧ 2009-10-30 22:01:50 檢舉

最後一點補充,所以想要精確比較的話,就用===吧。

fillano iT邦超人 1 級 ‧ 2009-10-31 01:05:54 檢舉

對了,還有一件事情,在javascript中,一切都是reference喔,沒有傳值這回事。primitive value也是用reference傳的,保存在變數內的也只是一個reference。(Reference這個內部type就是拿來實作這個的,不過規格裡要求的只是要運作起來像是有Reference,底層要如何實作是另一回事。不過用了Reference要做Garbage Collection應該會比較容易就是了)

blanse iT邦新手 5 級 ‧ 2009-10-31 02:17:30 檢舉

謝謝pantc328,fillano兩位大大,fillano大大您分析得太精闢了,不過我需要理解的時間,還有個問題請教一下您:a.valueOf = function(){return 'abc';};按照您這樣好像在IE8裡面出現錯誤!firefox裡面沒有任何訊息.

fillano iT邦超人 1 級 ‧ 2009-10-31 08:07:04 檢舉

恩?我用Firefox3.5、Google Chrome3、IE7、Safari4、Opera10測過都ok。我還沒裝IE8...

不過這本來就可以算是一種「漏洞」,不能這樣做也算是合理的。

10
henryjuan
iT邦新手 5 級 ‧ 2009-11-05 17:11:34

請教fillano:

<pre class="c" name="code">function f(x) { x++; return x; }
var a = 1;
f(a) => 2;
print a => 1;

這樣算不算傳值呢?

fillano iT邦超人 1 級 ‧ 2009-11-05 20:44:23 檢舉

看你怎麼定義傳值,但是這樣對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喔。

fillano iT邦超人 1 級 ‧ 2009-11-06 22:05:51 檢舉

是可以這樣理解,但是不精確啦。

我上面回應的部份是根據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中找的。

規格是這樣定義啦,不過實作只要運作的看起來是依照這樣定義在運作就可以。

8
jyw110408
iT邦新手 5 級 ‧ 2010-05-10 21:19:03

以物件導向概念易言之:
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 → 因為兩者本來就是不同物件

fillano iT邦超人 1 級 ‧ 2010-05-10 23:44:07 檢舉

javascript沒有a.equals(b)這樣的東西啦...這又不是Java

==並不單純是判斷是否為同一物件...這樣講太籠統,因為Object只是javascript的一個型別而已,而比較運算要對所有型別都能作用。==如何作用,在ECMA-262 Edition 3裡面定義的比較運算的演算規則有22個步驟,請看一下ECMA-262 Edition 3標準文件的11.9.1及11.9.3。

jyw110408 iT邦新手 5 級 ‧ 2010-05-11 14:33:09 檢舉

嗯嗯 謝謝指教!!
確實javascript是沒有equals()用法的!!
若有誤導大家的地方請見諒
(寫java太習慣讓自己簡易記法了,連舉例都忘記是javascript了...)

4
shunyuan
iT邦研究生 1 級 ‧ 2010-06-30 09:58:39

其實沒什麼差。

傳值先,傳址後。
如果傳值,效率差,改傳址。
如果傳值,結果不對,改傳址。
噴鼻血

fillano iT邦超人 1 級 ‧ 2010-06-30 10:13:30 檢舉

這篇是個萬年梗...

shunyuan iT邦研究生 1 級 ‧ 2010-06-30 14:06:29 檢舉

fillano提到:
這篇是個萬年梗...

高手一出手,就知有沒有。驚

0
hungchinwai
iT邦研究生 1 級 ‧ 2015-02-25 10:31:39
<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); //三個等於比較準確,無轉換型別的動作

我要發表回答

立即登入回答