JavaScript高級程序設計(第三版)②

7 語句

? if

if (i > 25) {
    alert(" Greater than 25.");
} else if (i < 0) {
    alert(" Less than 0.");
} else {
    alert(" Between 0 and 25, inclusive.");
}

? do-while后測試循環(huán),至少運一次

do {
    statement
} while (出口條件);
var i = 0;
do {
    i += 2;
} while (i < 10);
alert(i);

? while前測試循環(huán),可能一次不運行

var i = 0;
while (i < 10) {
    i += 2;
}

? for

for (initialization; expression; post - loop - expression) {
    statement
}
var count = 10;
for (var i = 1; i < count; i++) {
    alert(i);
}

?注意表達式用;號結束,初始化表達式、控制表達式和循環(huán)后表達式都是可選的,將這些表達式全部省略,就會創(chuàng)建一個無限循環(huán):

for (;;) {
    // 無限 循環(huán) doSomething(); 
}

? for-in
?for-in語句是一種精準的迭代語句,可以用來枚舉對象的屬性,在ECMAScript對象的屬性是沒有順序的。

for (var propName in window) {
    document.write(propName);
}

? label

start: for (var i = 0; i < count; i++) {
    alert(i);
}

?主要作用:在嵌套循環(huán)中使用break、continue配合label可以精確地返回想要的位置:

var num = 0;
for (var i = 0; i < 10; i++) {
    for (var j = 0; j < 10; j++) {
        if (i == 5 && j == 5) {
            break;
        }
        num++;
    }
}
alert(num); //循環(huán)在i為5,j為5的時候跳出j循環(huán),但會繼續(xù)執(zhí)行i循環(huán),輸出95

?添加Label之后:

var num = 0;
outPoint:
for (var i = 0; i < 10; i++) {
    for (var j = 0; j < 10; j++) {
        if (i == 5 && j == 5) {
            break outPoint;
        }
        num++;
    }
}
alert(num); //循環(huán)在i為5,j為5的時候跳出雙循環(huán),返回到outPoint層,執(zhí)行下面的語句,輸出55

? breakcontinue

var num = 0;
for (var i = 1; i < 10; i++) {
    if (i % 5 == 0) {
        break;
    }
    num++;
}
alert(num); //4

?當i等于5,直接跳出循環(huán),num加到了4。

var num = 0;
for (var i = 1; i < 10; i++) {
    if (i % 5 == 0) {
        continue;
    }
    num++;
}
alert(num); //8

?當i等于5,會跳出來再從循環(huán)頭部重新以i等于5開始,再到10的時候再跳一次,此時i < 10已經(jīng)不成立了,不進入循環(huán),等到8。

? with不建議使用

var qs = location.search.substring(1);
var hostName = location.hostname;
var url = location.href;
/* 使用with簡化后 */
with(location) {
    var qs = search.substring(1);
    var hostName = hostname;
    var url = href;
}

? switch

switch (i) {
    case 25:
        alert(" 25");
        break;
    case 35:
        alert(" 35");
        break;
    case 45:
        alert(" 45");
        break;
    default:
        alert(" Other");
}

?在語句里break;得加上,防止發(fā)生同時執(zhí)行多個case語句。
?JS的switch語句能帶任意數(shù)據(jù)類型,甚至是對象:

switch ("hello world") {
    case "hello" + " world":
        alert(" Greeting was found.");
        break;
    case "goodbye":
        alert(" Closing was found.");
        break;
    default:
        alert(" Unexpected message was found.");
}

8 函數(shù)

? 寫法

function sayHi(name, message) {
    alert(" Hello " + name + "," + message);
}
sayHi(" Nicholas", "how are you today?");

?return,實際上,沒有指定返回值的函數(shù)返回的是一個特殊的undefined值:

function sayHi(name, message) {
    return;
    alert(" Hello " + name + "," + message); //永遠不會調(diào)用 
}
function sum(num1, num2) {
    return num1 + num2;
}
var result = sum(5, 10);
function diff(num1, num2) {
    if (num1 < num2) {
        return num2 - num1;
    } else {
        return num1 - num2;
    }
}

? 傳遞參數(shù)有無,數(shù)據(jù)類型任意都可,原因是ECMAScript中的參數(shù)在內(nèi)部是用一個數(shù)組來表示的;還通過arguments可以查看傳進的參數(shù):

function doAdd() {
    if (arguments.length == 1) {
        alert(arguments[0] + 10);
    } else if (arguments.length == 2) {
        alert(arguments[0] + arguments[1]);
    }
}
doAdd(10); //20 
doAdd(30, 20); //50

?還可以同時使用傳參與argumentsarguments[0]對應num1

function doAdd(num1, num2) {
    if (arguments.length == 1) {
        alert(num1 + 10);
    } else if (arguments.length == 2) {
        alert(arguments[0] + num2);
    }
}

?給傳參賦值:

function doAdd(num1, num2) {
    arguments[1] = 10;
    alert(arguments[0] + num2);
}

?還有些要注意的事情,例如,如果只給doAdd()函數(shù)傳遞了一個參數(shù),則num2中就會保存undefined值。這種賦值方法在嚴格模式下不起作用。

? 沒有重載:

function addSomeNumber(num) {
    return num + 100;
}

function addSomeNumber(num) {
    return num + 200;
}
var result = addSomeNumber(100); //300 FunctionExample10.

?不過可以用檢查arguments的方法,模仿重載,讓一個函數(shù)有兩個定義。

9 變量、作用域與內(nèi)存問題

