入門
數據類型和變量
數據類型
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
來聲明常量 - 常量不可重新賦值,會報錯
-
const
和let
一樣,是塊級作用域
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還有另一個巨大的好處,就是把異步回調代碼變成“同步”代碼。