javascript 雜記

說實話,再你熟悉了C C++ java python後再學js,老是會有無數錯覺,總以爲它的語法像什麼,又不太像。做爲動態語言又沒python那麼簡潔,裏面不少語法借鑑了c和java的那套,可是又不像c那麼嚴格。js是弱類型語言,並且仍是有一些動態語言的靈活性在裏面。javascript

題記

若是帶着以前學C++/java的思路去學js,總以爲十分別扭,特別是它的object和function,真是讓人摸不着頭腦。php

因此說啊,js真是個磨人的小妖精。要征服這個小妖精還真不是一時半會能實現的,因此,仍是慢慢來吧,把征服它的過程都記錄一下。java

注意:本文是一個本人的總結記錄文,並非一個js教程,請不要抱着《從入門到精通》的思路來看本文。若是你有必定的編程基礎,或者正在學習js,這個筆記可能對你有點用。(如下筆記是廖雪峯javascript教程,還有freecodecamp教程筆記)python

對了,本文持續更新ing...es6

語法

目前大多語言都是類C的,js也同樣(除了python ruby這些自成一派的)正則表達式

  • 區分大小寫
  • 字符串能夠用''或「」和python同樣
  • 變量不區分類型,和python同樣(python不用寫var)
  • 每條語句結尾能夠省略分號
  • 註釋與C,C++,java,php相同
  • 代碼段要封閉與C,C++,java同(和python不同)
  • 運算符徹底和C一致,有自加自減,有逗號表達式,有條件運算符!!(和python不同)
  • 邏輯運算符,條件,循環語句和C一致(和python不同)
  • 有switch-case語句(和python不同)

函數相關

兩種定義

  • 普通定義
function abs(x) {
    if (x >= 0) {
        return x;
    } else {
        return -x;
    }
}
  • 匿名函數
var abs = function (x) {
    if (x >= 0) {
        return x;
    } else {
        return -x;
    } 
};  //注意分號

函數傳參能夠任意個

arguments

用這個關鍵詞能夠判斷函數傳入參數的個數編程

function foo(a, b) {
    var i, rest = [];
    if (arguments.length > 2) {
        for (i = 2; i<arguments.length; i++) {
            rest.push(arguments[i]);
        }
    }
    console.log('a = ' + a);
    console.log('b = ' + b);
    console.log(rest);
}

rest

ES6標準引入了rest參數,上面的函數能夠改寫爲:json

function foo(a, b, ...rest) {
    console.log('a = ' + a);
    console.log('b = ' + b);
    console.log(rest);
}
foo(1, 2, 3, 4, 5);
// 結果:
// a = 1
// b = 2
// Array [ 3, 4, 5 ]

foo(1);
// 結果:
// a = 1
// b = undefined
// Array []

rest參數只能寫在最後,前面用...標識,從運行結果可知,傳入的參數先綁定a、b,多餘的參數以數組形式交給變量rest,因此,再也不須要arguments咱們就獲取了所有參數。windows

若是傳入的參數連正常定義的參數都沒填滿,也沒關係,rest參數會接收一個空數組(注意不是undefined)。數組

變量提高

JavaScript的函數定義有個特色,它會先掃描整個函數體的語句,把全部申明的變量「提高」到函數頂部:

'use strict';

function foo() {
    var x = 'Hello, ' + y;
    alert(x);
    var y = 'Bob';
}

foo();

雖然是strict模式,但語句var x = 'Hello, ' + y;並不報錯,緣由是變量y在稍後申明瞭。可是alert顯示Hello, undefined,說明變量y的值爲undefined。這正是由於JavaScript引擎自動提高了變量y的聲明,但不會提高變量y的賦值。

因爲JavaScript的這一怪異的「特性」,咱們在函數內部定義變量時,請嚴格遵照「在函數內部首先申明全部變量」這一規則。最多見的作法是用一個var申明函數內部用到的全部變量

全局做用域

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

你可能猜到了,因爲函數定義有兩種方式,以變量方式var foo = function () {}定義的函數實際上也是一個全局變量,所以,頂層函數的定義也被視爲一個全局變量,並綁定到window對象:

alert也是windows的一個變量

這說明JavaScript實際上只有一個全局做用域。任何變量(函數也視爲變量),若是沒有在當前函數做用域中找到,就會繼續往上查找,最後若是在全局做用域中也沒有找到,則報ReferenceError錯誤。

名字空間,避免關鍵詞衝突

全局變量會綁定到window上,不一樣的JavaScript文件若是使用了相同的全局變量,或者定義了相同名字的頂層函數,都會形成命名衝突,而且很難被發現。

減小衝突的一個方法是把本身的全部變量和函數所有綁定到一個全局變量中。例如:

// 惟一的全局變量MYAPP:
var MYAPP = {};

// 其餘變量:
MYAPP.name = 'myapp';
MYAPP.version = 1.0;

// 其餘函數:
MYAPP.foo = function () {
    return 'foo';
};

把本身的代碼所有放入惟一的名字空間MYAPP中,會大大減小全局變量衝突的可能。

許多著名的JavaScript庫都是這麼幹的:jQuery,YUI,underscore等等。

局部做用域 let

因爲JavaScript的變量做用域其實是函數內部,咱們在for循環等語句塊中是沒法定義具備局部做用域的變量的:

'use strict';

function foo() {
    for (var i=0; i<100; i++) {
        //
    }
    i += 100; // 仍然能夠引用變量i
}

爲了解決塊級做用域,ES6引入了新的關鍵字let,用let替代var能夠申明一個塊級做用域的變量:

'use strict';

function foo() {
    var sum = 0;
    for (let i=0; i<100; i++) {
        sum += i;
    }
    i += 1; // SyntaxError
}

閉包函數

js能夠把函數名返回,只有再次調用的時候才返回結果。

function lazy_sum(arr) {
    var sum = function () {
        return arr.reduce(function (x, y) {
            return x + y;
        });
    }
    return sum;
}
var f = lazy_sum([1, 2, 3, 4, 5]); // function sum()
f(); // 15

返回閉包時牢記的一點就是:返回函數不要引用任何循環變量,或者後續會發生變化的變量。

理論上講,建立一個匿名函數並馬上執行能夠這麼寫:

function (x) { return x * x } (3);

可是因爲JavaScript語法解析的問題,會報SyntaxError錯誤,所以須要用括號把整個函數定義括起來:
一般,一個當即執行的匿名函數能夠把函數體拆開,通常這麼寫:

(function (x) {
    return x * x;
})(3);

閉包有很是強大的功能。舉個栗子:

在面向對象的程序設計語言裏,好比Java和C++,要在對象內部封裝一個私有變量,能夠用private修飾一個成員變量。

在沒有class機制,只有函數的語言裏,藉助閉包,一樣能夠封裝一個私有變量。咱們用JavaScript建立一個計數器:

'use strict';

function create_counter(initial) {
    var x = initial || 0;
    return {
        inc: function () {
            x += 1;
            return x;
        }
    }
}

var c1 = create_counter();
c1.inc(); // 1
c1.inc(); // 2
c1.inc(); // 3

var c2 = create_counter(10);
c2.inc(); // 11
c2.inc(); // 12
c2.inc(); // 13

在返回的對象中,實現了一個閉包,該閉包攜帶了局部變量x,而且,從外部代碼根本沒法訪問到變量x。換句話說,閉包就是攜帶狀態的函數,而且它的狀態能夠徹底對外隱藏起來。

閉包還能夠把多參數的函數變成單參數的函數。例如,要計算xy能夠用Math.pow(x, y)函數,不過考慮到常常計算x2或x3,咱們能夠利用閉包建立新的函數pow2和pow3:

function make_pow(n) {
    return function (x) {
        return Math.pow(x, n);
    }
}

// 建立兩個新函數:
var pow2 = make_pow(2);
var pow3 = make_pow(3);

pow2(5); // 25
pow3(7); // 343

箭頭函數

ES6標準新增了一種新的函數:Arrow Function(箭頭函數)。

爲何叫Arrow Function?由於它的定義用的就是一個箭頭:

x => x * x
上面的箭頭函數至關於:

function (x) {
    return x * x;
}

箭頭函數至關於匿名函數,而且簡化了函數定義。箭頭函數有兩種格式,一種像上面的,只包含一個表達式,連{ ... }和return都省略掉了。還有一種能夠包含多條語句,這時候就不能省略{ ... }和return:

若是參數不是一個,就須要用括號()括起來:

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

// 無參數:
() => 3.14

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

如今,箭頭函數徹底修復了this的指向,this老是指向詞法做用域,也就是外層調用者obj

var obj = {
    birth: 1990,
    getAge: function () {
        var b = this.birth; // 1990
        var fn = () => new Date().getFullYear() - this.birth; // this指向obj對象
        return fn();
    }
};
obj.getAge(); // 25

因爲this在箭頭函數中已經按照詞法做用域綁定了,因此,用call()或者apply()調用箭頭函數時,沒法對this進行綁定,即傳入的第一個參數被忽略:

var obj = {
    birth: 1990,
    getAge: function (year) {
        var b = this.birth; // 1990
        var fn = (y) => y - this.birth; // this.birth還是1990
        return fn.call({birth:2000}, year);
    }
};
obj.getAge(2015); // 25

Map和Set

map

  • 建立map
var m = new Map([['Michael', 95], ['Bob', 75], ['Tracy', 85]]);
m.get('Michael'); // 95
  • Map具備如下方法:(set get has delete)
var m = new Map(); // 空Map
m.set('Adam', 67); // 添加新的key-value
m.set('Bob', 59);m.has('Adam'); // 是否存在key 'Adam':true
m.get('Adam'); // 67
m.delete('Adam'); // 刪除key 'Adam' 若是刪除失敗返回false
m.get('Adam'); // undefined

因爲一個key只能對應一個value,因此,屢次對一個key放入value,後面的值會把前面的值沖掉:

var m = new Map();
m.set('Adam', 67);
m.set('Adam', 88);
m.get('Adam'); // 88

set