? 變量 ★
?ECMAScript變量可能包含兩種不同數(shù)據(jù)類型的值:基本類型值引用類型值;基本類型是按值訪問的,因為可以操作保存在變量中的實際的值;引用類型的值是保存在內(nèi)存中的對象。與其他語言不同,JavaScript不允許直接訪問內(nèi)存中的位置,也就是說不能直接操作對象的內(nèi)存空間。在操作對象時,實際上是在操作對象的引用而不是實際的對象。為此,引用類型的值是按引用訪問的。
?引用類型與基本類型的差異:
?1> 動態(tài)賦值:

var person = new Object();
person.name = "Nicholas";
alert(person.name); //"Nicholas"
var name = "Nicholas";
name.age = 27;
alert(name.age); //undefined

?2> 復制變量值:

var num1 = 5;
var num2 = num1;
num1 = 4;
alert(num2); //5
var obj1 = new Object();
var obj2 = obj1;
obj1.name = "Nicholas";
alert(obj2.name); //"Nicholas"

?3> 作為傳參時,函數(shù)的參數(shù)都是按值傳遞的

function addTen(num) {
    num += 10;
    return num;
}
var count = 20;
var result = addTen(count);
alert(count); //20,不影響作為傳參的count
alert(result); //30
function setName(obj) {
    obj.name = "Nicholas";
}
var person = new Object();
setName(person);
alert(person.name); //"Nicholas"
function setName(obj) {
    obj.name = "Nicholas";
    obj = new Object();
    obj.name = "Greg";
    /* 在函數(shù)里重寫obj時,obj會指向一個新的Object,而且這個變量引用的是一個局部變量,會在函數(shù)執(zhí)行完后被銷毀 */
}
var person = new Object();
setName(person);
alert(person.name); //"Nicholas"

?3> 檢測變量類型:
?typeof操作符是確定一個變量是字符串、數(shù)值、布爾值,還是undefined的最佳工具。
?instanceof操作符用于檢查對象是何類型,返回布爾值:

alert(person instanceof Object); // 變量 person 是 Object 嗎? 
alert(colors instanceof Array); // 變量 colors 是 Array 嗎? 
alert(pattern instanceof RegExp); // 變量 pattern 是 RegExp 嗎?

? 執(zhí)行環(huán)境與作用域 ★:
?1> ES6之前只有全局作用域與函數(shù)作用域;
?2> 每個執(zhí)行環(huán)境都有一個與之關聯(lián)的變量對象(variableobject),環(huán)境中定義的所有變量和函數(shù)都保存在這個對象中;
?3> 每個函數(shù)都有自己的執(zhí)行環(huán)境。當執(zhí)行流進入一個函數(shù)時,函數(shù)的環(huán)境就會被推入一個環(huán)境棧中。而在函數(shù)執(zhí)行之后,棧將其環(huán)境彈出,把控制權返回給之前的執(zhí)行環(huán)境。ECMAScript程序中的執(zhí)行流正是由這個方便的機制控制著;
?4> 當代碼在一個環(huán)境中執(zhí)行時,會創(chuàng)建變量對象的一個作用域鏈,作用域鏈的前端始終是當前代碼執(zhí)行的變量對象,鏈子上后一個的變量對象是包含當前變量對象的變量對象,以此類推,自至到全局執(zhí)行環(huán)境;

var color = "blue";

function changeColor() {
    var anotherColor = "red";

    function swapColors() {
        var tempColor = anotherColor;
        anotherColor = color;
        color = tempColor;
        /* 這里可以訪問color、anotherColor和tempColor */
    }
    /* 這里可以訪問color和anotherColor,但不能訪問tempColor */
    swapColors();
}
/* 這里只能訪問color */
changeColor();

?5> 有些語句可以在作用域的前端臨時增加一個變量對象,比如with語句和try-catch語句的catch塊,前者把填入的變量對象增加到作用域的前端,后者把錯誤對象添加到前端。
?6> 在ES6之前,JavaScript沒有塊級作用域;在其他類型的語言中,連由花括號封閉的代碼塊都有自己的作用域。
?7> 使用var聲明的變量會自動被添加到最接近的環(huán)境中。在函數(shù)內(nèi)部,最接近的環(huán)境就是函數(shù)的局部環(huán)境;在with語句中,最接近的環(huán)境是函數(shù)環(huán)境。如果初始化變量時沒有使用var聲明,該變量會自動被添加到全局環(huán)境。

? 垃圾收集
?JS具有自動垃圾回收機制,通過以下幾種方法去判斷是否可以回收:
?1> 標記清除(mark-and-sweap),最常用的,當代碼執(zhí)行時,會將已存儲在內(nèi)存中的所有變量加上標記,然后每每進入一個環(huán)境都會去掉環(huán)境中的變量和被環(huán)境中的變量引用的變量的標記,然后其余的變量都被視為準備刪除的變量;
?2> 引用計數(shù)(reference counting),不太常見,當聲明一個變量并將一個引用類型值賦給該變量時,則這個值的引用次數(shù)就是1。如果同一個值又被賦給另一個變量,則該值的引用次數(shù)加1。相反,如果包含對這個值引用的變量又取得了另外一個值,則這個值的引用次數(shù)減1。當這個值的引用次數(shù)變成0時,會在垃圾回收機制觸發(fā)時被銷毀;
?3> 垃圾回收機制觸發(fā)靠的是判斷是否達到動態(tài)修正的一個瀏覽器內(nèi)存分配量的臨界值,也可以手動觸發(fā),比如IE的window.CollectGarbage()。

?④ 手動內(nèi)存管理,太騷氣了...
?1> 把值設置為null來釋放其引用:

function createPerson(name) {
    var localPerson = new Object();
    localPerson.name = name;
    return localPerson;
}
var globalPerson = createPerson(" Nicholas");
globalPerson = null; // 手動解除globalPerson的引用

10 引用類型

? 對象類型:
?1> 創(chuàng)建Object實例的兩種方法:


var person = new Object();
person.name = "Nicholas";
person.age = 29;
var person = {
    name: "Nicholas",
    age: 29
};
var person = {}; //與new Object()相同 
person.name = "Nicholas";
person.age = 29;

?2> 關于對象字面量語法,我們推薦只在考慮對象屬性名的可讀性時使用。
?3> 使用對象字面量作為參數(shù)傳遞也是向函數(shù)傳遞大量可選參數(shù)的首選方法,如下:

function displayInfo(args) {
    var output = "";
    if (typeof args.name == "string") {
        output += "Name: " + args.name + "\n";
    }
    if (typeof args.age == "number") {
        output += "Age: " + args.age + "\n";
    }
    alert(output);
}
displayInfo({
    name: "Nicholas",
    age: 29
});
displayInfo({
    name: "Greg"
});

?4> 訪問對象屬性的兩種方法:

alert(person["name"]); //"Nicholas" 

?等價于:

alert(person.name); //"Nicholas"

?等價于:

var propertyName = "name";
alert(person[propertyName]); //"Nicholas"

?如果對象屬性名帶有空格,那只能用方括號表示法:

person.first name = "Nicholas";// 點表示法不能帶空格,會保錯
person["first name"] = "Nicholas";// 方括號表示法可以

?② Array類型 ★

var colors = new Array(3); // 創(chuàng)建一個包含3項的數(shù)組var 
names = new Array(" Greg"); // 創(chuàng)建一個包含1項,即字符串"Greg"的數(shù)組

?1> Array類型也有數(shù)組字面量表示法:

var colors = ["red", "blue", "green"]; // 創(chuàng)建一個包含3個字符串的數(shù)組 
var names = []; // 創(chuàng)建一個空數(shù)組
var values = [1, 2,]; // 不要這樣!這樣會創(chuàng)建一個包含2或3項的數(shù)組
var options = [,,,,,]; // 不要這樣!這樣會創(chuàng)建一個包含5或6項的數(shù)組

?2> 與對象一樣,在使用數(shù)組字面量表示法時,也不會調(diào)用Array構造函數(shù),會節(jié)約性能
?3> 讀取和修改數(shù)組

var colors = ["red", "blue", "green"]; // 定義一個符串數(shù)組 
alert(colors[0]); // 顯示第一項 
colors[2] = "black"; // 修改第三項 
colors[3] = "brown"; // 新增第四項
var colors = ["red", "blue", "green"]; // 創(chuàng)建一個包含3個字符串的數(shù)組 
var names = []; // 創(chuàng)建一個空數(shù)組
alert(colors.length); // 3 
alert(names.length); // 0

?4> 通過length屬性可以去訪問數(shù)組末尾項,可以新增、修改和刪除:

var colors = ["red", "blue", "green"]; // 創(chuàng)建一個包含3個字符串的數(shù)組 
colors.length = 2; // 把數(shù)組長度變?yōu)?,變相刪除末尾項
alert(colors[2]); //undefined
var colors = ["red", "blue", "green"]; // 創(chuàng)建一個包含3個字符串的數(shù)組
colors[colors.length] = "black"; //(在位置3)添加一種顏色
colors[colors.length] = "brown"; //(在位置4)再添加一種顏色

?5> 檢測數(shù)組

if (Array.isArray(value)) {
    // 判斷是否為數(shù)組
    // 對數(shù)組執(zhí)行某些操作
}

?6> 如前所述,所有對象都具有toLocaleString()、toString()valueOf()方法,對數(shù)組應用這些方法時,toString()valueOf()得到的值是相同的字符串,實際上,為了創(chuàng)建這個字符串會調(diào)用數(shù)組每一項的toString()方法,alert輸出數(shù)組也會在后臺調(diào)用toString()

var colors = ["red", "blue", "yellow"];
alert(colors.toString()); // red,blue,yellow
alert(colors.valueOf()); // red,blue,yellow
alert(colors); // red,blue,yellow

?toLocaleString()有時候會返回和用toString()、valueOf()方法不一樣的值,因為toLocaleString()是對數(shù)組每一項調(diào)用toLocaleString()方法。我們在數(shù)組里重寫toLocaleString()toString()測試下:

var person1 = {
    toLocaleString: function() {
        return "Nikolaos";
    },
    toString: function() {
        return "Nicholas";
    }
};
var person2 = {
    toLocaleString: function() {
        return "Grigorios";
    },
    toString: function() {
        return "Greg";
    }
};
var people = [person1, person2];
alert(people); //Nicholas, Greg 
alert(people.toString()); //Nicholas, Greg 
alert(people.toLocaleString()); //Nikolaos, Grigorios

?關于join()方法,用于重現(xiàn)toString(),代入的參數(shù)是什么分隔符,就可以得到什么樣的字符串:

var colors = ["red", "green", "blue"];
alert(colors.join(",")); // red,green,blue 
alert(colors.join("||")); // red||green||blue

?7> 數(shù)組還有種棧方法可以讓數(shù)組的行為表現(xiàn)得像棧(后進先出)一樣,方法有兩:push()(接收任意數(shù)量的參數(shù),把它們逐一添加到數(shù)組末尾,再返回修改后的數(shù)組長度)和pop()(移除數(shù)組最后一項,數(shù)組長度減一,)。

var colors = new Array(); // 創(chuàng)建一個數(shù)組
var count = colors.push(" red", "green"); // 推入兩項 
alert(count); // 2 
count = colors.push(" black"); // 推入另 一項 
alert(count); // 3 
var item = colors.pop(); // 取得最后一項 
alert(item); // "black" 
alert(colors.length); // 2

?8> 數(shù)組還還有種隊列方法可以讓數(shù)組的行為表現(xiàn)得像隊列(先進先出)一樣,方法有三:push()(因為隊列和棧都是從列表的末端添加項)、shift()(移除數(shù)組的第一項,返回該項,同時長度減一),unshift()(往數(shù)組的頭部添加任意項,返回數(shù)組長度):

