廖雪峰的javascript教程筆記

入門

數據類型和變量

數據類型

Number

  • +、-、*、/四則運算,%求余數

String

  • 單引號或雙引號包裹
  • 轉義字符:\
  • 多行字符串:ES6新方式:使用...
  • 連接字符串:用+號
模板字符串
var name = "lxf";
var age = 26;
var msg = '您好,$(name),您今年$(age)歲。';
操作字符串
  • 獲取字符串的長度:變量名.length
  • 通過索引訪問對應字符串
  • 字符串是不可變的,對某個索引賦值,不會報錯也不會有效果
  • 字符串全部變大寫:變量名.toUpperCase( )
  • 字符串全部變小寫:變量名.toLowerCase( )
  • 檢索字符的位置:變量名.indexOf(字符),返回字符的索引值,如果沒有這個字符,則返回-1
  • 返回指定區間的字符:變量名.substring(開始位置,結束位置 )返回的字符串不包括結束位置的字符

Boolean

  • true、false

邏輯運算符

  • &&邏輯與運算符:所有都為true時,結果才是true
  • ||邏輯或運算符:其中一個為true,結果就是true
  • !邏輯非運算符:單目運算符,true變false,false變true

比較運算符

  • 包括:>,<,>=,<=,==,===
  • ==:先自動轉換數據類型,再比較
  • ===:比較原始的數據類型
  • NaN:與所有其他值都不相等,包括它自己。
    • 判斷NaN的方法:isNaN( )函數
  • 不要直接比較兩個浮點小數

null

  • 表示一個空的值

undefined

  • 未定義

Array

  • 數組:一組按順序排列的集合,集合的每個值稱為元素,數組可以包括任意數據類型
  • 創建數組方式:new Array()var arr = []
  • 訪問數組中的元素可以使用索引值,索引值從0開始
  • 訪問超出數組長度的索引值時,返回undefined
  • 獲取數組長度:數組.length
  • 給length賦值會改變數組的長度,多出來的值為undefined
  • 通過訪問索引值并對索引賦值,會修改數組,如果索引值超出長度范圍,該索引的值為賦的值,中間多出來的索引的值為undefined
  • 搜索指定元素的索引值:數組.indexOf(元素),未找到會返回-1
  • 截取指定范圍的元素并返回一個新數組:數組.slice(開始位置,結束位置),新的數組不包含結束位置的元素,不給slice傳遞任何參數,則會從頭到尾復制數組
push和pop
  • push:向數組末尾添加若干元素數組.push(x,y,z,...)
  • pop:刪除數組最后一個元素數組.pop( ),對空數組使用pop會返回undefined
unshift和shift
  • unshift:向數組的頭部添加若干元素arr.unshift(x,y,z,...)
  • shift:刪除數組的第一個元素arr.shift(),空數組使用會返回undefined
sort
  • 對當前數組進行排序,會修改元素的位置,直接調用sort()會按照默認條件排序
reverse
  • 將數組反轉排列
var arr = [1,2,3];
arr.reverse();
arr;  // [3,2,1]
splice
  • 從指定的位置刪除若干元素,并從該位置添加新元素
  • splice(開始位置,刪除元素數量,新的元素,新的元素,...)
  • 只刪除不添加:splice(開始位置,刪除元素數量)
  • 只添加不刪除:splice(開始位置,0,新的元素,新的元素,...)
var arr = [1,2,3];
arr.splice(1,2,'a','b','c');  // [2,3]
arr; // [1,'a','b','c']
concat
  • concat()將數組與其他數組組合起來,并返回一個新的數組
  • concat并沒有修改原數組,而是返回一個新數組
var arr = [1,2,3];
var newArr = arr.concat([9,8,7]);
newArr;  // [1,2,3,9,8,7]
arr;  // [1,2,3]
  • concat可以接收任意個元素和數組,并會把接收到的數組拆開再添加到返回的新的數組中
