javascript:引用型別

南郭竽發表於2018-04-21

JavaScript讀書筆記

引用型別

引用型別的值(物件)是引用型別的一個例項。在ECMAScript中,引用型別是一種資料結構,用於將資料和功能組織在一起。它也常被成為,但是這種稱呼並不妥當。儘管ECMAScript從技術上講是一門物件導向的語言,但它不具備傳統的面嚮物件語言所支援的類和介面等基本結構。引用型別有時候也被稱為物件定義,因為它們描述的是一類物件所具有的屬性和方法。

1.x Object型別
1.1.x Object型別物件的建立方式有兩種:
  • 第一種是使用new操作符後跟Object建構函式,如下所示:
var person = new Object();
person.name = 'Stone';
person.age = 29;
  • 第二種是使用物件字面量表示法。物件字面量是物件定義的一種簡寫形式,目的在於簡化建立包含大量屬性的物件的過程。如下所示:
var person = {
    name:"Stone",
    age:29
};

使用字面量表示法更常見。

function displayInfo(args) {
    var info = "";
    if (typeof args.name === "string") {
        info += "Name: " + args.name;
    }
    if (typeof args.age === "number") {
        info += "Age: " + args.age;
    }

    console.log("arg===>" + info);
}

displayInfo({name: "張飛", age: 33}); // 引數傳入一個物件
displayInfo({name: "王菲"});
displayInfo({name: "北條麻妃", age: 38, "job": "actress"});
1.2.x 訪問物件屬性的兩種方式:

obj.attrobj["attr"],如下所示:

var slave = {"first name": "stone", "last name": "Jack", "age": 22};

console.log(slave['first name'] + " , " + slave.age);

對於有特殊字元的屬性,只能通過obj["attr"]的方式訪問了。

2.x Array型別

ECMAScript陣列與其他語言中的陣列都是資料的有序列表,但是與其他語言不同的是,ECMAScript資料的每一項可以儲存任何型別的資料。而且ECMAScript陣列的大小是可以動態調整的,即可以隨著資料的新增自動增長以容納新增資料。

2.1.x Array型別物件建立方式有兩種:
  • 第一種,通過new操作符與Array的建構函式來建立:
var arr1 = new Array(); // 建立一個空的陣列

var arr2 = new Array(3); // 建立一個 length 值為 3 的陣列
//  這種建立方式,裡面有 3 個元素,每個的值都是 undefined。

var arr3 = new Array("star","shit"); // 建立一個包含兩個字串的陣列

另外,使用這種方式建立陣列,new操作符也可以省略。

var tom = Array("rose","victor");
  • 第二種,使用陣列字面量表示法。如下所示:
var colors = ['red','blue','green'];
var names = [];
var values = [1,2,3,]; // todo: 不要這樣!這樣會建立一個包含3項或4項的陣列
var options = [,,,]; // todo: 不要這樣!這樣會建立一個包含3項或4項的陣列
2.2.x 讀取和設定陣列的值

要使用方括號並提供相應值的基於0的數字索引,如下所示:

var colors = ["red", "green", "blue"];
console.log(colors[0]); // 讀取值
colors[1] = "yellow";   // 設定值
colors[2] = "black";

陣列的length屬性不是隻讀的。因此,通過設定這個屬性,可以從陣列的末尾移除項或向陣列中新增新項。如下所示:

var colors = ["red", "blue", "green"];
colors.length = 2; // 原本是3的
console.log(colors[2]); // undefined

colors[colors.length] = "pink"; // 末尾新增一個元素
colors[colors.length] = "gray"; // 末尾再新增一個元素

陣列的騷操作:

var colors = ["red" , "blue" , "green"];

colors[99] = "pink";
console.log(colors.length); // 100

通過這種方式也可以跳躍的給陣列新增元素,在[2]-[99]之間的元素未定義,所以訪問它們,返回的都是undefined