var colors = new Array(); // 創(chuàng)建一個數(shù)組 
var count = colors.unshift(" red", "green"); // 推入兩項 
alert(count); // 2
count = colors.unshift(" black"); // 推入另一項 
alert(count); //3 
var item = colors.pop(); // 取得最后一項 
alert(item); //"green" 
alert(colors.length); // 2

?注意上面輸出green,表明unshift()推入項的順序

?9> reverse()sort(),reverse()用來反轉數(shù)組項的順序,sort()更加靈活,可以按升序排列數(shù)組,最小的在前面,最大的排在后面,sort()方法會對數(shù)組每個項調(diào)用toString(),再比較它的字符串,即便它是數(shù)值

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

?由于比較的是字符串,所以, 不一定返回我們想當然的結果,就像上面的例子,"5"在"15"和"10"前面,為了得到準確的結果,可以往sort()里加一個比較函數(shù),比較函數(shù)接受兩個參數(shù),返回結果有3種,如果第一個參數(shù)應該在第二個參數(shù)后面,返回正數(shù);如果在前面,返回負數(shù);兩數(shù)相等,返回0。示例如下:

function compare(value1, value2) {
    if (value1 < value2) {
        return -1;
    } else if (value1 > value2) {
        return 1;
    } else {
        return 0;
    }
}

代入sort()后:

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

?10> concat(),可以往原有數(shù)組里加參數(shù)變成新的數(shù)組,并返回新數(shù)組:

var colors = ["red", "green", "blue"];
var colors2 = colors.concat(" yellow", ["black", "brown"]);
alert(colors); // red, green, blue 
alert(colors2); // red, green, blue, yellow, black, brown

?11> slice(),用原有數(shù)組的項組成新的數(shù)組,并返回新數(shù)組,傳入的兩個參數(shù)為起始位置與終止位置:

var colors = ["red", "green", "blue", "yellow", "purple"];
var colors2 = colors.slice(1); //不傳入終止位置,默認為數(shù)組的長度-1
var colors3 = colors.slice(1, 4);
alert(colors2); //green, blue, yellow, purple 
alert(colors3); //green, blue, yellow

?12> splice(),超常用,始終返回一個包含被刪除項的數(shù)組 ★

var colors = ["red", "green", "yellow", "blue", "black"];
colors.splice(0, 1);
alert(colors); // green,yellow,blue,black
colors.splice(0, 2);
alert(colors); // blue,black
colors.splice(0, 0, "red", "green");
alert(colors); // red,green,blue,black
colors.splice(2, 0, "yellow");
alert(colors); // red,green,yellow,blue,black
var remove = colors.splice(1, 2, "green1", "yellow1");
alert(colors); // red,green1,yellow1,blue,black
alert(remove); // green,yellow

?13> indexOf()(從前面往后找)和lastIndexOf()(從后面找起)用于查找特定項在數(shù)組中的位置,后臺比較用的是===

var colors = ["red", "green", "yellow", "red", "blue", "black"];
alert(colors.indexOf("red")); // 0
alert(colors.lastIndexOf("red")); // 3
alert(colors.lastIndexOf("white")); // -1


var ball = {
    color: "white"
}
var ball1 = {
    color: "black"
}
var balls = [{
    color: "white"
}];
alert(balls.indexOf(ball)); // -1
var balls1 = [ball1, ball];
alert(balls1.indexOf(ball)); // 1

?14> 迭代方法:every() filter() forEach() map() some(),每個方法都接受兩個參數(shù),一是對數(shù)組每一項都運行的函數(shù),二是該函數(shù)的作用域(會影響this的值,該參數(shù)可選用),另外作為參數(shù)的函數(shù)要接收這三個參數(shù),數(shù)組項的值、該項在數(shù)組中的位置和數(shù)組對象本身:★
?every() some(),返回布爾值:

var numbers = [1, 2, 3, 4, 5, 6];
alert(numbers.every(function(item, index, array) {
    return (item > 3);
}));
alert(numbers.some(function(item, index, array) {
    return (item > 3);
}));

?filter(),返回符合條件項組成的數(shù)組;map(),返回被傳入函數(shù)操作過后的每一項組成的數(shù)組:

var numbers = [1, 2, 3, 4, 5, 6];
alert(numbers.filter(function(item, index, array) {
    return (item > 3);
}));
alert(numbers.map(function(item, index, array) {
    return (item + 3);
}));

?forEach(),沒有返回值,只是對每一項運行傳入?yún)?shù):

var numbers = [1, 2, 3, 4, 5, 6];
var sum = 0;

function foo(item, index, array) {
    sum += item;
}
numbers.forEach(foo);
alert(sum); // 21

?15> reduce() reduceRight(),這兩個方法都會迭代數(shù)組的所有項,然后構建一個最終返回的值;方法接受兩個參數(shù):傳入的函數(shù)(接收4個參數(shù),前一個值、當前值、項的索引和數(shù)組對象,而且函數(shù)返回的值會作為下一項的第一個參數(shù))和(可選的)作為縮小基礎的初始值。
?第一次迭代反生在數(shù)組的第二項,因此第一個參數(shù)是數(shù)組的第一項,第二個參數(shù)就是數(shù)組的第二項:

var numbers = [1, 2, 3, 4, 5, 6];

function bar(prev, cur, index, array) {
    return prev + cur;
}
alert(numbers.reduce(bar));// 21

?reduceRight()只是換了個方向的reduce()方法,從數(shù)組后面開始迭代:

? Date類型
?Date類型使用自UTC(CoordinatedUniversalTime,國際協(xié)調(diào)時間)1970年1月1日午夜(零時)開始經(jīng)過的毫秒數(shù)來保存日期。

