javascript - jquery抓取網頁內容




JavaScript是傳遞引用還是傳值語言? (20)

sharing what I know of references in javascript

In Javascript, objects are stored as references:

var a = {
  a: 1,
  b: 2,
  c: 3
};
var b = a;

//b.c is referencing to a.c value
console.log(b.c) //output: 3
//changing value of b.c
b.c = 4
//also changes the value of a.c
console.log(a.c) //output: 4

原始類型(數字,字符串等)按值傳遞,但對像是未知的,因為它們可以是值傳遞的(如果我們認為持有對象的變量實際上是對對象的引用)並通過引用傳遞(當我們認為對象的變量持有對象本身時)。

雖然最後並不重要,但我想知道傳遞約定的正確方法。 有沒有JavaScript規範的摘錄,它定義了什麼應該是關於這個的語義?


  1. Primitives (Number, Boolean) are passed by value.
    • Strings are immutable, so it doesn't really matter for them.
  2. Objects are passed by reference ( the reference is passed by value)

Javascript始終是按值傳遞的 ,一切都是有價值的。 對像是值,對象的成員函數本身就是值(記住函數是Javascript中的第一類對象)。 另外,關於Javascript中的所有內容都是對象的概念,這是錯誤的。 字符串,符號,數字,布爾值,零值和未定義都是原語 。 有時它們可以利用從其基本原型繼承的一些成員函數和屬性,但這僅僅是為了方便,並不意味著它們本身就是對象。 嘗試以下參考

x = "test";
alert(x.foo);
x.foo = 12;
alert(x.foo);

在這兩個警報中,你會發現值是未定義的。


All function arguments in ECMAScript are passed by value. This means that the value outside of the function is copied into an argument on the inside of the function the same way a value is copied from one variable to another. If the value is primitive, then it acts just like a primitive variable copy, and if the value is a reference, it acts just like a reference variable copy. This is often a point of confusion for developers, because variables are accessed both by value and by reference, but arguments are passed only by value. When an argument is passed by value, the value is copied into a local variable (a named argument and, in ECMAScript, a slot in the arguments object). When an argument is passed by reference, the location of the value in memory is stored into a local variable, which means that changes to the local variable are refl ected outside of the function. (This is not possible in ECMAScript.)


For programming language lawyers, I've went through the following sections of ECMAScript 5.1 (which is easier to read than the latest edition), and go as far as asking it on the ECMAScript mailing list.

TL;DR : Everythings're passed by value, but properties of Objects are references, and the definition of Object is creepily lacking in the standard.

Construction of Argument Lists

Section 11.2.4 "Argument Lists" says the following on producing a argument list consisting of only 1 argument:

The production ArgumentList : AssignmentExpression is evaluated as follows:

  1. Let ref be the result of evaluating AssignmentExpression.
  2. Let arg be GetValue(ref).
  3. Return a List whose sole item is arg.

The section also enumerate cases where argument list has 0 or >1 arguments.

Thus, everything's are passed by reference.

Access of Object Properties

Section 11.2.1 "Property Accessors"

The production MemberExpression : MemberExpression [ Expression ] is evaluated as follows:

  1. Let baseReference be the result of evaluating MemberExpression.
  2. Let baseValue be GetValue(baseReference).
  3. Let propertyNameReference be the result of evaluating Expression.
  4. Let propertyNameValue be GetValue(propertyNameReference).
  5. Call CheckObjectCoercible(baseValue).
  6. Let propertyNameString be ToString(propertyNameValue).
  7. If the syntactic production that is being evaluated is contained in strict mode code, let strict be true, else let strict be false.
  8. Return a value of type Reference whose base value is baseValue and whose referenced name is propertyNameString, and whose strict mode flag is strict.

Thus, properties of Objects are always available as reference.

On Reference

It is described in section 8.7 "The Reference Specification Type", that references are not real types in the language - they're only used to describe the behavior of the delete, the typeof, and the assignment operators.

Definition of "Object"

It is defined in 5.1 edition that "An Object is a collection of properties". Therefore, we can infer, that the value of the object is the collection, but as to what is the value of the collection is poorly defined in the spec, and requires a bit of effort to understand.


I have found the extend method of the Underscore.js library very useful when I want to pass in an object as a parameter which may either be modified or replaced entirely.