2.3.x 判斷一個物件是不是陣列型別
if (value isinstanceof Array){
    // do something
}

instanceof操作符的問題在於,它假設單一的全域性執行環境。如果網頁中包含多個框架,那實際上就存在兩個以上不同的全域性執行環境,從而存在兩個以上不同版本的Array建構函式。如果你從一個框架向另一個框架傳入一個陣列,那麼傳入的陣列與在第二個框架中原生建立的陣列分別具有各自不同的建構函式。

為了解決這個問題,ECMAScript5新增了 Array.isArray()方法。這個方法的
目的是最終確定某個值到底是不是陣列,而不管它是在哪個全域性執行環境中建立的。

if (Array.isArray(value)){
    // do something
} // 這種判斷方式更好
2.4.x 轉換方法

每個物件都包含toString(),toLocaleString(),valueof()方法。

var colors = ["red", "green" , "blue"];
console.log(colors); // red,green,blue
console.log(colors.toString()); // red,green,blue
console.log(colors.join("|")); // red|green|blue
2.5.x 棧方法

棧是一種LIFO(last in first out)的資料結構。也就是最新新增的項最早被移除。而棧中項的插入(叫做推入)和移除(叫做彈出),只發生在一個位置–棧的頂部。ECMAScript為陣列專門提供了push()pop()方法,以便實現類似棧的行為。
push()方法可以接收任意數量的引數,把它們逐個新增到陣列末尾,並返回修改後陣列的長度。而pop()方法則從陣列的末尾移除最後一項,減少陣列的length值,然後返回移除的項。如下所示:

var colors = new Array();
var count = colors.push("red","blue");
console.log(count); // 2
count = colors.push("pink");
console.log(count); // 1
var item = colors.pop();
console.log(item); // pink
2.6.x 佇列方法

棧資料結構的訪問規則是LIFO(後進先出),而佇列資料結構的訪問規則是FIFO(first in first out 先進先出)。佇列在列表的末端新增項,從列表的前端移除項。由於push()方法是向陣列的末端新增項的方法,因此要模擬佇列只需一個從陣列前端取出項的方法。實現這一操作的陣列方式就是shift(),它能夠移除陣列的第一個項並返回該項,同時將陣列的長度-1.結合使用 shift()push()方法,就可以像使用佇列一樣使用陣列

  • 反向佇列

ECMAScript還為陣列提供了一個unshift()方法。unshift()方法與shift()的用途相反:它能在陣列的前端新增任意個項並返回新陣列的長度。因此,同時使用 unshift()pop()方法,可以從相反飛方法來模擬佇列,即在陣列的前端新增項,從陣列的末端移除項

2.7.x 重排序方法

陣列中已經存在兩個可以直接用來重排序的方法:reverse()sort()

reverse()方法用於反轉陣列。
預設情況下,sort()方法按升序排列陣列項–即,最小值在最前面,最大值在最後面。為了實現排序,sort()方法會呼叫每個陣列項的toString()轉型方法,然後比較得到的字串,以確定如何排序。即使陣列的每一項都是數值,sort()方法比較的也是字串

var values = [0 ,1 , 5, 10 ,15];
values.sort();

console.log(values); // 0,1,10,15,5

sort()方法可以接受一個比較函式作為引數,以便我們指定哪個值位於哪個值的前面

比較函式接受兩個引數,如果第一個引數應該位於第二個之前則返回一個負數,如果兩個引數相等則返回0,如果第一個引數應該位於第二個引數之後則返回一個正數。如下所示:


var stu1 = {
    name: "python", age: 80, grade: 99,
    toString: function () {
        return this.name + " , " + this.age + " , " + this.grade;
    }
};
var stu2 = {
    name: "javascript", age: 30, grade: 88, toString: function () {
        return this.name + " , " + this.age + " , " + this.grade;
    }
};
var stu3 = {
    name: "php", age: 18, grade: 59, toString: function () {
        return this.name + " , " + this.age + " , " + this.grade;
    }
};
var stu4 = {
    name: "vb", age: 50, grade: 70, toString: function () {
        return this.name + " , " + this.age + " , " + this.grade;
    }
};
var stu5 = {
    name: "shell", age: 60, grade: 70, toString: function () {
        return this.name + " , " + this.age + " , " + this.grade;
    }
};
var stu6 = {
    name: "java", age: 40, grade: 99, toString: function () {
        return this.name + " , " + this.age + " , " + this.grade;
    }
};

var langArr = [stu1, stu2, stu3, stu4, stu5, stu6];

console.log("排序前:" + langArr);
console.log("-------------------");


function compare(a, b) {
    if (a.grade > b.grade) {
        return -5;
    } else if (a.grade === b.grade) {
        return 0;
    } else {
        return 5;
    }
} // todo:簡單排序,僅僅通過分數進行排序

function cmp(a, b) {

    if (a.grade > b.grade) {
        return -5;
    } else if (a.grade === b.grade) {
        // return 0;
        if (a.age < b.age) {
            return -4;
        } else if (a.age === b.age) {
            return a.name.localeCompare(b.name);
        } else {
            return 4;
        }
    } else {
        return 5;
    }
} // todo:複雜排序,先比較分數,然後是年齡,最後是名字字串

// langArr.sort(compare);
langArr.sort(cmp);

console.log("排序後:" + langArr);
2.8.x 操作方法

ECMAScript為操作已經包含在陣列中的項提供了很多方法。

  • 其中,concat()方法可以基於當前陣列中的所有項建立一個新陣列。具體來說,這個方法會先建立當前陣列的一個副本,然後將接收到的引數新增到這個副本的末尾,最後返回新構建的陣列,在沒有給concat()方法傳遞引數的情況下,它只是複製當前陣列並返回副本。如果傳遞給concat()方法的是一個或多個陣列,則該方法會將這些陣列中的每一項都新增到結果陣列中。如果傳遞的值不是陣列,這些值就會簡單地被新增到結果陣列的末尾。如下所示:
var colors = ["red" , "green" , "blue" ];

var colors2 = colors.concat("pink" , ["white" , "black"]);

console.log(colors2.toString()); // red,green,blue,pink,white,black
  • 然後是slice()方法。它能夠基於當前陣列中的一個或多個項建立一個新的陣列slice()方法可以接受一個或兩個引數,即要返回項的起始和結束位置。但是在只有一個引數的情況下,slice()方法返回從該引數指定位置開始到當前陣列末尾的所有項。如果有兩個引數,該方法返回起始和結束位置之間的項–但是不包括結束位置的項。注意,slice()方法不會影響原來的陣列。如下所示:
var colors = ["red" , "green" , "blue" ];
var another = colors.slice(0,1);
var demo = colors.slice(1);

console.log(another.toString()); // red -->實際是 ['red'];
console.log(demo.toString()); // green,blue

如果slice()方法的引數中有一個負數,則用陣列長度加上該數來確定相應的位置。例如,在一個包含5項的陣列上呼叫slice(-2,-1)與呼叫slice(5-2,5-1)得到的結果相同。如果結束位置小於起始位置,則返回空陣列。

  • 強大的splice()方法

注意:這個方法和之前的concat() , slice()不同的是,這個方法是原地修改,修改當前陣列,而不是產生一個副本!!!!

該方法的返回值是由被刪除的項組成的陣列。

它有很多種用法。splice()的主要用途是向陣列的中部插入項,但是使用這種方法的方式則有3種。

刪除:可以刪除任意數量的項,只需指定2個引數:要刪除的第一項的位置和要刪除的項數。例如,splice(0,2)會刪除陣列中的前兩項。

插入:起始位置、0(要刪除的項數)和要插入的項。如果要插入多個項,可以再傳入第四個,第五個以至任意多個專案。例如,splice(2,0,"red","green")會從當前陣列的位置2開始插入字串"red""green".