var now = new Date(); //不傳入?yún)?shù),默認返回當前時間

?1> Date.parse(),返回轉換后的毫秒數(shù):

var someDate = new Date(Date.parse("july 29, 2019"));
var someDate1 = new Date("july 29, 2019"); // 后臺調(diào)用Date.parse,等同于上一行

?2> Date.UTC()

// GMT 時間 2000 年 1 月 1 日 午夜 零時 
var y2k = new Date(Date.UTC(2000, 0));
// GMT 時間 2005 年 5 月 5 日 下午 5: 55: 55 
var allFives = new Date(Date.UTC(2005, 4, 5, 17, 55, 55));

// 本地 時間 2000 年 1 月 1 日 午夜 零時 
var y2k = new Date(2000, 0); // 如果第一個是年份,后臺調(diào)用Date.UTC
// 本地 時間 2005 年 5 月 5 日 下午 5: 55: 55 
var allFives = new Date(2005, 4, 5, 17, 55, 55);

?3> 用于分析代碼運行時間:

//取得開始時間 
var start = Date.now();
//調(diào)用函數(shù) 
doSomething();
//取得停止時間 
var stop = Date.now(),
    result = stop– start;

?4> 與其他引用類型一樣,Date類型也重寫了toLocaleString()toString()valueOf()方法,還有一些把日期格式格式化成特定字符串方法,如toDateString() toTimeString() toLocalDateString() toLocalTimeString() toUTCStringtoDateString()toString()是一樣的:

var now = new Date();
alert(now.toString()); // Mon Jul 29 2019 21:08:54 GMT+0800 (中國標準時間)
alert(now.toUTCString()); // Mon, 29 Jul 2019 13:08:54 GMT
alert(now.toLocaleDateString()); // 2019/7/29
alert(now.toTimeString()); // 21:08:54 GMT+0800 (中國標準時間)
alert(now.toLocaleTimeString()); // 下午9:08:54
alert(now.toDateString()); // Mon Jul 29 2019

?還有一些別的格式化方法,如下:


? RegExp類型

var expression = / pattern / flags;  

?1> pattern部分是正則表達式,flags為標識,用以標明正則表達式的行為;構建方法也有兩種,但其實都會創(chuàng)建一個RegExp實例;實例有著內(nèi)置屬性;元字符需要轉義:

var pattern1 = /\[bc\] at/i;
alert(pattern1.global); //false 
alert(pattern1.ignoreCase); //true 
alert(pattern1.multiline); //false 
alert(pattern1.lastIndex); //0 
alert(pattern1.source); //"\[bc\] at" 

var pattern2 = new RegExp("\\[ bc\\] at", "i");
alert(pattern2.global); //false 
alert(pattern2.ignoreCase); //true 
alert(pattern2.multiline); //false 
alert(pattern2.lastIndex); //0 
alert(pattern2.source); //"\[bc\] at"

?2> exec()是RegExp實例常用的方法:

var text = "I am a rookie";
var pattern = /I( am( a rookie)?)?/gi; // ?表示匹配前面的子表達式零次或一次
var matches = pattern.exec(text);
alert(matches[0]); // I am a rookie
alert(matches[1]); //  am a rookie
alert(matches[2]); //  a rookie

?捕獲組可以通過從左到右計算其開括號來編號 。例如,在表達式 (A)(B(C)) 中,存在四個這樣的組:


?在g標識符下每次調(diào)用exec()都會返回字符串中的下一個匹配項,直至搜索到字符串末尾為止,lastIndex的值也會在每次調(diào)用exec()后都會增加:

var text = "cat, bat, sat, fat";
var pattern1 = /.at/;
var matches = pattern1.exec(text);
alert(matches.index); //0 
alert(matches[0]); //cat 
alert(pattern1.lastIndex); //0 

matches = pattern1.exec(text);
alert(matches.index); //0 
alert(matches[0]); //cat 
alert(pattern1.lastIndex); //0 

var pattern2 = /.at/g;
var matches = pattern2.exec(text);
alert(matches.index); //0 
alert(matches[0]); //cat 
alert(pattern2.lastIndex); //0 

matches = pattern2.exec(text);
alert(matches.index); //5 
alert(matches[0]); //bat 
alert(pattern2.lastIndex); //8

?2> test()

var text = "000-00-0000";
var pattern = /\d{3}-\d{2}-\d{4}/;
if (pattern.test(text)) {
    alert(" The pattern was matched.");
}

?2> toString()toLocalString()

var pattern = new RegExp("\\[bc\\]at","gi");
alert(pattern.toString()); // /\[bc\]at/gi 
alert(pattern.toLocaleString()); // /\[bc\]at/gi

?④ Function類型 ★
?1> 每個函數(shù)都是Function類型的實例,函數(shù)名是指向?qū)嵗瘮?shù)指針,ECMAScript中沒有函數(shù)重載的概念,定義方法如下:

function sum(num1, num2) {
    return num1 + num2;
}
var sum = function(num1, num2) {
    return num1 + num2;
};
function sum(num1, num2) {
    return num1 + num2;
}
alert(sum(10, 10)); //20 
var anotherSum = sum;
alert(anotherSum(10, 10)); //20 
sum = null;
alert(anotherSum(10, 10)); //20

?2> 函數(shù)聲明會被解析器提升到源代碼樹的頂部,至于函數(shù)表達式var foo = new function(){...},則必須等到解析器執(zhí)行到它所在的代碼行,才會真正被解釋執(zhí)行:

alert(sum(10, 10));// 20
function sum(num1, num2) {
    return num1 + num2;
}

?3> 函數(shù)作為函數(shù)的參數(shù):

function callSomeFunction(someFunction, someArgument) {
    return someFunction(someArgument);
}