function replaceOrModify(aObj) {
  if (modify) {

    aObj.setNewValue('foo');

  } else {

   var newObj = new MyObject();
   // _.extend(destination, *sources) 
   _.extend(newObj, aObj);
  }
}

I've read through these answers multiple times, but didn't REALLY get it until I learned about the technical definition of "Call by sharing" as termed by Barbara Liskov

The semantics of call by sharing differ from call by reference in that assignments to function arguments within the function aren't visible to the caller (unlike by reference semantics)[citation needed], so eg if a variable was passed, it is not possible to simulate an assignment on that variable in the caller's scope. However, since the function has access to the same object as the caller (no copy is made), mutations to those objects, if the objects are mutable, within the function are visible to the caller, which may appear to differ from call by value semantics. Mutations of a mutable object within the function are visible to the caller because the object is not copied or cloned — it is shared.

That is, parameter references are alterable if you go and access the parameter value itself. On the other hand, assignment to a parameter will disappear after evaluation, and is non-accessible to the function caller.


In a low level language, if you want to pass a variable by reference you have to use a specific syntax in the creation of the function:

int myAge = 14;
increaseAgeByRef(myAge);
function increaseAgeByRef(int &age) {
  *age = *age + 1;
}

The &age is a reference to myAge , but if you want the value you have to convert the reference, using *age .

Javascript is a high level language that does this conversion for you. So, although objects are passed by reference, the language converts the reference parameter to the value. You don't need to use & , on the function definition, to pass it by reference, neither * , on the function body, to convert the reference to the value, JS does it for you.

That's why when you try to change an object inside a function, by replacing it's value (ie age = {value:5} ), the change doesn't persist, but if you change it's properties (ie age.value = 5 ), it does.

學到更多


My simple way to understand this...

  • When calling a function, you are passing the content (reference or value) of the argument variables, not the the variables themselves.

    var var1 = 13;
    var var2 = { prop: 2 };
    
    //13 and var2's content (reference) are being passed here
    foo(var1, var2); 
    
  • Inside the function, parameter variables, inVar1 and inVar2 , receive the contents being passed.

    function foo(inVar1, inVar2){
        //changing contents of inVar1 and inVar2 won't affect variables outside
        inVar1 = 20;
        inVar2 = { prop: 7 };
    }
    
  • Since inVar2 received the reference of { prop: 2 } , you can change the value of the object's property.

    function foo(inVar1, inVar2){
        inVar2.prop = 7; 
    }
    

Passing arguments to a function in JavaScript is analogous to passing parameters by pointer value in C:

/*
The following C program demonstrates how arguments
to JavaScript functions are passed in a way analogous
to pass-by-pointer-value in C. The original JavaScript
test case by @Shog9 follows with the translation of
the code into C. This should make things clear to
those transitioning from C to JavaScript.

function changeStuff(num, obj1, obj2)
{
    num = num * 10;
    obj1.item = "changed";
    obj2 = {item: "changed"};
}

var num = 10;
var obj1 = {item: "unchanged"};
var obj2 = {item: "unchanged"};
changeStuff(num, obj1, obj2);
console.log(num);
console.log(obj1.item);    
console.log(obj2.item);

This produces the output:

10
changed
unchanged
*/

#include <stdio.h>
#include <stdlib.h>

struct obj {
    char *item;
};

void changeStuff(int *num, struct obj *obj1, struct obj *obj2)
{
    // make pointer point to a new memory location
    // holding the new integer value
    int *old_num = num;
    num = malloc(sizeof(int));
    *num = *old_num * 10;
    // make property of structure pointed to by pointer
    // point to the new value
    obj1->item = "changed";
    // make pointer point to a new memory location
    // holding the new structure value
    obj2 = malloc(sizeof(struct obj));
    obj2->item = "changed";
    free(num); // end of scope
    free(obj2); // end of scope
}

int num = 10;
struct obj obj1 = { "unchanged" };
struct obj obj2 = { "unchanged" };

int main()
{
    // pass pointers by value: the pointers
    // will be copied into the argument list
    // of the called function and the copied
    // pointers will point to the same values
    // as the original pointers
    changeStuff(&num, &obj1, &obj2);
    printf("%d\n", num);
    puts(obj1.item);
    puts(obj2.item);
    return 0;
}