var arr = [1,2,3];
var newArr = arr.concat('a','b',[9,8,7]);
newArr;  // [1,2,3,'a','b',9,8,7]
arr;  // [1,2,3]
join
  • 將數組內的所有元素按照指定的字符連接起來,然后返回連接后的字符串
var arr = [1,2,3];
arr.join('+');  // '1+2+3'

Object

  • 對象:一組由鍵值對組成的無序集合
  • 對象的鍵名是字符串類型,值可以是任意數據類型
  • 鍵名又稱對象的屬性
  • 獲取對象的屬性:obj.屬性名/obj["屬性名"]
  • 訪問不存在的屬性時,返回undefined
  • 新增屬性:obj.屬性 = 值
  • 刪除屬性:delete obj.屬性
  • 刪除不存在的屬性時并不會報錯
  • 判斷屬性是否是obj自有的而不是繼承的:obj.hasOwnProperty()

變量

  • 變量可以是任意數據類型
  • 變量名可以包含:大小寫英文、數字、$、_
  • 變量名不可以是數字開頭
  • 變量名不能是關鍵字

strict模式

  • 開啟:'use strict'

條件判斷

  • if...else
  • null、undefined、0、NaN、空字符串視為false

循環

for循環

  • 省略三個條件將無限循環,需要使用break退出循環

for...in

  • 將對象的所有屬性依次循環出來
var obj = {
    name: 'lxf',
    age: 26
};
for(var prop in obj){
    if(obj.hasOwnProperty(prop)){  // 過濾掉繼承的屬性
        console.log(prop);
    }
}
  • 由于Array也是對象,而它每個元素的索引被視為對象的屬性,因此for...in可以循環出數組的索引
  • for...in循環出的結果是字符串
var arr = ['a','b','c'];
for(var item in arr){
    alert(item);  // "0,1,2"
    alert(arr[item]);  // 'a','b','c'
}

while循環

  • 只有一個判斷條件,條件滿足就不斷循環

do...while

  • 先執行循環,再判斷

Map和Set

  • ES6

Map

  • map是一組鍵值對結構,擁有極快的查找速度
  • 初始化:需要一個二維數組,或直接初始化一個空的map
var m = new Map();
m.set("name","lxf");  // 添加鍵值對
m.has("name");  // 是否含有name屬性
m.get("name");  // 返回值lxf
m.delete("name");  // 刪除name
m.get('name');  // 不存在的返回undefined

Set

  • set是一組key的集合,沒有重復的key
  • 創建:var s1 = new Set()var s2 = new Set([])
  • 重復元素的set中會被自動過濾
  • 添加:s1.add(key)
  • 刪除:s1.delete(key)

iterable

  • ES6引入的新的類型
  • Array、Map、Set都屬于iterable類型

for...of

  • 只循環集合本身的元素
var a = ['A', 'B', 'C'];
var s = new Set(['A', 'B', 'C']);
var m = new Map([[1, 'x'], [2, 'y'], [3, 'z']]);
for (var x of a) { // 遍歷Array 
    alert(x);
}
for (var x of s) { // 遍歷Set 
    alert(x);
}
for (var x of m) { // 遍歷Map 
    alert(x[0] + '=' + x[1]);
}

forEach()

  • ES5.1的方法
  • iterable內置的方法,接收一個函數,每次迭代就自動回調該函數
  • 不需要的參數可以省略
var arr = [1,2,3];
arr.forEach(function(element, index, array){
    // element:當前元素的值
    // index:當前元素的索引值
    // array:數組本身
    alert(element);
});

var s = new Set(['1','a','c']);
s.forEach(function(element, sameElement, set){
    // element, sameElement:當前元素本身
    // set:該set
    alert(element);
})

var m = new Map([[1,11],['asd',222],[3,'zxc']]);
m.forEach(function(value, key, map){
    // value:當前屬性的值
    // key:當前屬性
    // map:map本身
    alert(key + '=' + value);
})