function add(item) {
    return item + 10;
}
alert(callSomeFunction(add, 10));
function createComparisonFunction(propertyName) {
    return function(object1, object2) {
        var value1 = object1[propertyName];
        var value2 = object2[propertyName];
        if (value1 < value2) {
            return -1;
        } else if (value1 > value2) {
            return 1;
        } else {
            return 0;
        }
    };
}

var data = [{
    name: "Zachary",
    age: 28
}, {
    name: "Nicholas",
    age: 29
}];
data.sort(createComparisonFunction(" name"));
alert(data[0].name); //Nicholas
data.sort(createComparisonFunction(" age"));
alert(data[0].name); //Zachary

?4> 函數(shù)內(nèi)部屬性 ★
?在函數(shù)內(nèi)部,有兩個特殊的對象:
?arguments(類數(shù)組對象,包含著傳入函數(shù)中的所有參數(shù)),其中arguments.callee屬性指向擁有這個argument的函數(shù):

function factorial(num) {
    if (num <= 1) {
        return 1;
    } else {
        return num * factorial(num - 1)
    }
}

?為了消除與函數(shù)名factorial的耦合,改寫后:

function factorial(num) {
    if (num <= 1) {
        return 1;
    } else {
        return num * arguments.callee(num - 1)
    }
}
var trueFactorial = factorial;
factorial = function() {
    return 0;
};
alert(trueFactorial(5)); //120
alert(factorial(5)); //0

?arguments.callee.caller提供更好的解耦,它保存著調(diào)用當前函數(shù)的函數(shù)的引用:

function outer() {
    inner();
}

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

?this(引用的是函數(shù)據(jù)以執(zhí)行的環(huán)境對象,當在網(wǎng)頁的全局作用域中調(diào)用函數(shù)時,this對象引用的就是window):

window.color = "red";
var o = {
    color: "blue"
};

function sayColor() {
    alert(this.color);
}
sayColor(); // red
o.sayColor = sayColor; // 添加sayColor方法給o
o.sayColor(); // blue

?length,表示函數(shù)希望接收的參數(shù)個數(shù):

function sayName(name) {
    alert(name);
}

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

function sayHi() {
    alert(" hi");
}
alert(sayName.length); //1 
alert(sum.length); //2
alert(sayHi.length); //0

?prototype點擊

?5> 函數(shù)內(nèi)的方法 ★
?apply(),內(nèi)置于函數(shù),用于在特定的作用域調(diào)用函數(shù);接受兩個參數(shù),一個是在其中運行函數(shù)的作用域,另一個是參數(shù)數(shù)組:

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

function callSum1(num1, num2) {
    return sum.apply(this, arguments); // 傳入arguments對象 
}

function callSum2(num1, num2) {
    return sum.apply(this, [num1, num2]); // 傳入數(shù)組 
}
alert(callSum1(10, 10)); //20
alert(callSum2(10, 10)); //20

?call()apply()的作用一樣,區(qū)別在于接收的參數(shù)不同,除了this值,其余的參數(shù)都得一個個列出來:

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

function callSum(num1, num2) {
    return sum.call(this, num1, num2);
}
alert(callSum(10, 10)); //20

?call()apply()真正強大的地方是使對象不需要與方法有任何耦合關系,比如修改之前那個例子,不用再把sayColor()方法復制給o

  window.color = "red";
var o = {
    color: "blue"
};

function sayColor() {
    alert(this.color);
}
sayColor(); // red
sayColor.call(this); // red 
sayColor.call(window); // red 
sayColor.call(o); // blue

?bind()這個方法會創(chuàng)建一個函數(shù)的實例,這個函數(shù)的this值會被綁定到所傳入函數(shù):

window.color = "red";
var o = {
    color: "blue"
};

function sayColor() {
    alert(this.color);
}
var objectSayColor = sayColor.bind(o);
objectSayColor(); //blue

?⑤ 相似的基本包裝類型:Boolean、Number和String ★
?1> 實際上,每當讀取一個基本類型值的時候,后臺就會創(chuàng)建一個對應的基本包裝類型的對象實例,從而讓我們使用一些方法去操縱數(shù)據(jù),而且每次使用完都會銷毀這個實例:

var s1 = "some text";
var s2 = s1.substring(2);
var s1 = "some text";
s1.color = "red";// 運行結束后,實例對象被銷毀
alert(s1.color); //undefined,因為這個是新建的實例,不帶color屬性

?等同于:

var s1 = new String(" some text");
var s2 = s1.substring(2);
s1 = null;,

?2> Object構造函數(shù)也會像工廠方法一樣,根據(jù)傳入值的類型返回相應基本包裝類型的實例:

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

?2> 使用new調(diào)用基本包裝類型的構造函數(shù),與直接調(diào)用同名的轉型函數(shù)是不一樣的:

var value = "25";
var number = Number(value); //轉型函數(shù),把字符串轉為數(shù)值,number保存的是數(shù)值
alert(typeof number); //"number" 
var obj = new Number(value); //構造函數(shù),保存的是實例
alert(typeof obj); //"object"

?3> 這些類型都重寫了valueOf(),toString(),toLocalString()方法。

?4> Boolean類型需要注意的是,Boolean類型實例與bool值的區(qū)別:

var falseObject = new Boolean(false);
var result = falseObject && true;
alert(result); //true,因為所有對象轉化成bool值是true

var falseValue = false;
result = falseValue && true;
alert(result); //false

?5> Number類型

var numberObject = new Number(10);

var num = 10;
alert(num.toString()); //"10" 
alert(num.toString(2)); //"1010"
alert(num.toString(8)); //"12" 
alert(num.toString(10)); //"10" 
alert(num.toString(16)); //"a"