替換:可以像指定位置插入任意數量的項,且同時可以刪除任意數量的項,只需指定3個引數:起始位置、要刪除的項數和要插入的任意數量的項。插入的項數不必與刪除的項數相等。例如:splice(2,1,"red","green")會刪除當前陣列位置2的項,然後再從位置2開始插入字串"red""green"

splice()方法始終都會返回一個陣列,該陣列中包含從原始陣列中刪除的項(如果沒有刪除任何項,則返回一個空陣列)。

var names=["python","php","perl","pascal"];
var another = names.splice(1,2,"java");
console.log(names+" ### "+another +" >>> "+(another instanceof Array));

輸出如下:

python,java,pascal ### php,perl >>> true

從輸出可以看到,將原陣列的元素從index=1開始,刪除了2個元素,並且,在index=1的位置插入了新的元素"java"

-<>- 從index=1開始刪除2個元素,然後在index=1的位置,插入"java","javascript","golang","shell"這幾個元素。

var names=["python","php","perl","pascal"];
var another = names.splice(1,2,"java","javascript","golang","shell");
console.log(names+"");

輸出如下:

python,java,javascript,golang,shell,pascal
  • 位置方法

ECMAScript5為陣列例項新增了兩個位置方法:indexOf()lastIndexOf()。這兩個方法都接收兩個引數:要查詢的項和(可選的)表示找起點位置的索引。

這兩個方法都返回要查詢的項在陣列中的位置,或者沒有找到的情況下返回-1在比較第一個引數與陣列中的每一項時,會使用全等操作符;也就是說,要求查詢的項必須嚴格相等(就像使用===一樣)。.

var names = ["python" , "golang" , "php" , "shell" , "java" ];
var posShell = names.indexOf("shell"); // 字串是基本型別,所以可以全等
var posPhp = names.indexOf("php",3); 

console.log("shell=="+posShll +" , php==" +posPhp);

var person = { name:"Tom" , age:13 };
var people = [person];
var morePeople = [{name:"Tom" , age:13 }];

console.log(people.lastIndexOf(person)+" , "+morePeople.indexOf(person));

輸出如下:

shell==3 , php==-1
0 , -1

為什麼 php===-1 ?因為是從index=3開始查詢,後面沒有"php"了。
為什麼最後一個輸出是-1 ?因為兩個數值相同的物件並不是同一個物件,並不全等。

  • 迭代方法

ECMAScript為陣列定義了5個迭代方法。每個方法都接收兩個引數:要再每一項上執行的函式和(可選的)執行該函式的作用域物件–影響this的值。傳入這些方法中的函式會接收三個引數:陣列項的值、該項在陣列中的位置和陣列物件本身。根據使用的方法不同,這個函式執行後的返回值可能會也可能不會影響訪問的返回值。以下是這5個迭代方法的作用。

every(): 對陣列中的每一項執行給定的函式,如果該函式的對每一項都返回true,則返回true

filter(): 對陣列中的每一項執行給定的函式,返回該函式會返回true的項組成的陣列。

forEach(): 對陣列中的每一項執行給定的函式,這個方法沒有返回值。

map(): 對陣列中的每一項執行給定的函式,返回每次函式呼叫的結果組成的陣列。

some(): 對陣列中的每一項執行給定的函式,如果該函式對任一項返回true,則返回true

var names = ["php" , 33 , "python" , "java" , "shell" , "golang" , 77];

function isString(item,index,array){
    return (typeof item === "string")
}

function plus(item,index,array){
    return item +"{#}";
}

console.log( names.map(plus) );

console.log( names.every(isString) );

names.forEach(console.log); // 列印 item,index, array |-->(其中item,index在不斷next)
  • 縮小方法

ECMAScript5還新增了兩個縮小陣列的方法:reduce()reduceRight()

todo:

3.x Date型別

ECMAScript中的Date型別是在早期Java中的java.util.Date類基礎上構建的。為此,Date型別使用自UTC(Coordinated Universal Time, 國際標準時間) 197011日午夜(零時)開始經過的毫秒值來保持日期。在使用這種資料儲存格式的條件下,Date型別儲存的日期能夠精確到197011日之前或之後的285 616年。

var now = new Date(); // Date

var day = Date.parse("formatStr");

Date.parse(str);解析的字串如果不是一個時間字串,會返回NAN;否則返回對應的毫秒值。

var seconds = Date.UTC(2016,6,16,8,17,25); 注意,這裡的月份是基於0的。

var now = Date.now(); // number

4.x RegExp型別

ECMAScript通過RegExp型別來支援正規表示式。使用下面類似Perl語法,就可以建立一個正規表示式。

var expression = / pattern / flags ;

其中的模式(pattern)部分可以是任何簡單或者複雜的正規表示式,可以包含字串類、限定符、分組、向前查詢以及反向引用。每個正規表示式都可以帶有一個或多個標誌(flag),用以標明正規表示式的行為。

-- `g`:`global`

-- `i` `ignore`忽略大小寫

-- `m` `multiline` 多行文字

正規表示式中的元字元必須轉義,元字元有:( [ { \ ^ $ | ) ? * + . ] }

var pat = /at/gi;

var pat = new RegExp("at","gi"); // 效果與上面的相同

var pat = /at/gi;
var check = pat instanceof RegExp;
// 輸出:true
  • RegExp例項屬性

global: 布林值,表示是否設定了g標誌。

ignoreCase: 布林值,表示是否設定了i標誌。

lastIndex: 整數,表示開始搜尋下一項的字串位置,從0算起。

multiline: 布林值,表示是否設定了m標誌。

source: 正規表示式的字串表示,按照字面量形式而非傳入建構函式中的字串模式返回。

  • RegExp例項方法

RegExp物件的主要方法是exec(),該方法是專門為捕獲組而設計的。exec()接受一個引數,即要應用模式的字串,然後返回包含第一個匹配項資訊的陣列;或者在沒有匹配項的情況下返回null。返回的陣列雖然是Array的例項,但包含兩個額外的屬性:indexinput。其中,index表示匹配項在字串中的位置,而input表示應用正規表示式的字串。在陣列中,第一項是與整個模式匹配的字串,其他項是與模式中捕獲組匹配的字串(如果模式中沒有捕獲組,則該陣列只包含一項)。如下所示:

var text = "mon and dad an baby";
var pattern = /mon( and dad( and baby)?)?/gi;
var matchs = pattern.exec(text);

RegExp物件的第二個方法是test(),它接受一個字串引數。在模式與該引數匹配的情況下返回true;否則,返回false

var text = "000-00-0000";
var pattern = /\d{3}-\d{2}-[0-9]{4}/;

var match = pattern.test(text);

console.log(match); // true
5.x Function型別

ECMAScript中的函式實際上是物件。每個函式都是Function型別的例項,而且都與其他引用型別一樣具有屬性和方法。由於函式是物件,因此函式名實際上是一個指向函式物件的指標,不會與某個函式繫結。函式通常是使用函式宣告的語法定義的,如下所示:

function sum(num1,num2) {
    return num1 + num2;
}

這與下面使用函式表示式定義函式的方式幾乎相差無幾。

var sum = function(num1 , num2){
    return num1 + num2;
};

注意:第二種方式定義函式,}後面需要新增;表示結束,就像定義其他變數時一樣。

最後,也可以通過使用Function建構函式來定義函式。Function建構函式可以接收任意數量的引數,*但是最後一個引數始終被看成是函式體,而前面的引數則列舉了新函式的引數。如下:

var sum = new Function("num1" , "num2" , "returun num1  + num2"); // 不推薦