函數

  • 函數體內語句執行到return時,函數就執行完畢,并返回結果;沒有return時,函數體內代碼執行完畢后也會返回結果,但結果為undefined。
  • 調用函數時,傳入的參數可以比接收的參數多,也可以少,也可以不傳
// 定義函數
function asd(){...}
var asd = function(){};

function abs(x){
    if(indexOf(x) !== 'number'){  // 如果參數不是數字,拋出異常 
        throw 'Not a number!';
    }
    if(x >= 0){
        return x;
    }else{  // 如果是負數,返回正數
        return -x;
    }
}
abs(-9);  // 9

arguments

  • 只在函數內部起作用
  • 永遠指向函數的調用者傳入的所有參數
  • 利用arguments可以獲得所有參數,即使函數沒有定義參數
function foo(){
    if(arguments.length === 0){
        return 0;
    }
    var arg = arguments[0];
    return arg >= 0 ? arg : -arg;
}
foo(-10);  // 10

rest參數

  • ES6引入了rest參數
  • 使用:function and(a, b, ...rest){}
  • rest參數接收多余的參數,是一個數組;如果沒有多余的參數,rest會是一個空數組
function abc(a, b, c, ...rest){
    alert('a='+a);  // 1
    alert('b='+b);  // 2
    alert('c='+c);  // 3
    alert('rest='+rest);  // 4,5
}
abc(1,2,3,4,5);

關于return

return {  // return的換行書寫方式 
    ...
}

變量作用域

  • 如果變量在函數體內聲明,這個變量的作用域就是整個函數體,函數外不可引用。
  • 如果兩個函數體內聲明了同樣的變量,則該變量的作用域為各自的函數體,互不影響。
  • 函數A嵌套函數B時,內部的函數可以訪問外部函數的變量(B可訪問A中的變量),外部不可訪問內部。

嵌套的函數出現變量重名時

  • 函數再查找變量時,從自身函數開始,由內向外查找。
function a(){
    var x = 1;
    function b(){
        var x = 'a';
        alert("b()的x=" + x);
    }
    alert("a()的x=" + x);
    b();
}

變量提升

  • 函數會將函數體內的變量提升到函數的頂部,但不會提升變量的賦值
  • 請在函數內部首先聲明所有變量喲親~
function a(){
    var x = 1;
    alert(x+y);
    var y = 2;

    // 變量提升后的排列:
    // var y;  變量提升
    // var x = 1;
    // alert(x+y);
    // y = 2;  不會提升變量的賦值
}

全局作用域

  • 不在任何函數內定義的變量就具有全局作用域。實際上,JavaScript默認有一個全局對象window,全局作用域的變量實際上被綁定到window的一個屬性

var name = 'lxf';
alert(name);  // 'lxf'
alert(window.name);  // 'lxf'

名字空間

  • 全局變量會綁定到window上,如果多個文件聲明了相同的全局變量會沖突。
  • 解決辦法:將所有的變量和函數,都綁定到一個全局變量中。
var myGlobal = {};
myGlobal.name = 'lxf';
myGlobal.age = 26;
myGlobal.a = function(){
    alert('您好,' + myGlobal.name + ',您今年' + myGlobal.age + '歲');
}

局部作用域

  • 由于變量的作用域是函數體,所以無法在for循環等代碼塊中聲明一個局部變量。
  • ES6引入了let用來定義塊級作用域的變量。
function a(){
    for(var i=0;i<10;i++){
        ...
    }
    i += 10;  // var聲明的變量的作用域仍是函數體
}

function b(){
    for(let i=0;i<10;i++){
        alert(i);
    }
    alert(i);  // let聲明了一個塊級作用域,所以for語句外無法訪問,報錯Uncaught ReferenceError: i is not defined
}

常量

  • ES6引入了新的關鍵字const來聲明常量
  • 常量不可重新賦值,會報錯
  • constlet一樣,是塊級作用域
const a = 1;
a;  // 1
a = 2;  // Uncaught TypeError: Assignment to constant variable