alert(num.toFixed(2)); //"10.00",toFixed會按照參數(shù)返回指定小數(shù)點位的數(shù)值
var num1 = 10.005;
alert(num1.toFixed(2)); //"10.01",還可以四舍五入

alert(num.toExponential(1)); //"1.0e+1",e表示法

// toPrecision方法返回參數(shù)指定位數(shù)的數(shù)組,還可以自動選擇最合適的表達方式
var num3 = 99;
alert(num3.toPrecision(1)); //"1e+ 2",一位數(shù)表達99的表達法
alert(num3.toPrecision(2)); //"99" 
alert(num3.toPrecision(3)); //"99. 0"
var numberObject = new Number(10);
var numberValue = 10;
alert(typeof numberObject); //"object" 
alert(typeof numberValue); //"number" 
alert(numberObject instanceof Number); //true 
alert(numberValue instanceof Number); //false

?6> String類型

var stringValue = "hello world";
alert(stringValue.charAt(1)); //"e"
alert( stringValue. charCodeAt( 1)); //輸出" 101",101是小寫e的字符編碼
alert(stringValue[1]); //"e"
var stringValue = "hello ";
var result = stringValue.concat(" world");
alert(result); //"hello world" 
alert(stringValue); //"hello"

?字符操作方法(返回新字符串),slice()substring()substr()

var stringValue = "hello world";
alert(stringValue.slice(3)); //"lo world" 
alert(stringValue.substring(3)); //"lo world" 
alert(stringValue.substr(3)); //"lo world"
alert(stringValue.slice(3,7)); //"lo w" ,第一個參數(shù)都是起始位置
alert(stringValue.substring(3,7)); //"lo w",slice()和substring()的第二個參數(shù)指定的是子字符串最后一個字符的位置 
alert(stringValue.substr(3,7)); //"lo worl",第二個參數(shù)指定的是返回的字符個數(shù)
var stringValue = "hello world";
alert(stringValue.slice(-3)); //"rld"
alert(stringValue.substring(-3)); //"hello world" 
alert(stringValue.substr(-3)); //"rld" 
alert(stringValue.slice(3,-4)); //"lo w" 
alert(stringValue.substring(3,-4)); //"hel" 
alert(stringValue.substr(3,-4)); //""( 空字符串)

?位置方法,indexOf()lastIndexOf()

var stringValue = "hello world";
alert(stringValue.indexOf("o")); //4 
alert(stringValue.lastIndexOf("o")); //7
alert(stringValue.indexOf("o",6)); //7 
alert(stringValue.lastIndexOf("o",6)); //4
var stringValue = "Lorem ipsum dolor sit amet, consectetur adipisicing elit";
var positions = new Array();
var pos = stringValue.indexOf("e");
while (pos > -1) {
    positions.push(pos);
    pos = stringValue.indexOf("e", pos + 1);
}
alert(positions); //"3, 24, 32, 35, 52"

?trim()去除前面和后面空格:

var stringValue = "   hello world    ";
var trimmedStringValue = stringValue.trim();
alert(stringValue); //"   hello world    "
alert(trimmedStringValue); //"hello world"

?toLowerCase()、toLocaleLowerCase()、toUpperCase()toLocaleUpperCase()用于轉換大小寫。

?match()匹配方法,返回一個數(shù)組:

var text = "cat,bat,sat,fat";
var pattern = /.at/; 

//與pattern.exec(text)相同 
var matches = text.match(pattern);
alert(matches.index); //0 
alert(matches[0]); //"cat" 
alert(pattern.lastIndex); //0,lastIndex:是RegExp的屬性,一個可讀/寫的整數(shù),如果匹配模式中帶有g修飾符,這個屬性存儲在整個字符串中下一次檢索的開始位置

?search(),這個方法的唯一參數(shù)與match()方法的參數(shù)相同,返回字符串中第一個匹配項的索引:

var text = "cat, bat, sat, fat";
var pos = text.search(/at/);
alert(pos); //1

?replace(),第二參數(shù)可以是一個函數(shù)或者一串字符串 ★:

var text = "cat, bat, sat, fat";
result = text.replace(/(.at)/g, "word ($1)");
alert(result); //word (cat), word (bat), word (sat), word (fat)
function htmlEscape(text) {
    return text.replace(/[<>"&]/g,
        function(match, pos, originalText) {
            switch (match) {
                case "<":
                    return "&lt;";
                case ">":
                    return "&gt;";
                case "&":
                    return "&amp;";
                case "\"":
                    return "&quot;";
            }
        });
}
alert(htmlEscape("<p class=\" greeting\"> Hello world!</p>")); //&lt; p class=& quot; greeting& quot;& gt; Hello world!& lt;/ p& gt;

?最后一個匹配方法split()

var colorText = "red,blue,green,yellow";
var colors1 = colorText.split(","); //["red","blue","green","yellow"] 
var colors2 = colorText.split(",", 2); //["red","blue"]
var colors3 = colorText.split(/[^\,]+/); //["",",",",",",",""]

?剩下與String類型有關的幾個方法:
?localeCompare(),比較字符串,根據(jù)在字母表的順序返回數(shù)字1(或正數(shù))、-1(或負數(shù))、0(相等)。
?fromCharCode(),接收一到多個字符編碼,轉化成一個字符串。

? Global對象,所有在全局作用域中定義的屬性和函數(shù),都是Global對象的屬性;window對象在Web瀏覽器中,是對Global對象的一種實現(xiàn),而且Global只是window對象的一部分。

?⑥ Math對象
?1> 屬性:


?2> 方法:
?max() min()

var max = Math.max(3, 54, 32, 16);
alert(max); //54 
var min = Math.min(3, 54, 32, 16);
alert(min); //3

?ceil() floor() round(),舍入方法:

alert(Math.ceil(25.9)); //26 
alert(Math.ceil(25.5)); //26 
alert(Math.ceil(25.1)); //26 
alert(Math.round(25.9)); //26

?random()

值 = Math.floor(Math. random() * 可能值的總數(shù) + 第一個可能的值)

?返回1到10的數(shù)值:

var num = Math.floor(Math.random() * 10 + 1);

?其他方法:


11 面向?qū)ο蟮腏S

