==============part1==========
1.面向過程與面向?qū)ο?/h2>
1.1面向過程
- 面向過程就是分析出解決問題所需要的步驟卷要,然后用函數(shù)把這些步驟一步一步實現(xiàn),使用的時候再一個一個的依次調(diào)用就可以了徒恋。
1.2面向?qū)ο?/h3>
- 面向?qū)ο笫前咽聞?wù)分解成為一個個對象,然后由對象之間分工與合作捏题。
1.3面向過程與面向?qū)ο髮Ρ?/h3>
- 面向?qū)ο笫前咽聞?wù)分解成為一個個對象,然后由對象之間分工與合作捏题。
1.3面向過程與面向?qū)ο髮Ρ?/h3>
面向過程 | 面向?qū)ο?/th> | |
---|---|---|
優(yōu)點 | 性能比面向?qū)ο蟾吣蕴猓m合跟硬件聯(lián)系很緊密的東西,例如單片機就采用的面向過程編程屏轰。 | 易維護颊郎、易復用、易擴展霎苗,由于面向?qū)ο笥蟹庋b姆吭、繼承、多態(tài)性的特性唁盏,可以設(shè)計出低耦合的系統(tǒng)内狸,使系統(tǒng) 更加靈活、更加易于維護 |
缺點 | 不易維護厘擂、不易復用昆淡、不易擴展 | 性能比面向過程低 |
2.對象與類
2.1對象
對象是由屬性和方法組成的:是一個無序鍵值對的集合,指的是一個具體的事物
- 屬性:事物的特征,在對象中用屬性來表示(常用名詞)
- 方法:事物的行為刽严,在對象中用方法來表示(常用動詞)
2.1.1創(chuàng)建對象
//以下代碼是對對象的復習
//字面量創(chuàng)建對象
var ldh = {
name: '劉德華',
age: 18
}
console.log(ldh);
//構(gòu)造函數(shù)創(chuàng)建對象
function Star(name, age) {
this.name = name;
this.age = age;
}
var ldh = new Star('劉德華', 18)//實例化對象
console.log(ldh);
2.2類
- 在 ES6 中新增加了類的概念昂灵,可以使用 class 關(guān)鍵字聲明一個類,之后以這個類來實例化對象舞萄。類抽象了對象的公共部分眨补,它泛指某一大類(class)對象特指某一個,通過類實例化一個具體的對象
2.2.1創(chuàng)建類
- 語法:
//步驟1 使用class關(guān)鍵字
class name {
// class body
}
//步驟2使用定義的類創(chuàng)建實例 注意new關(guān)鍵字
var xx = new name();
- 示例
// 1. 創(chuàng)建類 class 創(chuàng)建一個 明星類
class Star {
// 類的共有屬性放到 constructor 里面
constructor(name, age) {
this.name = name;
this.age = age;
}
}
// 2. 利用類創(chuàng)建對象 new
var ldh = new Star('劉德華', 18);
console.log(ldh);
以上代碼運行結(jié)果:
通過結(jié)果我們可以看出,運行結(jié)果和使用構(gòu)造函數(shù)方式一樣
2.2.2類創(chuàng)建添加屬性和方法
// 1. 創(chuàng)建類 class 創(chuàng)建一個類
class Star {
// 類的共有屬性放到 constructor 里面 constructor是 構(gòu)造器或者構(gòu)造函數(shù)
constructor(uname, age) {
this.uname = uname;
this.age = age;
}//------------------------------------------->注意,方法與方法之間不需要添加逗號
sing(song) {
console.log(this.uname + '唱' + song);
}
}
// 2. 利用類創(chuàng)建對象 new
var ldh = new Star('劉德華', 18);
console.log(ldh); // Star {uname: "劉德華", age: 18}
ldh.sing('冰雨'); // 劉德華唱冰雨
以上代碼運行結(jié)果:
注意:
- 通過class 關(guān)鍵字創(chuàng)建類, 類名我們還是習慣性定義首字母大寫
- 類里面有個constructor 函數(shù),可以接受傳遞過來的參數(shù),同時返回實例對象
- constructor 函數(shù) 只要 new 生成實例時,就會自動調(diào)用這個函數(shù), 如果我們不寫這個函數(shù),類也會自動生成這個函數(shù)
- 多個函數(shù)方法之間不需要添加逗號分隔
- 生成實例 new 不能省略
- 語法規(guī)范, 創(chuàng)建類 類名后面不要加小括號,生成實例 類名后面加小括號, 構(gòu)造函數(shù)不需要加function
2.2.3類的繼承
- 語法
// 父類
class Father{
}
// 子類繼承父類
class Son extends Father {
}
- 示例
class Father {
constructor(surname) {
this.surname= surname;
}
say() {
console.log('你的姓是' + this.surname);
}
}
class Son extends Father{ // 這樣子類就繼承了父類的屬性和方法
}
var damao= new Son('劉');
damao.say(); //結(jié)果為 你的姓是劉
以上代碼運行結(jié)果:
-
子類使用super關(guān)鍵字訪問父類的方法
//定義了父類 class Father { constructor(x, y) { this.x = x; this.y = y; } sum() { console.log(this.x + this.y); } } //子元素繼承父類 class Son extends Father { constructor(x, y) { super(x, y); //使用super調(diào)用了父類中的構(gòu)造函數(shù) } } var son = new Son(1, 2); son.sum(); //結(jié)果為3
注意:
繼承中,如果實例化子類輸出一個方法,先看子類有沒有這個方法,如果有就先執(zhí)行子類的
繼承中,如果子類里面沒有,就去查找父類有沒有這個方法,如果有,就執(zhí)行父類的這個方法(就近原則)
-
如果子類想要繼承父類的方法,同時在自己內(nèi)部擴展自己的方法,利用super 調(diào)用父類的構(gòu)造函數(shù),super 必須在子類this之前調(diào)用
// 父類有加法方法 class Father { constructor(x, y) { this.x = x; this.y = y; } sum() { console.log(this.x + this.y); } } // 子類繼承父類加法方法 同時 擴展減法方法 class Son extends Father { constructor(x, y) { // 利用super 調(diào)用父類的構(gòu)造函數(shù) super 必須在子類this之前調(diào)用,放到this之后會報錯 super(x, y); this.x = x; this.y = y; } subtract() { console.log(this.x - this.y); } } var son = new Son(5, 3); son.subtract(); //2 son.sum();//8
以上代碼運行結(jié)果為:
-
時刻注意this的指向問題,類里面的共有的屬性和方法一定要加this使用.
- constructor中的this指向的是new出來的實例對象
- 自定義的方法,一般也指向的new出來的實例對象
- 綁定事件之后this指向的就是觸發(fā)事件的事件源
在 ES6 中類沒有變量提升倒脓,所以必須先定義類撑螺,才能通過類實例化對象
3.面向?qū)ο蟀鎡ab 欄切換
3.1功能需求
- 點擊 tab欄,可以切換效果.
- 點擊 + 號, 可以添加 tab 項和內(nèi)容項.
- 點擊 x 號, 可以刪除當前的tab項和內(nèi)容項.
- 雙擊tab項文字或者內(nèi)容項文字可以修改里面的文字內(nèi)容
3.2案例準備
- 獲取到標題元素
- 獲取到內(nèi)容元素
- 獲取到刪除的小按鈕 x號
- 新建js文件,定義類,添加需要的屬性方法(切換,刪除,增加,修改)
- 時刻注意this的指向問題
3.3切換
-
為獲取到的標題綁定點擊事件,展示對應(yīng)的內(nèi)容區(qū)域,存儲對應(yīng)的索引
this.lis[i].index = i; this.lis[i].onclick = this.toggleTab;
-
使用排他,實現(xiàn)只有一個元素的顯示
toggleTab() { //將所有的標題與內(nèi)容類樣式全部移除 for (var i = 0; i < this.lis.length; i++) { this.lis[i].className = ''; this.sections[i].className = ''; } //為當前的標題添加激活樣式 this.className = 'liactive'; //為當前的內(nèi)容添加激活樣式 that.sections[this.index].className = 'conactive'; }
3.4添加
-
為添加按鈕+ 綁定點擊事件
this.add.onclick = this.addTab;
-
實現(xiàn)標題與內(nèi)容的添加,做好排他處理
addTab() { that.clearClass(); // (1) 創(chuàng)建li元素和section元素 var random = Math.random(); var li = '<li class="liactive"><span>新選項卡</span><span class="iconfont icon-guanbi"> </span></li>'; var section = '<section class="conactive">測試 ' + random + '</section>'; // (2) 把這兩個元素追加到對應(yīng)的父元素里面 that.ul.insertAdjacentHTML('beforeend', li); that.fsection.insertAdjacentHTML('beforeend', section); that.init(); }
3.5刪除
-
為元素的刪除按鈕x綁定點擊事件
this.remove[i].onclick = this.removeTab;
-
獲取到點擊的刪除按鈕的所在的父元素的所有,刪除對應(yīng)的標題與內(nèi)容
removeTab(e) { e.stopPropagation(); // 阻止冒泡 防止觸發(fā)li 的切換點擊事件 var index = this.parentNode.index; console.log(index); // 根據(jù)索引號刪除對應(yīng)的li 和section remove()方法可以直接刪除指定的元素 that.lis[index].remove(); that.sections[index].remove(); that.init(); // 當我們刪除的不是選中狀態(tài)的li 的時候,原來的選中狀態(tài)li保持不變 if (document.querySelector('.liactive')) return; // 當我們刪除了選中狀態(tài)的這個li 的時候, 讓它的前一個li 處于選定狀態(tài) index--; // 手動調(diào)用我們的點擊事件 不需要鼠標觸發(fā) that.lis[index] && that.lis[index].click(); }
3.6編輯
-
為元素(標題與內(nèi)容)綁定雙擊事件
this.spans[i].ondblclick = this.editTab; this.sections[i].ondblclick = this.editTab;
-
在雙擊事件處理文本選中狀態(tài),修改內(nèi)部DOM節(jié)點,實現(xiàn)新舊value值的傳遞
editTab() { var str = this.innerHTML; // 雙擊禁止選定文字 window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty(); // alert(11); this.innerHTML = '<input type="text" />'; var input = this.children[0]; input.value = str; input.select(); // 文本框里面的文字處于選定狀態(tài) // 當我們離開文本框就把文本框里面的值給span input.onblur = function() { this.parentNode.innerHTML = this.value; }; // 按下回車也可以把文本框里面的值給span input.onkeyup = function(e) { if (e.keyCode === 13) { // 手動調(diào)用表單失去焦點事件 不需要鼠標離開操作 this.blur(); } } }
==============part2==========
1.構(gòu)造函數(shù)和原型
1.1對象的三種創(chuàng)建方式--復習
-
字面量方式
var obj = {};
-
new關(guān)鍵字
var obj = new Object();
-
構(gòu)造函數(shù)方式
function Person(name,age){ this.name = name; this.age = age; } var obj = new Person('zs',12);
1.2靜態(tài)成員和實例成員
1.2.1實例成員
實例成員就是構(gòu)造函數(shù)內(nèi)部通過this添加的成員 如下列代碼中uname age sing 就是實例成員,實例成員只能通過實例化的對象來訪問
function Star(uname, age) {
this.uname = uname;
this.age = age;
this.sing = function() {
console.log('我會唱歌');
}
}
var ldh = new Star('劉德華', 18);
console.log(ldh.uname);//實例成員只能通過實例化的對象來訪問
1.2.2靜態(tài)成員
靜態(tài)成員 在構(gòu)造函數(shù)本身上添加的成員 如下列代碼中 sex 就是靜態(tài)成員,靜態(tài)成員只能通過構(gòu)造函數(shù)來訪問
function Star(uname, age) {
this.uname = uname;
this.age = age;
this.sing = function() {
console.log('我會唱歌');
}
}
Star.sex = '男';
var ldh = new Star('劉德華', 18);
console.log(Star.sex);//靜態(tài)成員只能通過構(gòu)造函數(shù)來訪問
1.3構(gòu)造函數(shù)的問題
構(gòu)造函數(shù)方法很好用,但是存在浪費內(nèi)存的問題崎弃。
1.4構(gòu)造函數(shù)原型prototype
構(gòu)造函數(shù)通過原型分配的函數(shù)是所有對象所共享的甘晤。
JavaScript 規(guī)定,每一個構(gòu)造函數(shù)都有一個prototype 屬性吊履,指向另一個對象安皱。注意這個prototype就是一個對象,這個對象的所有屬性和方法艇炎,都會被構(gòu)造函數(shù)所擁有酌伊。
我們可以把那些不變的方法,直接定義在 prototype 對象上,這樣所有對象的實例就可以共享這些方法居砖。
function Star(uname, age) {
this.uname = uname;
this.age = age;
}
Star.prototype.sing = function() {
console.log('我會唱歌');
}
var ldh = new Star('劉德華', 18);
var zxy = new Star('張學友', 19);
ldh.sing();//我會唱歌
zxy.sing();//我會唱歌
1.5對象原型
對象都會有一個屬性 proto 指向構(gòu)造函數(shù)的 prototype 原型對象虹脯,之所以我們對象可以使用構(gòu)造函數(shù) prototype 原型對象的屬性和方法,就是因為對象有 proto 原型的存在奏候。
proto對象原型和原型對象 prototype 是等價的
proto對象原型的意義就在于為對象的查找機制提供一個方向循集,或者說一條路線,但是它是一個非標準屬性蔗草,因此實際開發(fā)中咒彤,不可以使用這個屬性,它只是內(nèi)部指向原型對象 prototype
1.6constructor構(gòu)造函數(shù)
對象原型( __proto__)和構(gòu)造函數(shù)(prototype)原型對象里面都有一個屬性 constructor 屬性 咒精,constructor 我們稱為構(gòu)造函數(shù)镶柱,因為它指回構(gòu)造函數(shù)本身。
constructor 主要用于記錄該對象引用于哪個構(gòu)造函數(shù)模叙,它可以讓原型對象重新指向原來的構(gòu)造函數(shù)歇拆。
一般情況下,對象的方法都在構(gòu)造函數(shù)的原型對象中設(shè)置范咨。如果有多個對象的方法故觅,我們可以給原型對象采取對象形式賦值,但是這樣就會覆蓋構(gòu)造函數(shù)原型對象原來的內(nèi)容渠啊,這樣修改后的原型對象 constructor 就不再指向當前構(gòu)造函數(shù)了输吏。此時,我們可以在修改后的原型對象中昭抒,添加一個 constructor 指向原來的構(gòu)造函數(shù)评也。
如果我們修改了原來的原型對象,給原型對象賦值的是一個對象,則必須手動的利用constructor指回原來的構(gòu)造函數(shù)如:
function Star(uname, age) {
this.uname = uname;
this.age = age;
}
// 很多情況下,我們需要手動的利用constructor 這個屬性指回 原來的構(gòu)造函數(shù)
Star.prototype = {
// 如果我們修改了原來的原型對象,給原型對象賦值的是一個對象,則必須手動的利用constructor指回原來的構(gòu)造函數(shù)
constructor: Star, // 手動設(shè)置指回原來的構(gòu)造函數(shù)
sing: function() {
console.log('我會唱歌');
},
movie: function() {
console.log('我會演電影');
}
}
var zxy = new Star('張學友', 19);
console.log(zxy)
以上代碼運行結(jié)果,設(shè)置constructor屬性如圖:
如果未設(shè)置constructor屬性,如圖:
1.7原型鏈
每一個實例對象又有一個__proto__屬性,指向的構(gòu)造函數(shù)的原型對象灭返,構(gòu)造函數(shù)的原型對象也是一個對象盗迟,也有__proto__屬性,這樣一層一層往上找就形成了原型鏈熙含。
1.8構(gòu)造函數(shù)實例和原型對象三角關(guān)系
1.構(gòu)造函數(shù)的prototype屬性指向了構(gòu)造函數(shù)原型對象
2.實例對象是由構(gòu)造函數(shù)創(chuàng)建的,實例對象的proto屬性指向了構(gòu)造函數(shù)的原型對象
3.構(gòu)造函數(shù)的原型對象的constructor屬性指向了構(gòu)造函數(shù),實例對象的原型的constructor屬性也指向了構(gòu)造函數(shù)
1.9原型鏈和成員的查找機制
任何對象都有原型對象,也就是prototype屬性,任何原型對象也是一個對象,該對象就有proto屬性,這樣一層一層往上找,就形成了一條鏈,我們稱此為原型鏈;
當訪問一個對象的屬性(包括方法)時罚缕,首先查找這個對象自身有沒有該屬性。
如果沒有就查找它的原型(也就是 proto指向的 prototype 原型對象)怎静。
如果還沒有就查找原型對象的原型(Object的原型對象)邮弹。
依此類推一直找到 Object 為止(null)。
proto對象原型的意義就在于為對象成員查找機制提供一個方向蚓聘,或者說一條路線腌乡。
1.10原型對象中this指向
構(gòu)造函數(shù)中的this和原型對象的this,都指向我們new出來的實例對象
function Star(uname, age) {
this.uname = uname;
this.age = age;
}
var that;
Star.prototype.sing = function() {
console.log('我會唱歌');
that = this;
}
var ldh = new Star('劉德華', 18);
// 1. 在構(gòu)造函數(shù)中,里面this指向的是對象實例 ldh
console.log(that === ldh);//true
// 2.原型對象函數(shù)里面的this 指向的是 實例對象 ldh
1.11通過原型為數(shù)組擴展內(nèi)置方法
Array.prototype.sum = function() {
var sum = 0;
for (var i = 0; i < this.length; i++) {
sum += this[i];
}
return sum;
};
//此時數(shù)組對象中已經(jīng)存在sum()方法了 可以始終 數(shù)組.sum()進行數(shù)據(jù)的求
2.繼承
2.1call()
- call()可以調(diào)用函數(shù)
- call()可以修改this的指向,使用call()的時候 參數(shù)一是修改后的this指向,參數(shù)2,參數(shù)3..使用逗號隔開連接
function fn(x, y) {
console.log(this);
console.log(x + y);
}
var o = {
name: 'andy'
};
fn.call(o, 1, 2);//調(diào)用了函數(shù)此時的this指向了對象o,
2.2子構(gòu)造函數(shù)繼承父構(gòu)造函數(shù)中的屬性
- 先定義一個父構(gòu)造函數(shù)
- 再定義一個子構(gòu)造函數(shù)
- 子構(gòu)造函數(shù)繼承父構(gòu)造函數(shù)的屬性(使用call方法)
// 1. 父構(gòu)造函數(shù)
function Father(uname, age) {
// this 指向父構(gòu)造函數(shù)的對象實例
this.uname = uname;
this.age = age;
}
// 2 .子構(gòu)造函數(shù)
function Son(uname, age, score) {
// this 指向子構(gòu)造函數(shù)的對象實例
3.使用call方式實現(xiàn)子繼承父的屬性
Father.call(this, uname, age);
this.score = score;
}
var son = new Son('劉德華', 18, 100);
console.log(son);
2.3借用原型對象繼承方法
- 先定義一個父構(gòu)造函數(shù)
- 再定義一個子構(gòu)造函數(shù)
- 子構(gòu)造函數(shù)繼承父構(gòu)造函數(shù)的屬性(使用call方法)
// 1. 父構(gòu)造函數(shù)
function Father(uname, age) {
// this 指向父構(gòu)造函數(shù)的對象實例
this.uname = uname;
this.age = age;
}
Father.prototype.money = function() {
console.log(100000);
};
// 2 .子構(gòu)造函數(shù)
function Son(uname, age, score) {
// this 指向子構(gòu)造函數(shù)的對象實例
Father.call(this, uname, age);
this.score = score;
}
// Son.prototype = Father.prototype; 這樣直接賦值會有問題,如果修改了子原型對象,父原型對象也會跟著一起變化
Son.prototype = new Father();
// 如果利用對象的形式修改了原型對象,別忘了利用constructor 指回原來的構(gòu)造函數(shù)
Son.prototype.constructor = Son;
// 這個是子構(gòu)造函數(shù)專門的方法
Son.prototype.exam = function() {
console.log('孩子要考試');
}
var son = new Son('劉德華', 18, 100);
console.log(son);
如上代碼結(jié)果如圖:
3.ES5新增方法
3.1數(shù)組方法forEach遍歷數(shù)組
arr.forEach(function(value, index, array) {
//參數(shù)一是:數(shù)組元素
//參數(shù)二是:數(shù)組元素的索引
//參數(shù)三是:當前的數(shù)組
})
//相當于數(shù)組遍歷的 for循環(huán) 沒有返回值
3.2數(shù)組方法filter過濾數(shù)組
var arr = [12, 66, 4, 88, 3, 7];
var newArr = arr.filter(function(value, index,array) {
//參數(shù)一是:數(shù)組元素
//參數(shù)二是:數(shù)組元素的索引
//參數(shù)三是:當前的數(shù)組
return value >= 20;
});
console.log(newArr);//[66,88] //返回值是一個新數(shù)組
3.3數(shù)組方法some
some 查找數(shù)組中是否有滿足條件的元素
var arr = [10, 30, 4];
var flag = arr.some(function(value,index,array) {
//參數(shù)一是:數(shù)組元素
//參數(shù)二是:數(shù)組元素的索引
//參數(shù)三是:當前的數(shù)組
return value < 3;
});
console.log(flag);//false返回值是布爾值,只要查找到滿足條件的一個元素就立馬終止循環(huán)
3.4篩選商品案例
-
定義數(shù)組對象數(shù)據(jù)
var data = [{ id: 1, pname: '小米', price: 3999 }, { id: 2, pname: 'oppo', price: 999 }, { id: 3, pname: '榮耀', price: 1299 }, { id: 4, pname: '華為', price: 1999 }, ];
-
使用forEach遍歷數(shù)據(jù)并渲染到頁面中
data.forEach(function(value) { var tr = document.createElement('tr'); tr.innerHTML = '<td>' + value.id + '</td><td>' + value.pname + '</td><td>' + value.price + '</td>'; tbody.appendChild(tr); });
-
根據(jù)價格篩選數(shù)據(jù)
-
獲取到搜索按鈕并為其綁定點擊事件
search_price.addEventListener('click', function() { });
-
使用filter將用戶輸入的價格信息篩選出來
search_price.addEventListener('click', function() { var newDate = data.filter(function(value) { //start.value是開始區(qū)間 //end.value是結(jié)束的區(qū)間 return value.price >= start.value && value.price <= end.value; }); console.log(newDate); });
-
將篩選出來的數(shù)據(jù)重新渲染到表格中
-
將渲染數(shù)據(jù)的邏輯封裝到一個函數(shù)中
function setDate(mydata) { // 先清空原來tbody 里面的數(shù)據(jù) tbody.innerHTML = ''; mydata.forEach(function(value) { var tr = document.createElement('tr'); tr.innerHTML = '<td>' + value.id + '</td><td>' + value.pname + '</td><td>' + value.price + '</td>'; tbody.appendChild(tr); }); }
-
將篩選之后的數(shù)據(jù)重新渲染
search_price.addEventListener('click', function() { var newDate = data.filter(function(value) { return value.price >= start.value && value.price <= end.value; }); console.log(newDate); // 把篩選完之后的對象渲染到頁面中 setDate(newDate); });
-
-
根據(jù)商品名稱篩選
獲取用戶輸入的商品名稱
-
為查詢按鈕綁定點擊事件,將輸入的商品名稱與這個數(shù)據(jù)進行篩選
search_pro.addEventListener('click', function() { var arr = []; data.some(function(value) { if (value.pname === product.value) { // console.log(value); arr.push(value); return true; // return 后面必須寫true } }); // 把拿到的數(shù)據(jù)渲染到頁面中 setDate(arr); })
-
3.5some和forEach區(qū)別
- 如果查詢數(shù)組中唯一的元素, 用some方法更合適,在some 里面 遇到 return true 就是終止遍歷 迭代效率更高
- 在forEach 里面 return 不會終止迭代
3.6trim方法去除字符串兩端的空格
var str = ' hello '
console.log(str.trim()) //hello 去除兩端空格
var str1 = ' he l l o '
console.log(str.trim()) //he l l o 去除兩端空格
3.7獲取對象的屬性名
Object.keys(對象) 獲取到當前對象中的屬性名 ,返回值是一個數(shù)組
var obj = {
id: 1,
pname: '小米',
price: 1999,
num: 2000
};
var result = Object.keys(obj)
console.log(result)//[id夜牡,pname,price,num]
3.8Object.defineProperty
Object.defineProperty設(shè)置或修改對象中的屬性
Object.defineProperty(對象与纽,修改或新增的屬性名,{
value:修改或新增的屬性的值,
writable:true/false,//如果值為false 不允許修改這個屬性值
enumerable: false,//enumerable 如果值為false 則不允許遍歷
configurable: false //configurable 如果為false 則不允許刪除這個屬性 屬性是否可以被刪除或是否可以再次修改特性
})
==============part3==========
1.函數(shù)的定義和調(diào)用
1.1函數(shù)的定義方式
-
方式1 函數(shù)聲明方式 function 關(guān)鍵字 (命名函數(shù))
function fn(){}
-
方式2 函數(shù)表達式(匿名函數(shù))
var fn = function(){}
-
方式3 new Function()
var f = new Function('a', 'b', 'console.log(a + b)'); f(1, 2); var fn = new Function('參數(shù)1','參數(shù)2'..., '函數(shù)體') 注意 /*Function 里面參數(shù)都必須是字符串格式 第三種方式執(zhí)行效率低,也不方便書寫急迂,因此較少使用 所有函數(shù)都是 Function 的實例(對象) 函數(shù)也屬于對象 */
1.2函數(shù)的調(diào)用
/* 1. 普通函數(shù) */
function fn() {
console.log('人生的巔峰');
}
fn();
/* 2. 對象的方法 */
var o = {
sayHi: function() {
console.log('人生的巔峰');
}
}
o.sayHi();
/* 3. 構(gòu)造函數(shù)*/
function Star() {};
new Star();
/* 4. 綁定事件函數(shù)*/
btn.onclick = function() {}; // 點擊了按鈕就可以調(diào)用這個函數(shù)
/* 5. 定時器函數(shù)*/
setInterval(function() {}, 1000); 這個函數(shù)是定時器自動1秒鐘調(diào)用一次
/* 6. 立即執(zhí)行函數(shù)(自調(diào)用函數(shù))*/
(function() {
console.log('人生的巔峰');
})();
2.this
2.1函數(shù)內(nèi)部的this指向
這些 this 的指向吏够,是當我們調(diào)用函數(shù)的時候確定的米罚。調(diào)用方式的不同決定了this 的指向不同
一般指向我們的調(diào)用者.
2.2改變函數(shù)內(nèi)部 this 指向
2.2.1 call方法
call()方法調(diào)用一個對象经柴。簡單理解為調(diào)用函數(shù)的方式抠藕,但是它可以改變函數(shù)的 this 指向
應(yīng)用場景: 經(jīng)常做繼承.
var o = {
name: 'andy'
}
function fn(a, b) {
console.log(this);
console.log(a+b)
};
fn(1,2)// 此時的this指向的是window 運行結(jié)果為3
fn.call(o,1,2)//此時的this指向的是對象o,參數(shù)使用逗號隔開,運行結(jié)果為3
以上代碼運行結(jié)果為:
2.2.2 apply方法
apply() 方法調(diào)用一個函數(shù)。簡單理解為調(diào)用函數(shù)的方式勺阐,但是它可以改變函數(shù)的 this 指向卷中。
應(yīng)用場景: 經(jīng)常跟數(shù)組有關(guān)系
var o = {
name: 'andy'
}
function fn(a, b) {
console.log(this);
console.log(a+b)
};
fn()// 此時的this指向的是window 運行結(jié)果為3
fn.apply(o,[1,2])//此時的this指向的是對象o,參數(shù)使用數(shù)組傳遞 運行結(jié)果為3
2.2.3 bind方法
bind() 方法不會調(diào)用函數(shù),但是能改變函數(shù)內(nèi)部this 指向,返回的是原函數(shù)改變this之后產(chǎn)生的新函數(shù)
如果只是想改變 this 指向,并且不想調(diào)用這個函數(shù)的時候渊抽,可以使用bind
應(yīng)用場景:不調(diào)用函數(shù),但是還想改變this指向
var o = {
name: 'andy'
};
function fn(a, b) {
console.log(this);
console.log(a + b);
};
var f = fn.bind(o, 1, 2); //此處的f是bind返回的新函數(shù)
f();//調(diào)用新函數(shù) this指向的是對象o 參數(shù)使用逗號隔開
2.2.4 call仓坞、apply、bind三者的異同
- 共同點 : 都可以改變this指向
- 不同點:
- call 和 apply 會調(diào)用函數(shù), 并且改變函數(shù)內(nèi)部this指向.
- call 和 apply傳遞的參數(shù)不一樣,call傳遞參數(shù)使用逗號隔開,apply使用數(shù)組傳遞
- bind 不會調(diào)用函數(shù), 可以改變函數(shù)內(nèi)部this指向.
- 應(yīng)用場景
- call 經(jīng)常做繼承.
- apply經(jīng)常跟數(shù)組有關(guān)系. 比如借助于數(shù)學對象實現(xiàn)數(shù)組最大值最小值
- bind 不調(diào)用函數(shù),但是還想改變this指向. 比如改變定時器內(nèi)部的this指向.
3.嚴格模式
3.1什么是嚴格模式
JavaScript 除了提供正常模式外腰吟,還提供了嚴格模式(strict mode)。ES5 的嚴格模式是采用具有限制性 JavaScript變體的一種方式徙瓶,即在嚴格的條件下運行 JS 代碼毛雇。
嚴格模式在 IE10 以上版本的瀏覽器中才會被支持,舊版本瀏覽器中會被忽略侦镇。
嚴格模式對正常的 JavaScript 語義做了一些更改:
1.消除了 Javascript 語法的一些不合理灵疮、不嚴謹之處,減少了一些怪異行為壳繁。
2.消除代碼運行的一些不安全之處震捣,保證代碼運行的安全。
3.提高編譯器效率闹炉,增加運行速度蒿赢。
4.禁用了在 ECMAScript 的未來版本中可能會定義的一些語法,為未來新版本的 Javascript 做好鋪墊渣触。比如一些保留字如:class,enum,export, extends, import, super 不能做變量名
3.2開啟嚴格模式
嚴格模式可以應(yīng)用到整個腳本或個別函數(shù)中羡棵。因此在使用時,我們可以將嚴格模式分為為腳本開啟嚴格模式和為函數(shù)開啟嚴格模式兩種情況嗅钻。
-
情況一 :為腳本開啟嚴格模式
-
有的 script 腳本是嚴格模式皂冰,有的 script 腳本是正常模式,這樣不利于文件合并养篓,所以可以將整個腳本文件放在一個立即執(zhí)行的匿名函數(shù)之中秃流。這樣獨立創(chuàng)建一個作用域而不影響其他
script 腳本文件。(function (){ //在當前的這個自調(diào)用函數(shù)中有開啟嚴格模式柳弄,當前函數(shù)之外還是普通模式 "use strict"; var num = 10; function fn() {} })(); //或者 <script> "use strict"; //當前script標簽開啟了嚴格模式 </script> <script> //當前script標簽未開啟嚴格模式 </script>
-
-
情況二: 為函數(shù)開啟嚴格模式
-
要給某個函數(shù)開啟嚴格模式舶胀,需要把“use strict”; (或 'use strict'; ) 聲明放在函數(shù)體所有語句之前。
function fn(){ "use strict"; return "123"; } //當前fn函數(shù)開啟了嚴格模式
-
3.3嚴格模式中的變化
嚴格模式對 Javascript 的語法和行為,都做了一些改變峻贮。
'use strict'
num = 10
console.log(num)//嚴格模式后使用未聲明的變量
--------------------------------------------------------------------------------
var num2 = 1;
delete num2;//嚴格模式不允許刪除變量
--------------------------------------------------------------------------------
function fn() {
console.log(this); // 嚴格模式下全局作用域中函數(shù)中的 this 是 undefined
}
fn();
---------------------------------------------------------------------------------
function Star() {
this.sex = '男';
}
// Star();嚴格模式下,如果 構(gòu)造函數(shù)不加new調(diào)用, this 指向的是undefined 如果給他賦值則 會報錯.
var ldh = new Star();
console.log(ldh.sex);
----------------------------------------------------------------------------------
setTimeout(function() {
console.log(this); //嚴格模式下席怪,定時器 this 還是指向 window
}, 2000);
4.高階函數(shù)
高階函數(shù)是對其他函數(shù)進行操作的函數(shù),它接收函數(shù)作為參數(shù)或?qū)⒑瘮?shù)作為返回值輸出纤控。
此時fn 就是一個高階函數(shù)
函數(shù)也是一種數(shù)據(jù)類型挂捻,同樣可以作為參數(shù),傳遞給另外一個參數(shù)使用船万。最典型的就是作為回調(diào)函數(shù)刻撒。
同理函數(shù)也可以作為返回值傳遞回來
5.閉包
5.1變量的作用域復習
變量根據(jù)作用域的不同分為兩種:全局變量和局部變量。
- 函數(shù)內(nèi)部可以使用全局變量耿导。
- 函數(shù)外部不可以使用局部變量声怔。
- 當函數(shù)執(zhí)行完畢,本作用域內(nèi)的局部變量會銷毀舱呻。
5.2什么是閉包
閉包(closure)指有權(quán)訪問另一個函數(shù)作用域中變量的函數(shù)醋火。簡單理解就是 ,一個作用域可以訪問另外一個函數(shù)內(nèi)部的局部變量箱吕。
5.3閉包的作用
作用:延伸變量的作用范圍芥驳。
function fn() {
var num = 10;
function fun() {
console.log(num);
}
return fun;
}
var f = fn();
f();
5.4閉包的案例
- 利用閉包的方式得到當前l(fā)i 的索引號
for (var i = 0; i < lis.length; i++) {
// 利用for循環(huán)創(chuàng)建了4個立即執(zhí)行函數(shù)
// 立即執(zhí)行函數(shù)也成為小閉包因為立即執(zhí)行函數(shù)里面的任何一個函數(shù)都可以使用它的i這變量
(function(i) {
lis[i].onclick = function() {
console.log(i);
}
})(i);
}
- 閉包應(yīng)用-3秒鐘之后,打印所有l(wèi)i元素的內(nèi)容
for (var i = 0; i < lis.length; i++) {
(function(i) {
setTimeout(function() {
console.log(lis[i].innerHTML);
}, 3000)
})(i);
}
- 閉包應(yīng)用-計算打車價格
/*需求分析
打車起步價13(3公里內(nèi)), 之后每多一公里增加 5塊錢. 用戶輸入公里數(shù)就可以計算打車價格
如果有擁堵情況,總價格多收取10塊錢擁堵費*/
var car = (function() {
var start = 13; // 起步價 局部變量
var total = 0; // 總價 局部變量
return {
// 正常的總價
price: function(n) {
if (n <= 3) {
total = start;
} else {
total = start + (n - 3) * 5
}
return total;
},
// 擁堵之后的費用
yd: function(flag) {
return flag ? total + 10 : total;
}
}
})();
console.log(car.price(5)); // 23
console.log(car.yd(true)); // 33
5.5案例
var name = "The Window";
var object = {
name: "My Object",
getNameFunc: function() {
return function() {
return this.name;
};
}
};
console.log(object.getNameFunc()())
-----------------------------------------------------------------------------------
var name = "The Window";
var object = {
name: "My Object",
getNameFunc: function() {
var that = this;
return function() {
return that.name;
};
}
};
console.log(object.getNameFunc()())
6.遞歸
6.1什么是遞歸
遞歸:如果一個函數(shù)在內(nèi)部可以調(diào)用其本身,那么這個函數(shù)就是遞歸函數(shù)茬高。簡單理解:函數(shù)內(nèi)部自己調(diào)用自己, 這個函數(shù)就是遞歸函數(shù)
注意:遞歸函數(shù)的作用和循環(huán)效果一樣兆旬,由于遞歸很容易發(fā)生“棧溢出”錯誤(stack overflow),所以必須要加退出條件return怎栽。
6.2利用遞歸求1~n的階乘
//利用遞歸函數(shù)求1~n的階乘 1 * 2 * 3 * 4 * ..n
function fn(n) {
if (n == 1) { //結(jié)束條件
return 1;
}
return n * fn(n - 1);
}
console.log(fn(3));
6.3利用遞歸求斐波那契數(shù)列
// 利用遞歸函數(shù)求斐波那契數(shù)列(兔子序列) 1丽猬、1、2熏瞄、3脚祟、5、8巴刻、13愚铡、21...
// 用戶輸入一個數(shù)字 n 就可以求出 這個數(shù)字對應(yīng)的兔子序列值
// 我們只需要知道用戶輸入的n 的前面兩項(n-1 n-2)就可以計算出n 對應(yīng)的序列值
function fb(n) {
if (n === 1 || n === 2) {
return 1;
}
return fb(n - 1) + fb(n - 2);
}
console.log(fb(3));
6.4利用遞歸遍歷數(shù)據(jù)
// 我們想要做輸入id號,就可以返回的數(shù)據(jù)對象
var data = [{
id: 1,
name: '家電',
goods: [{
id: 11,
gname: '冰箱',
goods: [{
id: 111,
gname: '海爾'
}, {
id: 112,
gname: '美的'
},
]
}, {
id: 12,
gname: '洗衣機'
}]
}, {
id: 2,
name: '服飾'
}];
//1.利用 forEach 去遍歷里面的每一個對象
function getID(json, id) {
var o = {};
json.forEach(function(item) {
// console.log(item); // 2個數(shù)組元素
if (item.id == id) {
// console.log(item);
o = item;
return o;
// 2. 我們想要得里層的數(shù)據(jù) 11 12 可以利用遞歸函數(shù)
// 里面應(yīng)該有g(shù)oods這個數(shù)組并且數(shù)組的長度不為 0
} else if (item.goods && item.goods.length > 0) {
o = getID(item.goods, id);
}
});
return o;
}
==============part4==========
1.正則表達式概述
1.1什么是正則表達式
正則表達式( Regular Expression )是用于匹配字符串中字符組合的模式。在JavaScript中胡陪,正則表達式也是對象沥寥。
正則表通常被用來檢索、替換那些符合某個模式(規(guī)則)的文本柠座,例如驗證表單:用戶名表單只能輸入英文字母邑雅、數(shù)字或者下劃線, 昵稱輸入框中可以輸入中文(匹配)妈经。此外淮野,正則表達式還常用于過濾掉頁面內(nèi)容中的一些敏感詞(替換)捧书,或從字符串中獲取我們想要的特定部分(提取)等 。
其他語言也會使用正則表達式骤星,本階段我們主要是利用JavaScript 正則表達式完成表單驗證经瓷。
1.2 正則表達式的特點
- 靈活性、邏輯性和功能性非常的強洞难。
- 可以迅速地用極簡單的方式達到字符串的復雜控制舆吮。
- 對于剛接觸的人來說,比較晦澀難懂队贱。比如:^\w+([-+.]\w+)@\w+([-.]\w+).\w+([-.]\w+)*$
- 實際開發(fā),一般都是直接復制寫好的正則表達式. 但是要求會使用正則表達式并且根據(jù)實際情況修改正則表達式. 比如用戶名: /^[a-z0-9_-]{3,16}$/
2.正則表達式在js中的使用
2.1正則表達式的創(chuàng)建
在 JavaScript 中色冀,可以通過兩種方式創(chuàng)建一個正則表達式。
方式一:通過調(diào)用RegExp對象的構(gòu)造函數(shù)創(chuàng)建
var regexp = new RegExp(/123/);
console.log(regexp);
方式二:利用字面量創(chuàng)建 正則表達式
var rg = /123/;
2.2測試正則表達式
test() 正則對象方法柱嫌,用于檢測字符串是否符合該規(guī)則锋恬,該對象會返回 true 或 false,其參數(shù)是測試字符串编丘。
var rg = /123/;
console.log(rg.test(123));//匹配字符中是否出現(xiàn)123 出現(xiàn)結(jié)果為true
console.log(rg.test('abc'));//匹配字符中是否出現(xiàn)123 未出現(xiàn)結(jié)果為false
3.正則表達式中的特殊字符
3.1正則表達式的組成
一個正則表達式可以由簡單的字符構(gòu)成与学,比如 /abc/,也可以是簡單和特殊字符的組合嘉抓,比如 /ab*c/ 癣防。其中特殊字符也被稱為元字符,在正則表達式中是具有特殊意義的專用符號掌眠,如 ^ 、$ 幕屹、+ 等蓝丙。
特殊字符非常多,可以參考:
jQuery 手冊:正則表達式部分
[正則測試工具]( <http://tool.oschina.net/regex)
3.2邊界符
正則表達式中的邊界符(位置符)用來提示字符所處的位置望拖,主要有兩個字符
邊界符 | 說明 |
---|---|
^ | 表示匹配行首的文本(以誰開始) |
$ | 表示匹配行尾的文本(以誰結(jié)束) |
如果 ^和 $ 在一起渺尘,表示必須是精確匹配。
var rg = /abc/; // 正則表達式里面不需要加引號 不管是數(shù)字型還是字符串型
// /abc/ 只要包含有abc這個字符串返回的都是true
console.log(rg.test('abc'));
console.log(rg.test('abcd'));
console.log(rg.test('aabcd'));
console.log('---------------------------');
var reg = /^abc/;
console.log(reg.test('abc')); // true
console.log(reg.test('abcd')); // true
console.log(reg.test('aabcd')); // false
console.log('---------------------------');
var reg1 = /^abc$/; // 精確匹配 要求必須是 abc字符串才符合規(guī)范
console.log(reg1.test('abc')); // true
console.log(reg1.test('abcd')); // false
console.log(reg1.test('aabcd')); // false
console.log(reg1.test('abcabc')); // false
3.3字符類
字符類表示有一系列字符可供選擇说敏,只要匹配其中一個就可以了鸥跟。所有可供選擇的字符都放在方括號內(nèi)。
3.3.1 [] 方括號
表示有一系列字符可供選擇盔沫,只要匹配其中一個就可以了
var rg = /[abc]/; // 只要包含有a 或者 包含有b 或者包含有c 都返回為true
console.log(rg.test('andy'));//true
console.log(rg.test('baby'));//true
console.log(rg.test('color'));//true
console.log(rg.test('red'));//false
var rg1 = /^[abc]$/; // 三選一 只有是a 或者是 b 或者是c 這三個字母才返回 true
console.log(rg1.test('aa'));//false
console.log(rg1.test('a'));//true
console.log(rg1.test('b'));//true
console.log(rg1.test('c'));//true
console.log(rg1.test('abc'));//false
----------------------------------------------------------------------------------
var reg = /^[a-z]$/ //26個英文字母任何一個字母返回 true - 表示的是a 到z 的范圍
console.log(reg.test('a'));//true
console.log(reg.test('z'));//true
console.log(reg.test('A'));//false
-----------------------------------------------------------------------------------
//字符組合
var reg1 = /^[a-zA-Z0-9]$/; // 26個英文字母(大寫和小寫都可以)任何一個字母返回 true
------------------------------------------------------------------------------------
//取反 方括號內(nèi)部加上 ^ 表示取反医咨,只要包含方括號內(nèi)的字符,都返回 false 架诞。
var reg2 = /^[^a-zA-Z0-9]$/;
console.log(reg2.test('a'));//false
console.log(reg2.test('B'));//false
console.log(reg2.test(8));//false
console.log(reg2.test('!'));//true
3.3.2量詞符
量詞符用來設(shè)定某個模式出現(xiàn)的次數(shù)拟淮。
量詞 | 說明 |
---|---|
* | 重復0次或更多次 |
+ | 重復1次或更多次 |
? | 重復0次或1次 |
{n} | 重復n次 |
{n,} | 重復n次或更多次 |
{n,m} | 重復n到m次 |
3.3.3用戶名表單驗證
功能需求:
- 如果用戶名輸入合法, 則后面提示信息為: 用戶名合法,并且顏色為綠色
-
如果用戶名輸入不合法, 則后面提示信息為: 用戶名不符合規(guī)范, 并且顏色為紅色
分析:
- 用戶名只能為英文字母,數(shù)字,下劃線或者短橫線組成, 并且用戶名長度為6~16位.
- 首先準備好這種正則表達式模式/$[a-zA-Z0-9-_]{6,16}^/
- 當表單失去焦點就開始驗證.
- 如果符合正則規(guī)范, 則讓后面的span標簽添加 right類.
- 如果不符合正則規(guī)范, 則讓后面的span標簽添加 wrong類.
<input type="text" class="uname"> <span>請輸入用戶名</span>
<script>
// 量詞是設(shè)定某個模式出現(xiàn)的次數(shù)
var reg = /^[a-zA-Z0-9_-]{6,16}$/; // 這個模式用戶只能輸入英文字母 數(shù)字 下劃線 中劃線
var uname = document.querySelector('.uname');
var span = document.querySelector('span');
uname.onblur = function() {
if (reg.test(this.value)) {
console.log('正確的');
span.className = 'right';
span.innerHTML = '用戶名格式輸入正確';
} else {
console.log('錯誤的');
span.className = 'wrong';
span.innerHTML = '用戶名格式輸入不正確';
}
}
</script>
3.3.4 括號總結(jié)
1.大括號 量詞符. 里面表示重復次數(shù)
2.中括號 字符集合。匹配方括號中的任意字符.
3.小括號表示優(yōu)先級
3.4預定義類
預定義類指的是某些常見模式的簡寫方式.
案例:驗證座機號碼
var reg = /^\d{3}-\d{8}|\d{4}-\d{7}$/;
var reg = /^\d{3,4}-\d{7,8}$/;
表單驗證案例
//手機號驗證:/^1[3|4|5|7|8][0-9]{9}$/;
//驗證通過與不通過更換元素的類名與元素中的內(nèi)容
if (reg.test(this.value)) {
// console.log('正確的');
this.nextElementSibling.className = 'success';
this.nextElementSibling.innerHTML = '<i class="success_icon"></i> 恭喜您輸入正確';
} else {
// console.log('不正確');
this.nextElementSibling.className = 'error';
this.nextElementSibling.innerHTML = '<i class="error_icon"></i>格式不正確,請從新輸入 ';
}
//QQ號驗證: /^[1-9]\d{4,}$/;
//昵稱驗證:/^[\u4e00-\u9fa5]{2,8}$/
//驗證通過與不通過更換元素的類名與元素中的內(nèi)容 ,將上一步的匹配代碼進行封裝,多次調(diào)用即可
function regexp(ele, reg) {
ele.onblur = function() {
if (reg.test(this.value)) {
// console.log('正確的');
this.nextElementSibling.className = 'success';
this.nextElementSibling.innerHTML = '<i class="success_icon"></i> 恭喜您輸入正確';
} else {
// console.log('不正確');
this.nextElementSibling.className = 'error';
this.nextElementSibling.innerHTML = '<i class="error_icon"></i> 格式不正確,請從新輸入 ';
}
}
};
//密碼驗證:/^[a-zA-Z0-9_-]{6,16}$/
//再次輸入密碼只需匹配與上次輸入的密碼值 是否一致
3.5正則替換replace
replace() 方法可以實現(xiàn)替換字符串操作谴忧,用來替換的參數(shù)可以是一個字符串或是一個正則表達式很泊。
var str = 'andy和red';
var newStr = str.replace('andy', 'baby');
console.log(newStr)//baby和red
//等同于 此處的andy可以寫在正則表達式內(nèi)
var newStr2 = str.replace(/andy/, 'baby');
console.log(newStr2)//baby和red
//全部替換
var str = 'abcabc'
var nStr = str.replace(/a/,'哈哈')
console.log(nStr) //哈哈bcabc
//全部替換g
var nStr = str.replace(/a/a,'哈哈')
console.log(nStr) //哈哈bc哈哈bc
//忽略大小寫i
var str = 'aAbcAba';
var newStr = str.replace(/a/gi,'哈哈')//"哈哈哈哈bc哈哈b哈哈"
案例:過濾敏感詞匯
<textarea name="" id="message"></textarea> <button>提交</button>
<div></div>
<script>
var text = document.querySelector('textarea');
var btn = document.querySelector('button');
var div = document.querySelector('div');
btn.onclick = function() {
div.innerHTML = text.value.replace(/激情|gay/g, '**');
}
</script>
==============part5==========
ES6
什么是ES6
ES 的全稱是 ECMAScript , 它是由 ECMA 國際標準化組織,制定的一項腳本語言的標準化規(guī)范角虫。
為什么使用 ES6 ?
每一次標準的誕生都意味著語言的完善,功能的加強委造。JavaScript語言本身也有一些令人不滿意的地方戳鹅。
- 變量提升特性增加了程序運行時的不可預測性
- 語法過于松散,實現(xiàn)相同的功能昏兆,不同的人可能會寫出不同的代碼
ES6新增語法
let(★★★)
ES6中新增了用于聲明變量的關(guān)鍵字
let聲明的變量只在所處于的塊級有效
if (true) {
let a = 10;
}
console.log(a) // a is not defined
注意:使用let關(guān)鍵字聲明的變量才具有塊級作用域枫虏,使用var聲明的變量不具備塊級作用域特性。
不存在變量提升
console.log(a); // a is not defined
let a = 20;
暫時性死區(qū)
利用let聲明的變量會綁定在這個塊級作用域亮垫,不會受外界的影響
var tmp = 123;
if (true) {
tmp = 'abc';
let tmp;
}
經(jīng)典面試題
var arr = [];
for (var i = 0; i < 2; i++) {
arr[i] = function () {
console.log(i);
}
}
arr[0]();
arr[1]();
經(jīng)典面試題圖解:此題的關(guān)鍵點在于變量i是全局的模软,函數(shù)執(zhí)行時輸出的都是全局作用域下的i值。
let arr = [];
for (let i = 0; i < 2; i++) {
arr[i] = function () {
console.log(i);
}
}
arr[0]();
arr[1]();
經(jīng)典面試題圖解:此題的關(guān)鍵點在于每次循環(huán)都會產(chǎn)生一個塊級作用域饮潦,每個塊級作用域中的變量都是不同的燃异,函數(shù)執(zhí)行時輸出的是自己上一級(循環(huán)產(chǎn)生的塊級作用域)作用域下的i值.
小結(jié)
- let關(guān)鍵字就是用來聲明變量的
- 使用let關(guān)鍵字聲明的變量具有塊級作用域
- 在一個大括號中 使用let關(guān)鍵字聲明的變量才具有塊級作用域 var關(guān)鍵字是不具備這個特點的
- 防止循環(huán)變量變成全局變量
- 使用let關(guān)鍵字聲明的變量沒有變量提升
- 使用let關(guān)鍵字聲明的變量具有暫時性死區(qū)特性
const(★★★)
聲明常量,常量就是值(內(nèi)存地址)不能變化的量
具有塊級作用域
if (true) {
const a = 10;
}
console.log(a) // a is not defined
聲明常量時必須賦值
const PI; // Missing initializer in const declaration
常量賦值后继蜡,值不能修改
const PI = 3.14;
PI = 100; // Assignment to constant variable.
const ary = [100, 200];
ary[0] = 'a';
ary[1] = 'b';
console.log(ary); // ['a', 'b'];
ary = ['a', 'b']; // Assignment to constant variable.
小結(jié)
- const聲明的變量是一個常量
- 既然是常量不能重新進行賦值回俐,如果是基本數(shù)據(jù)類型,不能更改值稀并,如果是復雜數(shù)據(jù)類型忘瓦,不能更改地址值
- 聲明 const時候必須要給定值
let蝙场、const、var 的區(qū)別
- 使用 var 聲明的變量,其作用域為該語句所在的函數(shù)內(nèi),且存在變量提升現(xiàn)象
- 使用 let 聲明的變量,其作用域為該語句所在的代碼塊內(nèi),不存在變量提升
-
使用 const 聲明的是常量,在后面出現(xiàn)的代碼中不能再修改該常量的值
解構(gòu)賦值(★★★)
ES6中允許從數(shù)組中提取值,按照對應(yīng)位置,對變量賦值,對象也可以實現(xiàn)解構(gòu)
數(shù)組解構(gòu)
let [a, b, c] = [1, 2, 3];
console.log(a)//1
console.log(b)//2
console.log(c)//3
//如果解構(gòu)不成功,變量的值為undefined
對象解構(gòu)
let person = { name: 'zhangsan', age: 20 };
let { name, age } = person;
console.log(name); // 'zhangsan'
console.log(age); // 20
let {name: myName, age: myAge} = person; // myName myAge 屬于別名
console.log(myName); // 'zhangsan'
console.log(myAge); // 20
小結(jié)
- 解構(gòu)賦值就是把數(shù)據(jù)結(jié)構(gòu)分解汞斧,然后給變量進行賦值
- 如果解構(gòu)不成功秋冰,變量跟數(shù)值個數(shù)不匹配的時候,變量的值為undefined
- 數(shù)組解構(gòu)用中括號包裹,多個變量用逗號隔開募寨,對象解構(gòu)用花括號包裹贵涵,多個變量用逗號隔開
- 利用解構(gòu)賦值能夠讓我們方便的去取對象中的屬性跟方法
箭頭函數(shù)(★★★)
ES6中新增的定義函數(shù)的方式。
() => {} //():代表是函數(shù); =>:必須要的符號,指向哪一個代碼塊;{}:函數(shù)體
const fn = () => {}//代表把一個函數(shù)賦值給fn
函數(shù)體中只有一句代碼,且代碼的執(zhí)行結(jié)果就是返回值阻塑,可以省略大括號
function sum(num1, num2) {
return num1 + num2;
}
//es6寫法
const sum = (num1, num2) => num1 + num2;
如果形參只有一個,可以省略小括號
function fn (v) {
return v;
}
//es6寫法
const fn = v => v;
箭頭函數(shù)不綁定this關(guān)鍵字私植,箭頭函數(shù)中的this,指向的是函數(shù)定義位置的上下文this
const obj = { name: '張三'}
function fn () {
console.log(this);//this 指向 是obj對象
return () => {
console.log(this);//this 指向 的是箭頭函數(shù)定義的位置,那么這個箭頭函數(shù)定義在fn里面,而這個fn指向是的obj對象拥刻,所以這個this也指向是obj對象
}
}
const resFn = fn.call(obj);
resFn();
小結(jié)
- 箭頭函數(shù)中不綁定this蒸眠,箭頭函數(shù)中的this指向是它所定義的位置蒋腮,可以簡單理解成作彤,定義箭頭函數(shù)中的作用域的this指向誰代咸,它就指向誰
- 箭頭函數(shù)的優(yōu)點在于解決了this執(zhí)行環(huán)境所造成的一些問題荸百。比如:解決了匿名函數(shù)this指向的問題(匿名函數(shù)的執(zhí)行環(huán)境具有全局性),包括setTimeout和setInterval中使用this所造成的問題
面試題
var age = 100;
var obj = {
age: 20,
say: () => {
alert(this.age)
}
}
obj.say();//箭頭函數(shù)this指向的是被聲明的作用域里面停蕉,而對象沒有作用域的谋旦,所以箭頭函數(shù)雖然在對象中被定義,但是this指向的是全局作用域
剩余參數(shù)(★★)
剩余參數(shù)語法允許我們將一個不定數(shù)量的參數(shù)表示為一個數(shù)組兄纺,不定參數(shù)定義方式付材,這種方式很方便的去聲明不知道參數(shù)情況下的一個函數(shù)
function sum (first, ...args) {
console.log(first); // 10
console.log(args); // [20, 30]
}
sum(10, 20, 30)
剩余參數(shù)和解構(gòu)配合使用
let students = ['wangwu', 'zhangsan', 'lisi'];
let [s1, ...s2] = students;
console.log(s1); // 'wangwu'
console.log(s2); // ['zhangsan', 'lisi']
ES6 的內(nèi)置對象擴展
Array 的擴展方法(★★)
擴展運算符(展開語法)
擴展運算符可以將數(shù)組或者對象轉(zhuǎn)為用逗號分隔的參數(shù)序列
let ary = [1, 2, 3];
...ary // 1, 2, 3
console.log(...ary); // 1 2 3,相當于下面的代碼
console.log(1,2,3);
擴展運算符可以應(yīng)用于合并數(shù)組
// 方法一
let ary1 = [1, 2, 3];
let ary2 = [3, 4, 5];
let ary3 = [...ary1, ...ary2];
// 方法二
ary1.push(...ary2);
將類數(shù)組或可遍歷對象轉(zhuǎn)換為真正的數(shù)組
let oDivs = document.getElementsByTagName('div');
oDivs = [...oDivs];
構(gòu)造函數(shù)方法:Array.from()
將偽數(shù)組或可遍歷對象轉(zhuǎn)換為真正的數(shù)組
//定義一個集合
let arrayLike = {
'0': 'a',
'1': 'b',
'2': 'c',
length: 3
};
//轉(zhuǎn)成數(shù)組
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']
方法還可以接受第二個參數(shù),作用類似于數(shù)組的map方法授嘀,用來對每個元素進行處理,將處理后的值放入返回的數(shù)組
let arrayLike = {
"0": 1,
"1": 2,
"length": 2
}
let newAry = Array.from(arrayLike, item => item *2)//[2,4]
注意:如果是對象,那么屬性需要寫對應(yīng)的索引
實例方法:find()
用于找出第一個符合條件的數(shù)組成員馒铃,如果沒有找到返回undefined
let ary = [{
id: 1,
name: '張三'
}, {
id: 2,
name: '李四'
}];
let target = ary.find((item, index) => item.id == 2);//找數(shù)組里面符合條件的值谴咸,當數(shù)組中元素id等于2的查找出來,注意骗露,只會匹配第一個
實例方法:findIndex()
用于找出第一個符合條件的數(shù)組成員的位置,如果沒有找到返回-1
let ary = [1, 5, 10, 15];
let index = ary.findIndex((value, index) => value > 9);
console.log(index); // 2
實例方法:includes()
判斷某個數(shù)組是否包含給定的值血巍,返回布爾值萧锉。
[1, 2, 3].includes(2) // true
[1, 2, 3].includes(4) // false
String 的擴展方法
模板字符串(★★★)
ES6新增的創(chuàng)建字符串的方式,使用反引號定義
let name = `zhangsan`;
模板字符串中可以解析變量
let name = '張三';
let sayHello = `hello,my name is ${name}`; // hello, my name is zhangsan
模板字符串中可以換行
let result = {
name: 'zhangsan',
age: 20,
sex: '男'
}
let html = ` <div>
<span>${result.name}</span>
<span>${result.age}</span>
<span>${result.sex}</span>
</div> `;
在模板字符串中可以調(diào)用函數(shù)
const sayHello = function () {
return '哈哈哈哈 追不到我吧 我就是這么強大';
};
let greet = `${sayHello()} 哈哈哈哈`;
console.log(greet); // 哈哈哈哈 追不到我吧 我就是這么強大 哈哈哈哈
實例方法:startsWith() 和 endsWith()
- startsWith():表示參數(shù)字符串是否在原字符串的頭部述寡,返回布爾值
- endsWith():表示參數(shù)字符串是否在原字符串的尾部柿隙,返回布爾值
let str = 'Hello world!';
str.startsWith('Hello') // true
str.endsWith('!') // true
實例方法:repeat()
repeat方法表示將原字符串重復n次,返回一個新字符串
'x'.repeat(3) // "xxx"
'hello'.repeat(2) // "hellohello"
Set 數(shù)據(jù)結(jié)構(gòu)(★★)
ES6 提供了新的數(shù)據(jù)結(jié)構(gòu) Set鲫凶。它類似于數(shù)組禀崖,但是成員的值都是唯一的,沒有重復的值螟炫。
Set本身是一個構(gòu)造函數(shù)波附,用來生成 Set 數(shù)據(jù)結(jié)構(gòu)
const s = new Set();
Set函數(shù)可以接受一個數(shù)組作為參數(shù),用來初始化昼钻。
const set = new Set([1, 2, 3, 4, 4]);//{1, 2, 3, 4}
實例方法
- add(value):添加某個值掸屡,返回 Set 結(jié)構(gòu)本身
- delete(value):刪除某個值,返回一個布爾值然评,表示刪除是否成功
- has(value):返回一個布爾值仅财,表示該值是否為 Set 的成員
- clear():清除所有成員,沒有返回值
const s = new Set();
s.add(1).add(2).add(3); // 向 set 結(jié)構(gòu)中添加值
s.delete(2) // 刪除 set 結(jié)構(gòu)中的2值
s.has(1) // 表示 set 結(jié)構(gòu)中是否有1這個值 返回布爾值
s.clear() // 清除 set 結(jié)構(gòu)中的所有值
//注意:刪除的是元素的值碗淌,不是代表的索引
遍歷
Set 結(jié)構(gòu)的實例與數(shù)組一樣盏求,也擁有forEach方法,用于對每個成員執(zhí)行某種操作亿眠,沒有返回值碎罚。
s.forEach(value => console.log(value))