Semantics!! Setting concrete definitions will necessarily make some answers and comments incompatible since they are not describing the same thing even when using the same words and phrases, but it is critical to get past the confusion (especially for new programmers).

First of all, there are multiple levels of abstraction that not everyone seems to grasp. Newer programmers who have learned on 4th or 5th generation languages may have difficulty wrapping their mind around concepts familiar to assembly or C programmers not phased by pointers to pointers to pointers. Pass-by-reference does not simply mean the ability to change a referenced object using a function parameter variable.

Variable : Combined concept of a symbol which references a value at a particular location in memory. This term is usually too loaded to be used alone in discussing details.

Symbol : Text string used to refer to variable (ie variable's name).

Value : Particular bits stored in memory and referenced using variable's symbol.

Memory location : Where a variable's value is stored. (The location itself is represented by a number separate from the value stored at the location.)

Function parameter : Variable declared in a function definition, used for referencing variables passed to the function.

Function argument : Variable outside the function which is passed to the function by the caller.

Object variable : Variable whose basic underlying value is not the "object" itself, rather its value is a pointer (memory location value) to another location in memory where the object's actual data is stored. In most higher-generation languages, the "pointer" aspect is effectively hidden by automatic de-referencing in various contexts.

Primitive variable : Variable whose value IS the actual value. Even this concept can be complicated by auto-boxing and object-like contexts of various languages, but the general ideas is that the variable's value IS the actual value represented by the variable's symbol rather than a pointer to another memory location.

Function arguments and parameters are not the same thing. Also, a variable's value is not the variable's object (as already pointed out by various people, but apparently ignored). These distinctions are critical to proper understanding.

Pass-by-value or Call-by-sharing (for objects) : The function argument's value is COPIED to another memory location which is referenced by the function's parameter symbol (regardless of whether it's on the stack or heap). In other words, the function parameter received a copy of the passed argument's value... AND (critical) the argument's value IS NEVER UPDATED / ALTERED / CHANGED by the calling function. Remember, an object variable's value is NOT the object itself, rather it is the pointer to the object, so passing an object variable by value copies the pointer to the function parameter variable. The function parameter's value points to the exact same object in memory. The object data itself can be altered directly via the function parameter, BUT the function argument's value IS NEVER UPDATED, so it will continue to point to the same object throughout and even after the function call (even if its object's data was altered or if the function parameter is assigned a different object altogether). It is incorrect to conclude that the function argument was passed by reference just because the referenced object is updatable via the function parameter variable.

Call / Pass-by-reference : The function argument's value can/will be updated directly by the corresponding function parameter. If it helps, the function parameter becomes an effective "alias" for the argument--they effectively refer to the same value at the same memory location. If a function argument is an object variable, the ability to change the object's data is no different than the pass-by-value case since the function parameter will still point to the same object as the argument. But in the object variable case, if the function parameter is set to a completely different object, then the argument will likewise also point to the different object--this does not happen in the pass-by-value case.

JavaScript does not pass by reference. If you read closely, you will realize that all contrary opinions misunderstand what is meant by pass-by-value and they falsely conclude that the ability to update an object's data via the function parameter is synonymous to "pass-by-value".

Object clone/copy : A new object is created and the original object's data is copied. This can be a deep copy or shallow copy, but the point is that a new object is created. Creating a copy of an object is a separate concept from pass-by-value. Some languages distinguish between class object and structs (or the like), and may have different behavior for passing variables of the different types. But JavaScript does not do anything like this automatically when passing object variables. But the absence of automatic object cloning does not translate to pass-by-reference.


Simple values inside functions will not change those values outside of the function (they are passed by value), whereas complex ones will (they are passed by reference).

function willNotChange(x) {

x = 1;

}

var x = 1000;

willNotChange(x);

document.write('After function call, x = ' + x + '<br>'); //still 1000

function willChange(y) {

y.num = 2;

}

var y = {num: 2000}; 

willChange(y);
document.write('After function call y.num = ' + y.num + '<br>'); //now 2, not 2000

可以這樣想:它總是按價值傳遞。 但是,對象的值不是對象本身,而是對該對象的引用。

這裡是一個例子,傳遞一個數字(一個原始類型)

function changePrimitive(val) {
    //at this point there are two '10's in memory. Changing one won't affect the other
    val = val * 10;
}
var x = 10;
changePrimitive(x);
//x === 10

用對象重複此操作會產生不同的結果:

function changeObject(obj) {
    //at this point there are two references (x and obj) in memory, but these both point to the same object.
    //changing the object will change the underlying object x and obj both hold a reference to
    obj.val = obj.val * 10;
}
var x = { val: 10 };
changeObject(x);
//x === { val: 100 }

再舉一個例子:

function changeObject(obj) {
    //again, there are two references (x and obj) in memory, these both point to the same object.
    //now we create a completely new object and assign it. 
    //obj's reference now points to the new object. x's reference doesn't change
    obj = { val: 100 };
}
var x = { val: 10 };
changeObject(x);
//x === { val: 10}

在JavaScript中,值的類型只能控制值是通過值複製還是通過引用複制分配。

原始值始終由值複製分配/傳遞

  • null
  • undefined
  • 布爾
  • 符號在ES6

複合值始終由引用副本分配/傳遞

  • 對象
  • 陣列
  • 功能

例如

var a = 2;
var b = a; // `b` is always a copy of the value in `a`
b++;
a; // 2
b; // 3

var c = [1,2,3];
var d = c; // `d` is a reference to the shared `[1,2,3]` value
d.push( 4 );
c; // [1,2,3,4]
d; // [1,2,3,4]

在上面的代碼片段中,因為2是一個標量原語, a保存該值的一個初始副本,並且b被賦值為另一個值。 當改變b ,你決不會改變b中的值。

但是cd都是對相同的共享值[1,2,3]單獨引用,這是一個複合值。 值得注意的是, cd都不會“擁有” [1,2,3]值 - 它們只是與該值相等的同位體引用。 因此,當使用任一引用修改( .push(4) )實際的共享array值時,它只影響一個共享值,並且兩個引用都會引用新修改的值[1,2,3,4]

var a = [1,2,3];
var b = a;
a; // [1,2,3]
b; // [1,2,3]

// later
b = [4,5,6];
a; // [1,2,3]
b; // [4,5,6]

當我們進行賦值b = [4,5,6] ,我們完全不會影響a仍在引用的地方( [1,2,3] )。 要做到這一點, b必須是指向array的指針而不是引用 - 但JS中不存在這樣的功能!

function foo(x) {
    x.push( 4 );
    x; // [1,2,3,4]

    // later
    x = [4,5,6];
    x.push( 7 );
    x; // [4,5,6,7]
}

var a = [1,2,3];

foo( a );

a; // [1,2,3,4]  not  [4,5,6,7]

當我們傳入參數a ,它將a引用的副本分配給xxa是指向相同[1,2,3]值的單獨參考。 現在,在函數內部,我們可以使用該引用來改變值本身( push(4) )。 但是當我們做賦值x = [4,5,6] ,這決不會影響初始引用a指向的位置 - 仍然指向(現在修改的) [1,2,3,4]值。

要通過值複製有效地傳遞複合值(如array ),您需要手動複製它,以便傳遞的引用不會指向原始值。 例如:

foo( a.slice() );

可以通過引用副本傳遞的複合值(對象,數組等)

function foo(wrapper) {
    wrapper.a = 42;
}

var obj = {
    a: 2
};

foo( obj );

obj.a; // 42

在這裡, obj充當標量原始屬性a的包裝。 當傳遞給foo(..)obj引用的副本被傳入並設置為wrapper參數。 我們現在可以使用wrapper引用來訪問共享對象,並更新其屬性。 函數完成後, obj.a將看到更新的值42

Source


我的2美分....這是我理解它的方式。 (如果我錯了,請隨時糾正我)

現在是時候拋出你所知道的關於價值/參考傳遞的一切。

因為在JavaScript中,它是通過值或通過引用或其他方式傳遞並不重要。 重要的是突變與分配傳遞給函數的參數。

好的,讓我盡力解釋我的意思。 假設你有幾個對象。

var object1 = {};
var object2 = {};

我們所做的是“賦值”......我們已經將2個單獨的空對象分配給變量“object1”和“object2”。

現在,讓我們說,我們更喜歡object1 ...所以,我們“分配”一個新的變量。

var favoriteObject = object1;

接下來,無論出於何種原因,我們認為我們更喜歡對象2。 所以,我們只需做一些重新分配。

favoriteObject = object2;

object1或object2沒有發生任何事情。 我們根本沒有改變任何數據。 我們所做的只是重新分配我們最喜歡的對象。 知道object2和favoriteObject都分配給同一個對像很重要。 我們可以通過這些變量中的任何一個來改變對象。

object2.name = 'Fred';
console.log(favoriteObject.name) // logs Fred
favoriteObject.name = 'Joe';
console.log(object2.name); // logs Joe 

好的,現在我們來看看像字符串這樣的基元

var string1 = 'Hello world';
var string2 = 'Goodbye world';

再次,我們選擇一個最喜歡的。

var favoriteString = string1;

我們的favoriteString和string1變量都分配給'Hello world'。 現在,如果我們想改變我們最喜歡的字符串? 會發生什麼???

favoriteString = 'Hello everyone';
console.log(favoriteString); // Logs 'Hello everyone'
console.log(string1); // Logs 'Hello world'

呃哦....發生了什麼事。 我們無法通過更改favoriteString來更改string1 ...為什麼? 因為字符串是不可變的,我們沒有改變它。 我們所做的只是“重新分配”favoriteString到一個新的字符串。 這基本上從string1斷開它。 在前面的示例中,當我們重命名對象時,我們沒有分配任何東西。 (嗯,實際上...我們做了,我們將name屬性分配給一個新的字符串。)相反,我們只是改變了保持2個變量和底層對象之間連接的對象。

現在,繼續使用函數和傳遞參數....當你調用一個函數並傳遞一個參數時,你實際上做的是“賦值”一個新變量,它的工作原理與使用相等(=)符號。

以這些例子。

var myString = 'hello';

// Assign to a new variable (just like when you pass to a function)
var param1 = myString; 
param1 = 'world'; // Re assignment

console.log(myString); // logs 'hello'
console.log(param1);   // logs 'world'

現在,同樣的事情,但有一個功能

function myFunc(param1) {
    param1 = 'world';

    console.log(param1);   // logs 'world'
}

var myString = 'hello';
// Calls myFunc and assigns param1 to myString just like param1 = myString
myFunc(myString); 

console.log(myString); // logs 'hello'

好的,現在讓我們舉幾個使用對象的例子來代替...首先,沒有這個功能。

var myObject = {
    firstName: 'Joe',
    lastName: 'Smith'
};

// Assign to a new variable (just like when you pass to a function)
var otherObj = myObject;

// Let's mutate our object
otherObj.firstName = 'Sue'; // I guess Joe decided to be a girl

console.log(myObject.firstName); // Logs 'Sue'
console.log(otherObj.firstName); // Logs 'Sue'

// Now, let's reassign
otherObj = {
    firstName: 'Jack',
    lastName: 'Frost'
};

// Now, otherObj and myObject are assigned to 2 very different objects
// And mutating one object no longer mutates the other
console.log(myObject.firstName); // Logs 'Sue'
console.log(otherObj.firstName); // Logs 'Jack';

現在,同樣的事情,但有一個函數調用

function myFunc(otherObj) {

    // Let's mutate our object
    otherObj.firstName = 'Sue';
    console.log(otherObj.firstName); // Logs 'Sue'

    // Now let's re-assign
    otherObj = {
        firstName: 'Jack',
        lastName: 'Frost'
    };
    console.log(otherObj.firstName); // Logs 'Jack'

    // Again, otherObj and myObject are assigned to 2 very different objects
    // And mutating one object no longer mutates the other
}

var myObject = {
    firstName: 'Joe',
    lastName: 'Smith'
};

// Calls myFunc and assigns otherObj to myObject just like otherObj = myObject
myFunc(myObject);

console.log(myObject.firstName); // Logs 'Sue', just like before

好的,如果您閱讀完整篇文章,也許您現在已經更好地理解函數調用在javascript中的工作原理了。 無論是通過引用還是通過價值傳遞都沒關係......重要的是分配與突變。

每次將一個變量傳遞給一個函數時,就像是使用了相等(=)符號一樣,無論參數變量的名稱是“賦值”。

永遠記住等號(=)意味著分配。 請記住,傳遞參數給函數也意味著賦值。 它們是相同的,兩個變量的連接方式完全相同。

修改變量的唯一時間會影響不同的變量,即基礎對象發生變化時。

在對象和原語之間進行區分是毫無意義的,因為它的工作方式與您沒有函數並且使用等號分配給新變量的方式完全相同。

唯一的問題是當你傳遞給函數的變量的名字與函數參數的名字相同時。 發生這種情況時,必須將函數內部的參數看作是函數的一個私有的全新變量(因為它是)

function myFunc(myString) {
    // myString is private and does not affect the outer variable
    myString = 'hello';
}

var myString = 'test';
myString = myString; // Does nothing, myString is still 'test';

myFunc(myString);
console.log(myString); // logs 'test'

有關複製,傳遞和按價值和參考進行比較的非常詳細的解釋在本書的“JavaScript:權威指南”一書中。

在我們通過引用來處理對象和數組的主題之前,我們需要澄清一個命名法。 短語“通過引用”可以有幾個含義。 對於一些讀者來說,這個短語指的是一種函數調用技術,它允許一個函數為其參數賦值新的值,並讓這些修改後的值在函數外部可見。 這不是本書中使用該術語的方式。 在這裡,我們的意思是指一個對像或數組的引用 - 而不是對象本身 - 傳遞給一個函數。 函數可以使用該引用來修改對像或數組元素的屬性。 但是,如果函數用引用新對像或數組的方式覆蓋引用,那麼該修改在函數外部是不可見的。 熟悉這個術語其他含義的讀者可能更喜歡說對象和數組是通過值傳遞的,但傳遞的值實際上是一個引用而不是對象本身。

有一件事我仍然無法弄清楚。 檢查下面的代碼。 有什麼想法嗎?

function A() {}
A.prototype.foo = function() {
    return 'initial value';
}


function B() {}
B.prototype.bar = A.prototype.foo;

console.log(A.prototype.foo()); //initial value
console.log(B.prototype.bar()); //initial value

A.prototype.foo = function() {
    return 'changed now';
}

console.log(A.prototype.foo()); //changed now
console.log(B.prototype.bar()); //Why still 'initial value'???

該變量不“持有”該對象,它擁有一個引用。 您可以將該引用分配給另一個變量,現在都引用同一個對象。 它總是按值傳遞(即使該值是引用...)。

沒有辦法改變作為參數傳遞的變量所持有的值,如果JS支持按引用傳遞,這將是可能的。


這在Javascript中很有趣。 考慮這個例子:

function changeStuff(a, b, c)
{
  a = a * 10;
  b.item = "changed";
  c = {item: "changed"};
}

var num = 10;
var obj1 = {item: "unchanged"};
var obj2 = {item: "unchanged"};

changeStuff(num, obj1, obj2);

console.log(num);
console.log(obj1.item);    
console.log(obj2.item);

這產生輸出:

10
changed
unchanged

如果它是純粹的按值傳遞,那麼更改obj1.item將不會影響函數外部的obj1 。 如果它是純粹的通過引用,那麼一切都會改變。 num100obj2.item會讀取"changed"

相反,情況是傳入的項目是按值傳遞的。 但是通過價值傳遞的項目本身就是一個參考。 從技術上講,這被稱為call-by-sharing

實際上,這意味著如果您更改參數本身(如numobj2 ),那麼這不會影響饋送到參數中的項目。 但是,如果更改參數的INTERNALS,則會傳回(與obj1 )。


關於在JS中使用術語“通過引用”這個術語有一些討論,但要回答你的問題:

一個對象通過引用自動傳遞,而不需要專門說明它

(從上面提到的文章。)


  • When an object is created, it exists on the heap.
  • When a primitive value is established, it lives on the stack.
  • In order to use either, you must have a variable associated with it.

With an object, the variable is essentially a pointer on the stack that points to the memory location on the heap where the object is stored. With a primitive, the variable and the value are stored together on the stack.

When you pass an object variable in JavaScript, it is always passed by Reference, meaning that the a copy of the REFERENCE is passed to the callee. If the underlying object is manipulated by the callee, the original object will be modified outside the function as well.

When you pass a stack variable in JavaScript, a copy of the actual value is passed to the callee. If the callee modifies the value it receives, the value is not modified outside of the function.







pass-by-value