? 一次定義多個屬性,defineProperties

var book = {};
Object.defineProperties(book, {
    _year: {
        value: 2004
    },
    edition: {
        value: 1
    },
    year: {
        get: function() {
            return this._ year;
        },
        set: function(newValue) {
            if (newValue > 2004) {
                this._ year = newValue;
                this.edition += newValue - 2004;
            }
        }
    }
});

?下面是幾種創(chuàng)建對象的方法與其的優(yōu)缺點:
?② 構造函數(shù)模式
?構造函數(shù)模式,構造函數(shù)應始終都應該以一個大寫字母開頭,而非構造函數(shù)則應該以一個小寫字母開頭:

function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function() {
        alert(this.name);
    };
}
var person1 = new Person(" Nicholas", 29, "Software Engineer");
var person2 = new Person(" Greg", 27, "Doctor");

?構造函數(shù)可以當普通函數(shù)使用:

// 當構造函數(shù)使用 
var person = new Person(" Nicholas", 29, "Software Engineer");
person.sayName(); //"Nicholas" 
// 作為普通函數(shù)調(diào)用 
Person(" Greg", 27, "Doctor");
// 添加到window 
window.sayName(); //"Greg" 
// 在另一個對象的作用域中調(diào)用
var o = new Object();
Person.call(o, "Kristen", 25, "Nurse");
o.sayName(); //"Kristen"

?構造函數(shù)的缺點:每次創(chuàng)建實例都會為實例新建一個方法,而且不是同一個方法的實例:

alert(person1.sayName == person2.sayName); //false

?可以這么改寫:

function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = sayName;
}

function sayName() {
    alert(this.name);
}
var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");

?③ 原型模式

function Person() {}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function() {
    alert(this.name);
};

var person1 = new Person();
person1.sayName(); //"
Nicholas " var person2 = new Person(); person2. sayName(); //"
Nicholas " 
alert(person1.sayName == person2.sayName); //true

?在上面,我們注意到在為原型對象添加屬性時,需要每個都增加Person.prototype,這個工作很重復,可以改成更簡單的語法:

function Person() {}
Person.prototype = {
    name: "Nicholas",
    age: 29,
    job: "Software Engineer",
    sayName: function() {
        alert(this.name);
    }
};

?原型模式的缺點:假如原型有引用類型,假如是數(shù)組,那么所有的實例里面的數(shù)組都指向同一個數(shù)組。

?混合模式 ★★
?創(chuàng)建自定義類型的最常見方式,就是組合使用構造函數(shù)模式與原型模式。構造函數(shù)模式用于定義實例屬性,而原型模式用于定義方法和共享的屬性,這樣我們就可以傳遞不同的參數(shù)來創(chuàng)建出不同的對象,同時又擁有了共享的方法和屬性

function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.friends = ["Shelby", "Court"];
}
Person.prototype = {
    constructor: Person,
    sayName: function() {
        alert(this.name);
    }
}
var person1 = new Person(" Nicholas", 29, "Software Engineer");
var person2 = new Person(" Greg", 27, "Doctor");
person1.friends.push(" Van");
alert(person1.friends); //"Shelby, Count, Van" 
alert(person2.friends); //"Shelby, Count"
alert(person1.friends === person2.friends); //false 
alert(person1.sayName === person2.sayName); //true

?⑤ 動態(tài)原型模式——將原型方法和構造函數(shù)封裝到一塊★★★

function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.friends = ["Shelby", "Court"];
    if (typeof this.sayHi != "function") {
        Person.prototype.sayHi = function() {
            alert("hi,i am " + this.name);
        }
    }// 這段判斷語句的作用是限制Person.prototype屬性(原型屬性對象)只生成一次,要不然每次實例化一個Person對象都會去寫一遍原型對象!!!!!
}
var lee = new Person("lee", 17, "student");
var jack = new Person("jack", 28, "doctor");
lee.friends.push("dand");
lee.sayHi(); // hi,i am lee
alert(lee.friends); // Shelby,Court,dand
jack.sayHi(); // hi,i am jack
alert(jack.friends); // Shelby,Court

?⑥ 寄生構造函數(shù)模式——假設我們想創(chuàng)建一個具有額外方法的特殊數(shù)組。由于不能直接修改Array構造函數(shù),所以我們可以使用寄生模式:

function SpecialArray() {
    //創(chuàng)建數(shù)組
    var array=new Array();
    //添加值  arguments獲取的是實參,不是形參,所以SpecialArray()并沒有形參接收傳遞過來的參數(shù)
    array.push.apply(array,arguments);
    array.toPipedString=function(){
        return this.join("|");
    }
    return array;
}
var colors=new SpecialArray("red","blue","black");
alert(colors.toPipedString());  //輸出:red|blue|black

?⑦ 穩(wěn)妥構造函數(shù)模式,所謂穩(wěn)妥對象,指的是沒有公共屬性,而且其方法也不引用this的對象。:

function Person(name,age) {
    //創(chuàng)建要返回的對象
    var o=new Object();
    //可以在這里定義私有變量和函數(shù)
    //添加方法
    o.sayName=function(){
        alert(name);
    }
    //返回對象
    return o;
}
var person=Person("張三",22);
    person.sayName();  //使用穩(wěn)妥構造函數(shù)模式只能通過其構造函數(shù)內(nèi)部的方法來獲取里面的屬性值
最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容