從技術角度講,這是一個函式表示式。但是,不推薦,因為這種語法會導致解析兩次程式碼(第一次是解析常規的ECMAScript程式碼,第二次是解析傳入建構函式中的字串),從而影響效能。不過,這種語法對於理解“函式是物件,函式名是指標”的概念倒是非常直觀的。.

由於函式名是指標,所以函式名和其他變數沒有什麼不同,一個函式可以有多個名字:

var sum = function(num1 , num2){
    return num1 + num2;
}

var another = sum; // 再來一個函式名

console.log(another(1,1)); 
  • !important:沒有過載(深入理解)

將函式名想象為指標,也有助於理解為什麼ECMAScript中沒有函式過載的概念。

如下程式碼:

function add(num){
    return num + 100;
}
function add(num){
    return num + 200;
}

其實等效於下面的寫法:

var add = function(num){
    return num + 100;
}

add = function(num){
    return num + 200;
}

從第二中寫法可以明顯看出,這種操作會導致後面的函式覆蓋了前面的函式。--在建立第二個函式時,實際上覆蓋了引用第一個函式的變數add。.

  • 函式宣告與函式表示式

函式宣告與函式表示式沒有什麼本質的區別。但是,解析器在向執行環境中載入資料時,對函式宣告和函式表示式並非一視同仁。解析器會率先讀取函式宣告,並使其在執行任何程式碼之前可用(可以訪問);至於函式表示式,則必須等到解析器執行到它所在的程式碼行,才會真正被解釋執行。

使用函式宣告,程式碼正常執行

console.log(sum(1+2));

function sum(num1 , num2){
    return num1 +  num2;
}

<-> 以上程式碼完全可以正常執行。因為在程式碼開始執行之前,解析器就已經通過一個名為函式宣告提升function declaration hoisting)的過程,讀取並將該函式宣告新增到執行環境中。對程式碼求值時,JavaScript引擎在第一遍會宣告函式並將它們放在原始碼樹的頂部。所以,即使宣告函式的程式碼在呼叫它的程式碼後面,JavaScript引擎也能把函式宣告提升到頂部。

使用函式表示式,程式碼報錯!

console.log(sum(1+2));

var sum = function(num1 , num2){
    return num1 + num2;
}

以下是在測試程式碼:



console.log(sum(1, 1)); // 輸出為 2 
function sum(a, b) {

    return a + b;
}


console.log(add(3, 4));  // 這裡會報錯:TypeError: add is not a function

var add = function (a, c) {
    return a + c;
};
  • 作為值的函式

因為在ECMAScript中,函式名本身就是變數,所以函式也可以作為值在使用。也就是說,不僅可以像傳遞引數一樣把一個函式傳遞給另一個函式,而且可以將一個函式作為另一個函式的結果返回。

  • 函式內部屬性

在函式內部,有兩個特殊的物件:argumentsthis。其中,arguments是一個類陣列物件,包含著傳入函式中的所有引數。雖然arguments的主要用途是儲存函式引數,但這個物件還有一個名叫callee的屬性,該屬性是一個指標,指向擁有這個arguments物件的函式。如下階乘函式中,就有對該屬性的使用:



function factorial(num) {
    if (num <= 1) {
        return 1;
    } else {
        return num * factorial(num - 1);
    }
} // 耦合性強,內部的函式名被指定了

function factorial(num) {
    if (num <= 1) {
        return 1;
    } else {
        return num * arguments.callee(num - 1);  
        // 使用 arguments.callee 指標
    }
} // 解除這種耦合

ECMAScript中的this物件和JavaC#中的this物件大體相似。換句話說,this引用的是函式據以執行的環境物件–或者也可以說是this值(當在網頁的全域性作用域中呼叫函式時,this物件引用的就是window)。如下所示:

color1 = "red";

function printColor() {

    console.log(this.color1);
}

var obj = {color1: "black"};

printColor(); // red         # 這裡的this 就是 window 物件