Set和Map相似,也是一組key的集合,但不存儲value。因爲key不能重複,因此,在Set中,沒有重複的key。
要建立一個Set,須要提供一個Array做爲輸入,或者直接建立一個`空

var s1 = new Set(); // 空Set
var s2 = new Set([1, 2, 3]); // 含1, 2, 3重複元素在Set中自動被過濾:
var s = new Set([1, 2, 3, 3, '3']);
s; // Set {1, 2, 3, "3"}

注意數字3和字符串'3'是不一樣的元素。

  • add(key)方法
    能夠添加元素到Set中,能夠重複添加,但不會有效果:
>>> s.add(4)
>>> s
{1, 2, 3, 4}
>>> s.add(4)
>>> s
{1, 2, 3, 4}
  • delete(key)方法
    能夠刪除元素:
var s = new Set([1, 2, 3]);
s; // Set {1, 2, 3}
s.delete(3);
s; // Set {1, 2}

其餘

常量const

因爲var和let申明的是變量,若是要申明一個常量,在ES6以前是不行的,咱們一般用所有大寫的變量來表示「這是一個常量,不要修改它的值」:

var PI = 3.14;
ES6標準引入了新的關鍵字const來定義常量,const與let都具備塊級做用域:

表示多行字符串 ES6提出了表示多行字符串

`這是一個
多行
字符串`

null和undefined

null表示一個「空」的值,它和0以及空字符串''不一樣,0是一個數值,''表示長度爲0的字符串,而null表示「空」。

在其餘語言中,也有相似JavaScript的null的表示,例如Java也用null,Swift用nil,Python用None表示。可是,在JavaScript中,還有一個和null相似的undefined,它表示「未定義」。

JavaScript的設計者但願用null表示一個空的值,而undefined表示值未定義。事實證實,這並無什麼卵用,區分二者的意義不大。大多數狀況下,咱們都應該用null。undefined僅僅在判斷函數參數是否傳遞的狀況下有用。

字符串轉正則eval()

有人可能發現了,js的正則表達式並非字符串,可是實際使用中,咱們常常須要字符串去構造正則表達式,這個時候就能夠用eval()去轉化。eval()能夠用來解析字符串
可是eval是一個很危險的操做,若是非必要,儘可能少使用

var test = 'this is a test';
var exp = '/\w/ig';
test.match(eval(exp));

輸出

console.log()  //在控制檯顯示
alert();  //彈出對話框顯示結果

或者直接輸入變量,在瀏覽器的控制檯也能顯示結果

嚴格相等

嚴格相等(===)不只比較值,還比較類型

由於在js中,7 == 「7」



number

JavaScript不區分整數和浮點數,統一用Number表示,如下都是合法的Number類型:

123; // 整數
1230.456; // 浮點數
0.4561.2345e3; // 科學計數法表示
1.2345x1000,等同於1234.5
-99; // 負數
NaN; // NaN表示Not a Number,當沒法計算結果時用NaN表示
Infinity; // Infinity表示無限大,當數值超過了JavaScript的Number所能表示的最大值時,就表示爲Infinity

注意NaN
這個特殊的Number與全部其餘值都不相等,包括它本身:

NaN === NaN; // false

惟一能判斷NaN
的方法是經過isNaN()函數:

isNaN(NaN); // true

for in vs for of (ES6新加)

js中也有for in用法,可是和python有些不同的地方。

  • js中的for in對數組也是返回的是鍵,好比「0」,「1」……(並且因爲歷史遺留問題,'name'也在遍歷範圍內)
  • python中直接返回的是數組中的值
  • js中的for of和python中的for in纔是真實對應的哦~不只能夠用於數組也能夠用於字符串

因此for in用在json中的遍歷比較合適。
好比說你若是須要遍歷數組,你可使用普通for循環作,也能夠用for of

var arr = ["a","b","c","d"];
for(var i = 0;i < arr.length;i++)
{
  console.log(arr[i]);
}
for(var i in arr)
{
 //這裏的i是字符串,打印出來能夠發現是「0」,「1」,「2」……
  console.log(arr[i]);
}
for(var i of arr)
{
  console.log(i);
}

forEach

更好的方式是直接使用iterable,內置的forEach方法,它接收一個函數,每次迭代就自動回調該函數。以Array爲例:

var a = ['A', 'B', 'C'];
a.forEach(function (element, index, array) { 
// element: 指向當前元素的值 
// index: 指向當前索引 
// array: 指向Array對象自己 
alert(element);
});

注意,forEach()方法是ES5.1標準引入的,你須要測試瀏覽器是否支持。

Set與Array相似,但Set沒有索引,所以回調函數的前兩個參數都是元素自己:

var s = new Set(['A', 'B', 'C']);
s.forEach(function (element, sameElement, set) { 
alert(element);
});

Map的回調函數參數依次爲value、key和map自己:

var m = new Map([[1, 'x'], [2, 'y'], [3, 'z']]);
m.forEach(function (value, key, map) { 
alert(value);
});

若是對某些參數不感興趣,因爲JavaScript的函數調用不要求參數必須一致,所以能夠忽略它們。例如,只須要得到Array的element:

var a = ['A', 'B', 'C'];
a.forEach(function (element) { 
alert(element);
});

字符串相關

越界不報錯

字符串越界不會報錯,會返回undefine

字符串不可變

須要特別注意的是,字符串是不可變的,若是對字符串的某個索引賦值,不會有任何錯誤,可是,也沒有任何效果:

var s = 'Test';
s[0] = 'X';
alert(s); // s仍然爲'Test'

JavaScript爲字符串提供了一些經常使用方法,注意,調用這些方法自己不會改變原有字符串的內容,而是返回一個新字符串

toUpperCase

toUpperCase()把一個字符串所有變爲大寫:

var s = 'Hello';
s.toUpperCase(); // 返回'HELLO'

toLowerCase

toLowerCase()把一個字符串所有變爲小寫:

var s = 'Hello';
var lower = s.toLowerCase(); // 返回'hello'並賦值給變量lower
lower; // 'hello'

indexOf

indexOf()會搜索指定字符串出現的位置:

var s = 'hello, world';
s.indexOf('world'); // 返回7
s.indexOf('World'); // 沒有找到指定的子串,返回-1

字符串的slice()和substr()和substring()

str.slice(beginSlice[, endSlice])
str.substr(start[, length])
str.substring(indexStart[, indexEnd])

都是截斷函數,可是有一些區別

  • slice和substr能接負數,表明倒數。substring不行
  • substr第二參數表明長度不是end
  • substring的end若是大於start會自動交換,slice不會,會返回""
  • 三者均支持越界
var str = "example";
console.log(str.slice(-3)); // "ple" 
console.log(str.substr(-3)); // "ple" 
console.log(str.substr(2,4)); // "ampl" 
console.log(str.substring(2,4)); // "am" 
console.log(str.slice(4,2)); // "" 
console.log(str.substring(4,2)); // "am" 
console.log(str.slice(2,-1)); // "ampl" 
console.log(str.substring(0,100)); // "example" 
console.log(str.slice(0,100)); // "example" 
console.log(str.substr(0,100)); // "example"

數組相關

js的數組和python的list同樣能夠存不一樣類型不一樣維度個數據,除了能夠用下標查看修改數據外,還有幾個方法:

  • push():加到最後
  • pop(): 從最後取
  • shift(): 從開頭取
  • unshift(): 加入開頭

構造數組的方式還有以下:(除了特別說明的外,都不改變原數組)

用map建立數組

var oldArray = [1,2,3,4]
var timesFour = oldArray.map(function(val){
  return val * 4;
});

用reduce壓縮數組

reduce的第2個參數(初始值)可選,若是沒有,就從數組第一個開始

var array = [4,5,6,7,8];
var sum = 0;
sum = array.reduce(function(pre,cur){
    return pre+cur;
},0);

用fliter過濾數組

若是咱們只須要數組中小於6的元素

var oldArray = [1,2,3,4,5,6,7,8,9,10];
var newArray = oldArray.fliter(function(val){
  return val < 6;
});

數組排序sort

數組有排序的功能(會改變原數組,而且也會返回),若是不帶參數,默認是按字符串排序,若是要改變排序方式,能夠在裏面增長比較函數,規則以下

  • 負數:a在b前
  • 大於:b在a前
var array = [1, 12, 21, 2];
//降序排序
array.sort(function(a,b){
   return  b-a;
});
//升序排序
array.sort(function(a,b){
   return  a-b;
});

逆轉數組reverse

改變原數組

var array = [1,2,3,4,5,6,7];
array.reverse();

數組拼接concat

var oldArray = [1,2,3];
var newArray = [];
var concatMe = [4,5,6];
newArray = oldArray.concat(concatMe);

字符串和數組轉換

  • 用split切割字符串
var string = "Split me into an array";
var array = [];
array = string.split(' ');
  • 用joint把數組拼接成字符串
var joinMe = ["Split","me","into","an","array"];
var joinedString = '';
joinedString = joinMe.join(' ');

splice函數

array.splice(start, deleteCount[, item1[, item2[, ...]]])

js提供了一個splice函數,用來刪除index位置處的deleteCount數目的元素,而且在index處加入item1,2,3……(能夠不加入)
這個函數能夠用來替換數組內的部分元素

var myFish = ['angel', 'clown', 'mandarin', 'surgeon'];
// removes 0 elements from index 2, and inserts 'drum'
var removed = myFish.splice(2, 0, 'drum');
// myFish is ['angel', 'clown', 'drum', 'mandarin', 'surgeon']
// removed is [], no elements removed

removed = myFish.splice(3, 1);
// myFish is ['angel', 'clown', 'drum', 'surgeon']
// removed is ['mandarin']

removed = myFish.splice(2, 1, 'trumpet');
// myFish is ['angel', 'clown', 'trumpet', 'surgeon']
// removed is ['drum']

slice函數

arr.slice([begin[, end]])

取出數組的從begin到end的元素,從新組成數組。

var fruits = ['Banana', 'Orange', 'Lemon', 'Apple', 'Mango'];
var citrus = fruits.slice(1, 3);// ['Orange','Lemon']
相關文章
相關標籤/搜索