方法

  • 在一個對象中綁定函數,稱為這個對象的方法
  • 在一個方法內部,this永遠指向當前對象
var menu = {
    name: '魚香肉絲',
    price: 15,
    order: function(num){
        var msg = '您購買了' + num + '份,總價為:' + (num * this.price) + '元。';
        return msg;
    }
}

this

  • 以對象的方法的形式調用對象,this指向這個對象
  • 對象的方法函數體中的this指向這個對象,方法中的函數的函數體內的this,指向window(strict模式中指向undefined)
  • 直接調用函數,this指向window
  • strict模式中,函數中的this指向undefined
  • 在方法函數的函數體內聲明that來捕獲this,然后再方法內部的函數中使用that。
function orderMsg(num){
        var msg = '您購買了' + num + '份,總價為:' + (num * this.price) + '元。';
        return msg;
    }
var menu = {
    name: '魚香肉絲',
    price: 15,
    order: orderMsg
}
menu.order(2);  //"您購買了2份,總價為:30元。"
orderMsg(2);  // NaN,直接調用函數,this指向的是window

apply和call

  • 使用函數本身的apply方法,控制函數中this的指向。
  • apply接收兩個參數:第一個是需要綁定this的變量;第二個是array,表示要傳給函數本身的參數。
  • apply和call類似;apply把參數打包成數組后再傳入;call把參數按順序傳入
  • 普通的函數使用apply/call時,通常將this綁定為null
function getAge() { 
    var y = new Date().getFullYear();
    return y - this.birth;
}
var xiaoming = { 
    name: '小明', 
    birth: 1990, 
    age: getAge
};
xiaoming.age(); // 25
getAge.apply(xiaoming, []); // 25, this指向xiaoming, 參數為空

Math.max.apply(null, [2,3,4]);  // 參數打包成數組傳入;this綁定為null
Math.max.call(null, 2,3,4);  // 按順序傳入參數;this綁定為null

裝飾器

  • 動態改變函數的行為、重新指向新的函數
var count = 0;
var oldAlert = window.alert;
window.alert = function(){
    count += 1;
    return oldAlert.apply(null, arguments);
};
alert(1);
alert(2);
count;  // 2

高階函數

  • 函數A接收函數B做為參數,這種函數就是高階函數
function toNum(a){
    var num = Number(a);
    return num;
}
function a(x,y,z){
    return z(x) + z(y);
}
a('1', '2', toNum);  // 3

map/reduce

  • map方法傳入自定義的函數,可以得到新的數組
  • reduce接收兩個參數,結果和下一個元素
// map()
function pow(x){
    return x * x;
}
var arr = [1,2,3,4,5];
arr.map(pow);  // [1,4,9,16,25]
// reduce()
function plus(arr){
    var val = arr.reduce(function(x,y){
        return x + y;
    });
    return val;
}
var arr = [1,2,3,5,9];
plus(arr);  // 20

練習

  • 名字首字母大寫,其他小寫
var arr = ['BoB', 'PaWN','ann','LEE'];
function change(arr){
    var newName = arr.map(function(name){
        var lower = name.toLowerCase();
        var upper = name[0].toUpperCase();
        return upper + lower.substring(1);
    });
    return newName;
}
change(arr);

filter

  • 接收一個函數,這個函數會依次作用于每個元素,然后根據返回的是true還是false來決定保留和刪除
var arr = [1,2,3,4,5];
var newArr = arr.filter(function(item){
    return item % 2 === 0;
});
newArr;  // [2,4]

排序sort()

  • sort的默認排序方式是將所有元素轉換為字符串,再比對ASCII碼進行排序,所以直接使用sort會有坑
  • sort排序時,會比較x和y:如果x<y返回-1;如果x=y返回0;如果x>y返回1。會根據返回的值做排序
  • sort可以接收一個函數做自定義排序
  • sort會直接修改原數組
