var
是容許從新賦值、從新定義的,只有 global
和 function scope
做用域,在 if
內無做用域git
let
、const
擁有 block scope
做用域,有 {}
的時候就代表它的做用域範圍github
let
、const
不可重複聲明一個變量,因此使用 var
容易不當心覆蓋原來的變量web
let
能夠從新賦值,const
不能夠從新賦值ajax
const
也是能夠修改的,不過要是引用類型,至關於人(Person)是不可改變的,但個人年齡會隨着時間改變數組
const person = { name: 'Jelly', age: 20 } person.age = 21;
實在不但願改變,能夠經過 Object.freeze
markdown
阻止修改現有屬性的特性和值,並阻止添加新屬性。app
const person = { name: 'Jelly', age: 20 } const Jelly = Object.freeze(person); person.age = 21; // 沒法修改,返回值依舊是 20
一、經過 let
、const
實現私有變量函數
window
對象下有一個 name
屬性ui
window.name = 'Jelly'; // 這樣就影響到 window 下的 name 屬性
爲了保證咱們不影響到 window
下的 name
屬性,咱們經過 IIFE(當即執行函數)去實現私有化變量,但爲了這個目的而去寫這個函數,不利於代碼的可讀性this
(function() {
var name = 'Jelly'; })(); window.name // ""
有了 let
、const
就能夠經過它們的特性 block scope
來實現了
{
const name = 'Jelly'; } window.name // ""
二、for
循環應用場景
for (var i = 0; i < 10; i++) { // 每次循環都重寫了 i 的值 setTimeout(function() { // 模擬 ajax 請求 console.log(`i:${i}`); // i:10(10個重複) }, 1000); } console.log(window.i); // 10
上面返回 10個 i:10
的緣由就在於,在 setTimeout
1秒後執行的時候,循環已經結束
只需將 var
改成 let
聲明,每次聲明的都屬於當前的塊級做用域,setTimeout
只認這個做用域
for (var i = 0; i < 10; i++) { // 每次循環都重寫了 i 的值 setTimeout(function() { // 模擬 ajax 請求 console.log(`i:${i}`); // 輸出 i:0 到 i:9 }, 1000); } console.log(window.i); // 10
PS:const
不能使用,const
的值是不能從新賦值的
三、臨時性死區(Temporal dead zone)
console.log(color); var color = 'yellow'; console.log(color); // undefined
緣由是變量提高(是 JS 將聲明移到做用域的頂部)
至關因而:
var color;
console.log(color); color = 'yellow';
使用 let
、const
呢?
console.log(color); // ReferenceError let color = 'yellow';
一樣有變量提高,它們會存在於臨時性死區,因此這個特徵讓咱們養成在變量未聲明的時候,不要使用
三個特色:
簡明的語法
// 傳統寫法 const numbers = [5, 6, 13, 5]; const double = numbers.map(function(number) { return number * 2; }) console.log(double);
// 簡化 const numbers = [5, 6, 13, 5]; const double = numbers.map(number => { // 一個參數不須要括號,多個參數須要,沒有參數須要保留括號 return number * 2; }) console.log(double);
隱式返回
有 return 則表示是顯示返回
const numbers = [5, 6, 13, 5]; const double = numbers.map(number => number * 2); // 放在一行,在咱們須要簡單返回一個東西的時候特別有用 console.log(double);
不綁定 this
const Jelly = { name: 'Jelly', hobbies: ['Coding', 'Reading', 'Sleeping'], printHobbies: function() { console.log(this); // printHobbies 的 this 指向調用它的對象 Jelly this.hobbies.map(function(hobby) { // map 函數是獨立的函數,獨立運行時候,沒經過 call、apply、bind 來改變 this 的狀況下,這個 this 則指向 window、global 或者在嚴格模式下指向 undefined console.log(`${this.name} loves ${hobby}`); }); } } Jelly.printHobbies(); // 打印不對,this.name 是空
老方法是經過:var self = this
新方法則經過箭頭函數,箭頭沒有本身的 this
,this
是繼承父級的
this.hobbies.map(hobby => { console.log(`${this.name} loves ${hobby}`); });
箭頭函數都是匿名函數(如何使用命名函數的箭頭函數)
首先了解什麼是命名函數:
function greet(name) { // 在遞歸和解除綁定的時候特別有用 console.log(`Hello ${name}`); } greet('lth'); // Hello lth
箭頭函數要寫成命名函數可:
let greet = name => {console.log(`Hello ${name}`)}; greet('lth'); // Hello lth
一、做爲構造函數,一個方法須要綁定到對象
const Person = (name, points) => { this.name = name; // 這裏用了箭頭函數,this 並無綁定到 Jelly 這個對象上去 this.points = points; } const Jelly = new Person('jelly', 5); // new Person 會完成四個步驟: // 生成一個新的對象、把構造函數中的 this 值指向這個新生成的對象、把這個綁定到它的原型對象、返回這個新生成的對象 Person.prototype.updatePoints = () => { this.points++; // 這裏的 this 指向 window,因此要使用正常的 function,這樣才能綁定到對象上去 console.log(this.points) } // 以上代碼會報錯 const Person = function(name, points) { this.name = name; this.points = points; } const Jelly = new Person('jelly', 5); Person.prototype.updatePoints = function() { this.points++; console.log(this.points) } Jelly // object name: 'jelly', points: 5 Jelly.updatePoints(); // 6
二、當你真的須要 this 時
const button = document.querySelector('.zoom'); button.addEventListener('click', () => { this.classList.add('in'); // 找不到綁定的對象 button,此時的 this 指向 window setTimeout(() => { this.classList.remove('in'); }, 2000) }) const button = document.querySelector('.zoom'); button.addEventListener('click', function() { this.classList.add('in'); setTimeout(() => { this.classList.remove('in'); }, 2000) })
三、須要使用 arguments 對象
// 這個功能返回全部參數的和 const sum = () => { return Array.from(arguments) // 在箭頭函數中是沒有 arguments 這個對象的 .reduce((prevSum, value) => prevSum + value, 0) } const sum = function() { return Array.from(arguments) // 這個 from 是將類數組對象轉化爲真正的數組對象 .reduce((prevSum, value) => prevSum + value, 0) }
// 老方法 function userInfo(a, b) { a = a || 5; // 參數默認聲明,不須要從新聲明 b = b || 6; return a * b; }
// ES6 新方法 function userInfo(a = 5, b = 6) { // 提升可讀性 return a * b; } userInfo(); // 30 userInfo(4); // 24 userInfo(undefined, 5); // 25 不是第一個值的時候,傳 undefined
字符串拼接能夠簡化,不用各類 ++++++ 了
const person = 'Jelly'; const age = 5; const sentence = `${person} is ${age} years old.`;
// 老方法 const template = [ '<div class="greet">', '<p>Hello</p>', '</div>' ].join(''); console.log(template);
// 新方法,模版字符串會保留空格 const template = ` <div class="greet"> <p>Hello</p> </div> `.trim(); console.log(template);
一、經過模版字符串輸出列表
const Jelly = { name: 'jelly', date: '2017-05-07', todos: [ { name: 'Go to Store', completed: false }, { name: 'Watch Movie', completed: true }, { name: 'Running', completed: true } ] } const template = ` <ul> ${Jelly.todos.map(todo => ` <li> ${todo.name} ${todo.completed ? '✅' : '❌'} </li> `).join('')} </ul> ` document.body.innerHTML = template;
二、給傳入的參數加標籤
function highlight(strings, ...values) { // strings 是個數組表示中間的字符串,values 表示 ${} 那些值 // debugger; // 這樣能夠看到 highlight 的組成 const highlighted = values.map(value => `<span class="highlight">${value}</span>`); let str = ''; strings.forEach((string, i) => str += `${string}${highlighted[i] || ''}`); return str; // 上面的 for return 也能夠用 reduce 來處理 // return strings.reduce((prev, curr, i) => `${prev}${cur}${highlighted[i] || ''}`, ''); } const user = 'Mary'; const topic = 'Learn to use markdown'; const sentence = highlight`${user} has commented on your topic ${topic}`;
三、過濾用戶輸入
防止用戶在輸入框的內容中,插入非法字符串或腳原本實現 XSS (跨站腳本攻擊),她們能夠從在實現獲取 Cookie、Session、密碼之類的敏感信息
使用到的庫:DOMPurify.js
下面這個案例就過濾掉了內嵌的 onload
事件
function sanitize(strings, ...values) { const dirty = strings.reduce((prev, curr, i) => `${prev}${curr}${values[i] || ''}`, ''); return DOMPurify.sanitize(dirty); } addCommentForm.addEventListener('submit', function(event) { event.preventDefault(); const newComment = textarea.value.trim(); if (newComment) { CommentDiv.innerHTML = sanitize` <div class="comment-header">${user}</div> <div class="comment-body">${textarea.value}</div> ` textarea.value = ''; } });
.startsWith()
.endsWidth()
.includes()
.repeat()
const id = '51030019800730366x'; const fan = 'I love Laravist'; id.startsWith('51'); // true // 是否是以 51 開頭的 id.startsWith('1980', 6); // true // 第 6 位開始是否是以 1980 開頭的 // 區分大小寫 fan.startsWith('I'); // true fan.startsWith('i'); // false id.endsWith('x'); // true id.endsWith('X'); // false fan.endsWith('love', 6); // true // 在 includes 方法以前咱們用 indexOf fan.indexOf('Laravist') !== -1 // true fan.includes('Laravist') // true fan.includes('Laravist', 10) // false 第十位開始有沒有 Laravist '哈'.repeat(10)
一、實現右對齊
function padder(string, length = 25) { return `${' '.repeat(Math.max(length - string.length), 0)}${string}` }
const Tom = { name: 'Tom Jones', age: 25, family: { mother: '123', father: '456', brother: '789' } } const name = ''; // 會報錯! const { name, age } = Tom; // 先聲明,而後去 Tom 對象找同名的屬性 console.log(name); // 'Tom Jones' console.log(age); // 25 // 若是你想本身聲明的話,用一堆括號包裹 let name = ''; ({ name, age } = Tom); // 會解析成代碼塊,而不是對象解構的語法 console.log(name); console.log(age); // 要訪問 family 也能夠這麼寫 const { fater, mother, brother } = Tom.family;
對象解構重命名
// 若是 father 已經別人提早聲明瞭 const { father: f, mother, brother } = Tom.family; // 講 father 重命名爲 f,但要注意的是,並無聲明 father,而是聲明瞭 f,而後去 Tom.family 裏找 father 屬性 console.log(f); // 123 console.log(father); // not defined console.log(sister); // undefined
對象解構默認值
// 可設置默認值,屬性值要是 undefined 的時候 const { father: f, mother, brother, sister = 'have no sister' } = Tom.family; console.log(sister); // have no sister
經常使用於一些庫,會提供一個 options 的選項讓你來自定義屬性,它們會有默認值
function appendChildDiv(options = {}) { const { parent = 'body', width = '100px', height = '80px', backgroundColor = 'pink' } = options; const div = document.createElement('div'); div.style.width = width; div.style.height = height; div.style.backgroundColor = backgroundColor; document.querySelector(parent).appendChild(div); } appendChildDiv({ parent: '.container', width: '200px', height: '150px', backgroundColor: 'blue' })
const numbers = ['one', 'two', 'three', 'four']; const [one, two] = numbers; console.log(one, two); // one two // 想獲取 一、3 const [one, ,three] = numbers; console.log(one, three); // one three // 獲取全部 const [one, ...others] = numbers; // ...other 只能存在於最後一個,不能是 const [one, ...others, five] = numbers; console.log(one, others); // one ['two', 'three', 'four']
數組解構默認值
const details = ['JellyBool', 'laravist.com']; const [name, website, category = 'PHP'] = details; // 默認值爲 undefined 時才生效 console.log(name, website, category); // JellyBool laravist.com PHP
一、交換變量
// 老辦法 let a = 10; let b = 20; let temp; temp = a; a = b; b = temp; // 新辦法 [a, b] = [b, a]
傳統的有三種方法:for、forEach、for-in
for
在於繁瑣forEach
缺點在於:不能停止 break、continue、returnfor in
在於會遍歷全部可枚舉的屬性for-of
如今暫時不支持對象
// for-in 須要注意的是,遍歷對象上可枚舉的屬性 friuts.describe = 'My favorite fruits'; // 或者給原型加一個方法 friuts.prototype.first = function() { return this[0]; } // 新增的屬性和方法都會被遍歷出來
for-of 的使用
// 只需將 for in 改成 for of 便可 for (let fruit of fruits) { // 要用 let if (fruit === 'Orange') { // 可停止 break; } console.log(fruit) }
一、可用於可迭代對象
對象有內置了遍歷器接口
fruits.entries() // 會返回 Array Iterator{} // 這個就是它的遍歷接口
const fruits = ['Apple', 'Banana', 'Orange', 'Mango']; for (let [index, fruit] of fruits.entries()) { console.log(`${fruit} ranks ${index + 1} in my favorite fruits`); } // Apple ranks 1 in my favorite fruits // Banana ranks 2 in my favorite fruits // Orange ranks 3 in my favorite fruits // Mango ranks 4 in my favorite fruits
二、轉換類數組 arguments
function sum() { let total = 0; for (let num of arguments) { total += num; } return total; } sum(1,2,3,4,5,6,7,8,9,10); // 55
三、綁定事件
const lis = document.querySelector('li'); for (let li of lis) { li.addEventListener('click', function() { this.classList.toggle('completed'); }) }