obj.printColor = printColor;
obj.printColor(); // black   # 這裡的this 就是 obj 物件

ECMAScript5規範了另一個函式物件的屬性:caller。這個屬性儲存著呼叫當前函式的函式的引用,如果是在全域性作用域中呼叫當前函式,它的值為null。如下所示:


function outer() {
    console.log(arguments.callee.caller);
    inner();
}

function inner() {
    console.log(arguments.callee.caller);
}

outer(); // 輸出 null , outer

var obj = {};

obj.inner = inner;

obj.inner();  // 輸出  null 

概括來說就是,如果當前函式被另一個函式呼叫,當前函式的caller是呼叫的函式物件引用;當前函式被非函式的物件呼叫(比如這裡定義的obj,或者全域性的window),當前函式的callernull!

探索thiscalleecaller

a. 單層呼叫

// 在 網頁執行:
function outer() {
    // inner();
    console.log("arguments.callee==[" + arguments.callee + "] , this==[" + this + "] , caller==[" + arguments.callee.caller + "]");
}

function inner() {
    console.log("arguments.callee==[" + arguments.callee + "] , this==[" + this + "] , caller==[" + arguments.callee.caller + "]");
}

outer();

輸出如下:

arguments.callee==[function outer() {
    // inner();
    console.log("arguments.callee==[" + arguments.callee + "] , this==[" + this + "] , caller==[" + arguments.callee.caller + "]");
}] , this==[[object Window]] , caller==[null]

也就是說,在網頁執行上述程式碼,函式的上述三個屬性分別是:

當前函式(arguments.callee):
function outer() {
    // inner();
    console.log("arguments.callee==[" + arguments.callee + "] , this==[" + this + "] , caller==[" + arguments.callee.caller + "]");
}
-------
this物件:
[object Window]
-------
呼叫當前函式的函式物件(caller):
null
-------

b. 函式呼叫函式

// 在網頁中執行
function outer() {
    inner();
}

function inner() {
    console.log("arguments.callee==[" + arguments.callee + "] , this==[" + this + "] , caller==[" + arguments.callee.caller + "]");
}

outer();

輸出如下:

arguments.callee==[function inner() {
    console.log("arguments.callee==[" + arguments.callee + "] , this==[" + this + "] , caller==[" + arguments.callee.caller + "]");
}] , this==[[object Window]] , caller==[function outer() {
    inner();
}]

函式中呼叫函式時,被呼叫函式的三個屬性分別是:

當前函式(arguments.callee):
function inner() {
    console.log("arguments.callee==[" + arguments.callee + "] , this==[" + this + "] , caller==[" + arguments.callee.caller + "]");
}
-------
this物件:
[object Window]
-------
呼叫當前函式的函式物件(caller):
function outer() {
    inner();
}
-------

從兩次驗證可以看出:

- `this`屬性不受函式的呼叫層級的影響,表示的總是當前執行環境物件。
- `arguments.callee`屬性表示的總是當前函式物件。
- `arguments.callee.caller`屬性 表示的總是呼叫當前函式的函式物件,如果當前當前函式不是被函式呼叫,該值為`null`。

嚴格模式下:不能訪問arguments.calleearguments.callee.caller

每個函式都包含兩個非繼承而來的方法:apply()call()。這兩個方法的用途是在特定的作用域中呼叫函式,實際上等於設定函式體內的this物件的值。

首先,apply()方法接收兩個引數:一個是其中執行函式的作用域,另一個是引數陣列。其中,第二個引數可以是Array例項,也可以是arguments物件。如下:

function sum(num1 , num2) {
    return num1 + num2 ;
}

sum.apply(this,[1,2]);

function callSum(num1 , num2){
    return sum.apply(this,arguments);
}

apply()方法的實際應用場景1:

var numbers = [2, 1, 3, 6, 4, 5, 7, 0];
var max = Math.max.apply(Math, numbers);
console.log(max);
// ------ apply 的第一個引數其實可以是任意物件,因為這個物件被當做是 Math.max()函式的this 物件,
// 也就是當前函式的執行環境物件。在網頁全域性執行,應該就是window物件

var max = Math.max.apply({}, numbers); // 比如這裡直接寫了一個字面量物件 {}
console.log(max);

call()方法與apply()方法類似,只是引數不同,第一個引數依然是其中執行函式的作用域,其餘的引數都直接傳遞給函式。換句話說,在使用call()方法時,傳遞給函式的引數必須逐個列舉出來。如下:

function sum (num1 , num2){
    return num1 + num2 ;
}

sum.call(this,1,2);

sum.call(this,4,5);

apply()call()的主要用途是擴充套件函式的賴以執行的作用域。.如下:

var red = {color: "red"};
var black = {color: "black"};

function printColor() {
    console.log(this.color); // 注意,這裡用到了this
}

printColor.call(red);
// 輸出 RED
printColor.call(black);
// 輸出 BLACK

函式的bind()方法。這個方法會建立一個函式例項,其this值會繫結到傳給bind()函式的值。如下:

color = "red";
var obj = {color:"blue"};
function sayColor(){
    return this.color;
}

anotherSay = sayColor.bind(obj);

anotherSay(); // blue
sayColor(); // red
6.x 基本包裝型別(Number,String,Boolean)

Object建構函式也會像工廠方法一樣,根據傳入值的型別返回相應基本包裝型別的例項。

var obj = new Object("some text");
alert(obj instanceof String); // true

注意:轉型函式會返回基本型別的資料,而使用new呼叫建構函式,會返回一個物件。

var value = "25";
var num = Number(value);  // 轉型函式
console.log(typeof num); // "number"

var obj = new Number(value); // 建構函式
console.log(typeof obj); // "object"

Boolean

Number

String

字串的操作方法:

var str="some text";
var s = str[0]; // "s"

str.charAt(2); // "m"

str.charCodeAt(2); // 109

str.concat(" good"); // some text good

Math物件的使用:選擇隨機顏色

var colors = ["red", "blue", "green", "black", "white", "gray", "pink", "yellow"];

function chooseRandom(colors) {
    var index = Math.floor(Math.random() * colors.length);
    return colors[index];
}
console.log(chooseRandom(colors));
7.x 小結

物件在Javascript中被成為引用型別的值,而且有一些內建的引用型別可以用來建立特定的物件。

  • 引用型別與傳統物件導向程式設計中的類相似,但實現不同;
  • Object是一個基礎型別,其他所有型別都從Object繼承了基本行為;
  • Array型別是一組值的有序列表,同時還提供了操作和轉換這些值的功能;
  • Date型別提供了有關日期和時間的資訊,包括當前日期和時間以及相關的計算功能;
  • RegExp型別是ECMAScript支援正規表示式的一個介面,提供了最基本的和一些高階的正規表示式功能。
  • 函式實際上是Function型別的例項,因此函式也是物件。所有函式也擁有方法,可以增強其行為;
  • 因為有基本包裝型別,所以Javascript中基本型別的值可以被當做物件來訪問。三種基本包裝型別分別是:Boolean,Number,String
    • 每個包裝型別都對映到同名的基本型別;
    • 在讀取模式下訪問基本型別值時,就會建立對應的基本包裝型別的一個物件,從而方便了資料操作;
    • 操作基本型別值的語句一經執行完畢,就會立即銷燬新建立的包裝物件。
  • 在所以程式碼執行之前,作用域中就已經存在了兩個內建物件:GlobalMath。在大多數ECMAScript實現中都不能直接訪問Global物件;不過,Web瀏覽器實現了承擔該角色的window物件。全域性變數和函式都是Global物件的屬性。Math物件提供了很多屬性和方法,用於輔助完成複雜的數學計算任務。

相關文章