// 字符串按字母順序排列
var arr = ['yes','app', 'ball'];
arr.sort(function(x,y){
    var first = x.toLowerCase();  // 先統一參數的大小寫
    var last = y.toLowerCase();
    if(first < last){
        return -1;
    } else if(first == last){
        return 0;
    } else if(first > base){
        return 1;
    }
});  // ['app', 'ball', 'yes']

閉包

  • 函數作為返回值
  • 返回函數不要引用任何循環變量,或者后續會發生變化的變量
  • 閉包就是攜帶狀態的函數,并且它的狀態會完全的對外隱藏起來
function lazy_sum(arr){
    var sum = function(){  // sum可以引用lazy_sum的參數和變量 
        return arr.reduce(function(x,y){
            return x + y;
        })
    }
    return sum;  // 返回sum函數,每次調用都會返回一個新的函數
}

// 調用
var f = lazy_sum([1,2,3,4,5]);
f();  // 15

// 一定要引用循環變量時:
function count(){
    var arr = [];
    for(var i=1;i<=3;i++){
        arr.push((function(n){
            return function(){
                return n * n;
            }
        })(i));
    }
    return arr;
}

var result = count();
var f1 = result[0];  
f1();  // 1

// 計數器
function counter(num){
    var x = num || 0;
    return {
        inc: function(){
            x += 1;
            return x;
        }
    }
}
var create = counter();
create.inc();  // 1
var create2 = counter(20);
create2.inc();  // 21

// 求根:
function newPow(n){
    return function (num){
        return Math.pow(num, n);  // num是數,n是次方
    }
}
var pow = newPow(2);
pow(3);  // 9

箭頭函數

  • ES6標準新增的一種函數
  • 定義使用的一個箭頭
  • x => x * x等于function(x){return x*x}
  • 箭頭函數相當于匿名函數
  • 如果參數不止一個,需要用括號()包起來
// 多語句:
x => { 
    if (x > 0) { 
        return x * x; 
    } else { 
        return - x * x; 
    }
}

// 多個參數:
(x,y)=> x * x + y * y;

// 無參數:
() => 1

// 可變參數:
(x,y,...rest) => {
    var i, 
    sum=x+y;
    for(I=0;i<rest.length;i++){
        sum += rest[I];
    }
    return sum;
}

// 如果要返回一個對象:
x => {{ foo:x }}

this

  • 箭頭函數內部的this是詞法作用域,由上下文決定
  • 由于this已經按詞法作用域綁定,所以使用call()或者apply()調用箭頭函數時,無法對this進行綁定,即傳入的第一個參數被忽略

generator

  • generator是ES6引入的新的數據類型,看起來像個函數,但能返回多次
  • generator由function*定義,除了return語句,還可以用yield返回多次
  • generator的next()方法:執行generator代碼,每當遇到yield時,就返回一個對象{value: x, done: false/true}
function* fib(max){
    var t,
          a = 0,
          b = 1,
          n = 1;
    while(n<max){
        yield a;
        t = a + b;
        a = b;
        b = t;
        n++;
    }
    return a;
}

// 調用generator

// 方法1:使用next()
// done:false表示沒有執行結束,true表示結束
// 當done是true時,`value`的值就是`return`的值
var f = fib(5);
f.next();  // Object {value: 0, done: false}
f.next();  // Object {value: 1, done: false}
f.next();  // Object {value: 1, done: false}
f.next();  // Object {value: 2, done: false}
f.next();  // Object {value: 3, done: true}

// done為true之后再使用next方法的話:
f.next();  // Object {value: undefined, done: true}

// 方法2:for...of循環迭代,不需要判斷done
for (var x of fib(5)){
    console.log(x);
}

因為generator可以在執行過程中多次返回,所以它看上去就像一個可以記住執行狀態的函數,利用這一點,寫一個generator就可以實現需要用面向對象才能實現的功能。

generator還有另一個巨大的好處,就是把異步回調代碼變成“同步”代碼。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容