let 定义的变量有自己的作用域, 只能在block中进行访问 这样当我们在作用域外引用变量的时候, 是会出现错误提示的
一个常见的问题是:
function callback(username) {
console.log(username);
}
let userNames = ["ken", "clark", "ian"]
// 由于var i在代码执行前已经定义, 所以在函数执行的时候, 对应的i已经为3了
function loadProfiles(userNames) {
for(var i in userNames) {
setTimeout(function(){
console.log(userNames[i])
}, 1000);
}
}
loadProfiles(["ian", "ken", "clark"])
// 但是如果这样则不行
function loadProfiles(userNames) {
for(var i in userNames) {
setTimeout(callback(userNames[i]), 1000);
}
}
loadProfiles(["ian", "ken", "clark"])
// 如果改成let则可以避免这个问题
function loadProfiles(userNames) {
for(let i in userNames) {
setTimeout(function(){
console.log(userNames[i])
}, 1000);
}
}
// 不同作用域中的let
let flashMessage = "Hello";
let flashMessage = "Hello"; // can't be redeclared
function loadProfiles(userNames){
let flashMessage = "Loading profiles";
return flashMessage;
}
const 常量定义
const MAX_USERS = 3; // 必须有初始值
MAX_USERS = 10; // 无法重新定义
// const也是基于block的作用域
if(userNames.length > MAX_USERS){
const MAX_REPLIES = 15;
}
console.log(MAX_REPLIES); // ReferenceError, MAX_REPLIES is not defined.
可变参数
// Q: 使用可变参数前, 判断参数是否有效的方式
function loadProfiles(userNames){
let names = typeof userNames !== 'undefined' ? userNames : [];
let namesLength = names.length;
}
// R: 通过设置默认值可以解决这个问题
function loadProfiles(userNames = []){...
// Q: 当想传递多个参数的时候, 我们无法通过参数定义了解有哪些参数
function setPageThread(name, options = {}){
let popular = options.popular;
let expires = options.expires;
let activeClass = options.activeClass;
//...
}
// R: 命名参数可以解决这个问题
function setPageThread(name, { popular, expires, activeClass = "aa" }){
console.log("Name: ", name);
console.log("Popular: ", popular);
console.log("Expires: ", expires);
console.log("Active: " , activeClass); //未传递的值设置为undefined
}
setPageThread("New Version out Soon!", {
popular: true,
expires: 10000,
});
// 更进一步, 通过设置默认值,可以在调用的时候,省略该参数
function setPageThread(name, { popular, expires, activeClass } = {}){
console.log(name);
console.log(popular);
};
setPageThread("New Version out Soon!")
function displayTags(targetElement, ...tags){
console.log(targetElement);
console.log(tags);
for(let i in tags){ // 这里tags是一个数组
console.log(tags[i]);
}
}
setTimeout(function(data){
let tags = ["no1", "no2", "no3"];
displayTags(...tags); //这里tags是多个参数
}, 1000)
函数与对象间的转换
function TagComponent(target, urlPath){
this.targetElement = target;
this.urlPath = urlPath;
}
function displayTags(target, ...tags) {
console.log(target);
console.log(tags);
}
TagComponent.prototype.render = function(){
setTimeout(function(data){
let tags = ["ken", "ian", "clark"];
displayTags(this.targetElement, ...tags); //this.targetElement is undefined
}, 1000);
}
let tagComponent = new TagComponent(document.getElementById("target_div"), "/topics/17/tags");
tagComponent.render();
// 通过火箭运算符来继承父作用域
getRequest(this.urlPath, (data) => {
let tags = data.tags;
displayTags(this.targetElement, ...tags);
});
对象初始化的省略
// 当我们返回的对象名和变量名是一致的, 则可以省略变量名的定义
function buildUser(first, last){
let fullName = first + " " + last;
return { first, last, fullName, isActive(){return true} };
}
// 同样可以用在定义变量中
let name = "Sam";
let age = 45;
let friends = ["Brook","Tyler"];
let user = { name, age, friends }; //equal to let user = { name: name, age: age, friends: friends }
// 对象解构
let { first, last, fullName } = buildUser("Sam", "Williams")
let { last, fullName } = buildUser("Sam", "Williams")
let user = buildUser("Sam", "Williams")
console.log(first)
console.log(user)
字符串模板
let veryLongText = `Hi ${userName},
this is a very
very
veeeery
long text.
Regards,
${admin.fullName}
`;
Object.assign
function countdownTimer(target, timeLeft, options = {}){
let defaults = {
a: "a",
b: "b",
c: "c"
};
// 该方法是模仿jquery的merge
let settings = Object.assign({}, defaults, options);
console.log(settings);
}
countdownTimer("target", "100", {a: "d"})
数组操作
let users = ["Sam", "Tyler", "Brook"];
let [a, b, c] = users;
let [a, , c] = users;
let [ first, ...rest ] = users;
for(let name of names){
console.log(name);
}
// find 方法
let users = [
{ login: "Sam", admin: false },
{ login: "Brook", admin: true },
{ login: "Tyler", admin: true }
];
let admin = users.find( (user) => {
return user.admin;
});
let admin = users.find(user => user.admin)
console.log(admin)
Map操作
let user1 = { name: "Sam" };
let user2 = { name: "Tyler" };
let totalReplies = {};
// 当我们使用对象作为key的时候, 因为对象都会被转化为“[object, object]”
totalReplies[user1] = 5;
totalReplies[user2] = 42;
console.log( totalReplies[user1] ); // 所以这两个对象的key都是一样的
console.log( totalReplies[user2] );
console.log( Object.keys(totalReplies) );
// 通过map对象, 可以解决这个问题
let totalReplies = new Map();
totalReplies.set( user1, 5 );
totalReplies.set( user2, 42 );
console.log( totalReplies.get(user1) );
console.log( totalReplies.get(user2) );
// 通过map对象, 可以在运行的时候, 动态的创建
let recentPosts = new Map();
createPost(newPost, (data) => {
recentPosts.set( data.author, data.message )
});
let recentPosts = new Map();
createPost(newPost, (data) => {
recentPosts.set( data.author, data.message );
});
socket.on('new post', function(data){
recentPosts.set( data.author, data.message );
});
// map 对象是可以进行遍历的
let mapSettings = new Map();
mapSettings.set( "user", "Sam" );
mapSettings.set( "topic", "ES2015" );
mapSettings.set( "replies", ["Can't wait!", "So Cool"] );
mapSettings.get("replies")
for(let [key, value] of mapSettings){
console.log(`${key} = ${value}`);
}
// weakMap对象只接受对象作为key, 而且不能遍历
let mapSettings = new WeakMap();
mapSettings.set( user, "ES2015" );
// weakMap在内存使用上占用更少, 因为它支持垃圾收集
Set集合
// set的key是唯一的, 并且也是可以遍历的
let tags = new Set();
tags.add({ version: "2015" });
tags.add("Web");
tags.add("Web");
// 同样存在weakSet, 但是不能遍历, 必须以对象作为key
// 同时weakSet不能通过get方法获取其中存取的值
// 那么我们要weakSet干嘛呢?
// 主要用于不改变对象状态的情况下, 对对象进行操作
// 假设我们需要判断post是否已读, 我们需要修改post的属性来判断
let post1 = { title: "hello1", content: "world1" };
let post2 = { title: "hello2", content: "world2" };
let post3 = { title: "hello3", content: "world3" };
let post4 = { title: "hello4", content: "world4" };
post1.isRead = true
let postArray = [post1, post2, post3, post4];
for(let post of postArray){
if(!post.isRead){
console.log(post);
}
}
// 而通过weakSet, 可以直接判断是否包含已读post
let readPosts = new WeakSet();
readPosts.add(post1);
for(let post of postArray){
if(!readPosts.has(post)){
console.log(post)
}
}
Class
class Widget {
constructor() {
this.baseCss = "site-widget";
}
parse() {
}
}
class SponsorWidget {
constructor(name, description, url){
super();
this.name = name;
this.description = description;
this.url = url;
}
render(){
let parsedName = this.parse(this.name);
let css = this._buildCSS(this.baseCSS);
}
}
// 重写父类的方法
parse(){
let parsedName = super.parse(this.name);
return `Sponsor: ${parsedName}`;
}
Module
// flash-messages.js
// 使用default只能导出一个方法
export default function(message){
alert(message);
}
function logMessage(message){
console.log(message);
}
// app.js
import flashMessage from './flash-message';
flashMessage("hello world!")
// 如果需要导出多个方法, 我们需要去掉default
export function alertMessage(message){..
export function logMessage(message){..
// 名字必须和模块中的一样
import { alertMessage, logMessage } from './flash-message';
// 将方法导出到一个对象中
import * as flash from './flash-message';
flash.alertMessage("haha");
flash.logMessage("haha");
// 简化export
export {alertMessage, logMessage}
// 如何在模块间共享常量
// constants.js
const MAX_USERS = 3
const MAX_REPLIES = 5
export {MAX_USERS, MAX_REPLIES}
import { MAX_REPLIES, MAX_USERS } from './constants';
//也可以选择只导出部分
import { MAX_REPLIES } from './constants'
// 导出class
// defalut允许在import的时候, 指定任意的值
export default class FlashMessage {
constructor(message){
this.message = message;
}
renderAlert(){
alert(`${this.message} from alert`);
}
renderLog(){
console.log(`${this.message} from log`);
}
}
import FlashMessage from './flash-message';
import Message from './flash-message'; //名字可以不一样
let flash = new FlashMessage("Hello");
flash.renderAlert();
flash.renderLog();
// 也可以使用和function一样的导出方式, 但是这种情况下, 名字必须一样
export {FlashMessage}
import FlashMessage from './flash-message';
Promise
getPollResultsFromServer(pollName, function(error, results){ if(error){ //.. handle error }
if(error){ //.. handle error }
//...
ui.renderSidebar(results, function(error){
if(error){ //.. handle error }
//...
sendNotificationToServer(pollName, results, function(error, response){
if(error){ //.. handle error }
//...
doSomethingElseNonBlocking(response, function(error){
if(error){ //.. handle error }
//...
});...
getPollResultsFromServer("Sass vs. LESS")
.then(ui.renderSidebar)
.then(sendNotificationToServer)
.then(doSomethingElseNonBlocking)
.catch(function(error){
console.log("Error: ", error);
});
// 如何实现
function getPollResultsFromServer(pollName){
return new Promise(function(resolve, reject){
let q = function(request) {
if (request.status >= 200 && request.status < 300) {
resolve(request.response);
}else {
reject(new Error(request.status));
}
};
setTimeout(q, 1000, {status: 203, response: "hehehehe, success!"})
});
};
//当我们调用promise对象的then方法的时候, 它会调用初始化时的方法,
//并将then方法的函数参数作为resolve传递进去
let fetchingResults = getPollResultsFromServer("Sass vs. Less");
fetchingResults
.then(function(results){
console.log(`resolve called: ${results}`);
})
.catch(function(error){
console.log(error);
})
如何实现一个迭代器
let post = {
title: "hello iterator",
content: "how to write a iterator"
};
post[Symbol.iterator] = function(){
let properties = Object.keys(this);
let count = 0;
let isDone = false;
let next = () => {
if(count >= properties.length){
isDone = true;
}
return { done: isDone, value: this[properties[count++]] };
}
return { next };
};
for(let p of post) {
console.log(p)
}
通过生成器生成迭代器
// 每次调用yield都会生成一个新的迭代器对象
function *nameList(){
yield "Sam"; // { done: false, value: "Sam"}
yield "Tyler"; // { done: true, value: "Tyler"}
}
// 通过生成器来简化代码
post[Symbol.iterator] = function *(){
let properties = Object.keys(this);
for(let p of properties){
yield this[p];
}
};