webpack 是一个现代 JavaScript 应用程序的静态模块打包器 (module bundler) 。
当 webpack 处理应用程序时,它会递归地构建一个依赖关系图 (dependency graph) ,其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle 。
入口会指示 webpack 应该使用哪个模块,来作为构建其内部依赖图的开始。
进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的。
在 webpack 中入口有多种方式来定义,
单个入口:
const config = {
entry: "./src/main.js"
};
对象语法:
const config = {
app: "./src/main.js",
vendors: "./src/vendors.js"
};
output 属性会告诉 webpack 在哪里输出它创建的 bundles,如何命名这些文件。
默认值为 ./dist
。
const config = {
entry: "./src/main.js",
output: {
filename: "bundle.js",
path: path.resolve(__dirname, 'dist')
}
};
loader 让 webpack 可以去处理那些非 JavaScript 文件( webpack 自身只理解 JavaScript )。
loader 可以将所有类型的文件转换为 webpack 能够有效处理的模块。
例如,开发的时候使用 ES6 ,通过 loader 将 ES6 的语法转为 ES5 ,如下配置:
const config = {
entry: "./src/main.js",
output: {
filename: "bundle.js",
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader",
options: [
presets: ["env"]
]
}
]
}
};
loader 被用于转换某些类型的模块,而插件则可以做更多的事情。
包括打包优化、压缩、定义环境变量
等等。
插件的功能强大,是 webpack 扩展非常重要的利器,可以用来处理各种各样的任务。
使用一个插件也非常容易,只需要 require() ,然后添加到 plugins 数组中。
// npm install
const HtmlWebPackPlugin = require('html-webpack-plugin');
// access to the embed plugin
const webpack = require('webpack');
const config = {
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader"
}
]
},
plugins: [
new HtmlWebPackPlugin({template: './src/index.html'})
]
};
简单的搭建:webpack.config.js
const path = require('path');
module.exports = {
mode: "development", // "production" | "development"
// 选择 development 为开发模式, production 为生产模式
entry: "./src/main.js",
output: {
filename: "bundle.js",
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader",
options: [
presets: ["env"]
]
}
]
},
plugins: [
...
]
}
webpack 会从入口 main.js 文件进行构建,通过 loader 进行js转换,输出一个为 bundle.js 的文件,至此一整个过程就构建完成。
gulp 是一个基于流的自动化构建工具。
具有易于使用、构建快速、插件高质和易于学习。
常用于轻量级的工程中。
全局安装: npm install --global gulp
项目里引入依赖: npm install --save-dev gulp
在项目根目录下创建名为 gulpfile.js 的文件:
const gulp = require('gulp');
// default 表示一个任务名,为默认执行任务
gulp.task('default', function() {
//default code
})
运行: gulp
gulp搭建应用:
const gulp = require('gulp');
const uglify = require("gulp-uglify");
gulp.task('default', function() {
gulp.src('./src/main.js')
.pipe(uglify())
.pipe(gulp.dest('./dist'));
})
let:
{
let a = 0;
console.log(a) //0
}
a // ReferenceError: a is not defined
与var对比:
let | var |
---|---|
代码块内有效 | 全局范围内有效 |
只能声明一次 | 多次声明 |
不能变量提升 | 变量提升 |
let [a, b, c] = [1, 2, 3];
// a = 1, b = 2, c = 3;
let [a, [[b], c]] = [1, [[2], 3]];
let [a, , b] = [1, 2, 3];
let [a = 1, b] = [];
let [a, ...b] = [1,2,3];
//a = 1, b = [2, 3]
let [a,b,c,d,e] = "hello";
let [a = 2] = [undefined];
//a = 2;
let [a = 3, b = a] = [];
let [a = 3, b = a] = [1]; // a=1, b=1
let [a = 3, b = a] = [1, 2];
let {foo, bar} = {foo: 'aaa', bar: 'bbb'};
同上S
新的数据类型 - 表示独一无二的值,用来定义对象的唯一属性名。
let sy = Symbol("KK");
console.log(sy); //Symbol(KK);
let sy2 = Symbol("KK");
sy === sy2; // false
let sy = Symbol("key1");
// 写法1
let syObject = {};
syObject[sy] = "kk";
console.log(syObject); // {Symbol(key1): "kk"}
// 写法2
let syObject = {
[sy]: "kk"
};
console.log(syObject); // {Symbol(key1): "kk"}
// 写法3
let syObject = {};
Object.defineProperty(syObject, sy, {value: "kk"});
console.log(syObject); // {Symbol(key1): "kk"}
object | Map |
---|---|
键只能是string或Symbols | 键是任意值 |
键不是有序的 | 键值是有序的(FIFO) |
键值个数对只能计算 | 键值个数从size属性获取 |
存在和原型链上的设置冲突 | 无 |
var myMap = new Map();
myMap.set(key, "value");
myMap.get(key);
遍历。
for(var[key, value] of myMap);
var arr = [[1,2], [3,4]];
//convert to map
var myMap = new Map(arr);
// convert to arr
var outArray = Array.from(myMap);
* Map克隆
myMap2 = new Map(myMap1);
* Map合并
var merge = new Map([...myMap1, ...myMap2]);
* 允许存储所有类型的唯一值
* 判断相等的特殊值:
* +0, -0
* undefined
* NaN
* 实现
mySet.add({a:1});
var mySet = new
var arr = [...mySet];
var mySet = new Set('Hello');
var mySet = new Set([1, 2, 3, 4, 4]);
[...mySet]; // [1, 2, 3, 4]
new Set([...a, ...b])
new Set([...a].filter(x => b.has(x)))
new Set([...a].filter(x => !b.has(x)))
Proxy 可以对目标对象的读取、函数调用等操作进行拦截,然后进行操作处理。
Reflect 可以用于获取目标对象的行为。
let target = {
name: 'Tom',
age: 24
}
let handler = {
get: function(target, key) {
console.log('getting '+key);
return target[key]; // 不是target.key
},
set: function(target, key, value) {
console.log('setting '+key);
target[key] = value;
}
}
let proxy = new Proxy(target, handler)
proxy.name // 实际执行 handler.get
proxy.age = 25 // 实际执行 handler.set
function sub(a, b){
return a - b;
}
let handler = {
apply: function(target, ctx, args){
console.log('handle apply');
return Reflect.apply(...arguments);
}
}
let proxy = new Proxy(sub, handler)
proxy(2, 1)
// handle apply
// 1
const age = 12;
const person = {age: age};
拓展运算符(…)用于取出参数对象所有可遍历属性然后拷贝到当前对象。
let person = {name: "Amy", age: 15};
ler person2 = ....;
let someone = {...person, ...person2};
someone;
用于将源对象的所有可枚举属性复制到目标对象中。
let target = {a: 1};
let object2 = {b: 2};
let object3 = {c: 3};
Object.assign(target,object2,object3);
// 第一个参数是目标对象,后面的参数是源对象
target; // {a: 1, b: 2, c: 3
console.log(Array.of(1, '2', true)); // [1, '2', true]
console.log(Array.from([1, , 3])); // [1, undefined, 3]
一个类数组对象必须含有 length 属性,且元素属性名必须是数值或者可转换为数值的字符。
let arr = Array.from({
0: '1',
1: '2',
2: 3,
length: 3
});
console.log(); // ['1', '2', 3]
// 没有 length 属性,则返回空数组
let array = Array.from({
0: '1',
1: '2',
2: 3,
});
console.log(array); // []
// 元素属性名不为数值且无法转换为数值,返回长度为 length 元素值为 undefined 的数组
let array1 = Array.from({
a: 1,
b: 2,
length: 2
});
console.log(array1); // [undefined, undefined]
let arr = Array.of(1, 2, 3, 4);
console.log(arr.find(item => item > 2)); // 3
// 数组空位处理为 undefined
console.log([, 1].find(n => true)); // undefined
findIndex()
查找数组中符合条件的元素索引,若有多个符合条件的元素,则返回第一个元素索引。
fill()
将一定范围索引的数组元素内容填充为单个指定的值。
copyWithin()
将一定范围索引的数组元素修改为此数组另一指定范围索引的元素。
entries()
遍历键值对。
keys()
遍历键名。
values()
遍历键值。
includes()
数组是否包含指定值。
flat()
嵌套数组转一维数组.
复制数组
let arr = [1, 2],
arr1 = [...arr];
console.log(arr1); // [1, 2]
// 数组含空位
let arr2 = [1, , 3],
arr3 = [...arr2];
console.log(arr3); [1, undefined, 3]
console.log([...[1, 2],...[3, 4]]); // [1, 2, 3, 4]
const items = ["zero", "one", "two"];
const it = items[Symbol.iterator]();
it.next();
>{value: "zero", done: false}
it.next();
>{value: "one", done: false}
it.next();
>{value: "two", done: false}
it.next();
>{value: undefined, done: true}
在ES6中,class (类)作为对象的模板被引入,可以通过 class 关键字定义类。
class 的本质是 function。
它可以看作一个语法糖,让对象原型的写法更加清晰、更像面向对象编程的语法。
// 匿名类
let Example = class {
constructor(a) {
this.a = a;
}
}
// 命名类
let Example = class Example {
constructor(a) {
this.a = a;
}
}
// 类声明
class Example {
constructor(a) {
this.a = a;
}
}
function testable(target) {
target.isTestable = true;
}
@testable
class Example {}
Example.isTestable; // true
class Example {
@writable
sum(a, b) {
return a + b;
}
}
function writable(target, name, descriptor) {
descriptor.writable = false;
return descriptor; // 必须返回
}
RequireJS
seaJS
ES6 -> export, import
use strict
/*-----export [test.js]-----*/
let myName = "Tom";
let myAge = 20;
let myfn = function(){
return "My name is" + myName + "! I'm '" + myAge + "years old."
}
let myClass = class myClass {
static a = "yeah!";
}
export { myName, myAge, myfn, myClass }
/*-----import [xxx.js]-----*/
import { myName, myAge, myfn, myClass } from "./test.js";
console.log(myfn());// My name is Tom! I'm 20 years old.
console.log(myAge);// 20
console.log(myName);// Tom
console.log(myClass.a );// yeah!
as 重新定义导出的接口名称,隐藏模块内部的变量
/*-----export [test.js]-----*/
let myName = "Tom";
export { myName as exportName }
/*-----import [xxx.js]-----*/
import { exportName } from "./test.js";
console.log(exportName);// Tom
使用 as 重新定义导出的接口名称,隐藏模块内部的变量
/*-----export [test1.js]-----*/
let myName = "Tom";
export { myName }
/*-----export [test2.js]-----*/
let myName = "Jerry";
export { myName }
/*-----import [xxx.js]-----*/
import { myName as name1 } from "./test1.js";
import { myName as name2 } from "./test2.js";
console.log(name1);// Tom
console.log(name2);// Jerry
Promise 异步操作有三种状态:
除了异步操作的结果,任何其他操作都无法改变这个状态。
Promise 对象只有:
const p1 = new Promise(function(resolve,reject){
resolve('success1');
resolve('success2');
});
const p2 = new Promise(function(resolve,reject){
resolve('success3');
reject('reject');
});
p1.then(function(value){
console.log(value); // success1
});
p2.then(function(value){
console.log(value); // success3
});
const p = new Promise(function(resolve,reject){
resolve('success');
});
p.then(function(value){
console.log(value);
});
console.log('first');
// first
// success
.then
形式添加的回调函数,不论什么时候,都会被调用。then
常用于多次调用添加多个回调函数,按照插入顺序独立运行
const p = new Promise(function(resolve,reject){
resolve(1);
}).then(function(value){ // 第一个then // 1
console.log(value);
return value * 2;
}).then(function(value){ // 第二个then // 2
console.log(value);
}).then(function(value){ // 第三个then // undefined
console.log(value);
return Promise.resolve('resolve');
}).then(function(value){ // 第四个then // resolve
console.log(value);
return Promise.reject('reject');
}).then(function(value){ // 第五个then //reject:reject
console.log('resolve:' + value);
}, function(err) {
console.log('reject:' + err);
});
const p1 = new Promise(function(resolve,reject){
resolve(1);
}).then(function(result) {
p2(result).then(newResult => p3(newResult));
}).then(() => p4());
var myFirstPromise = new Promise(function(resolve, reject){
//当异步代码执行成功时,我们才会调用resolve(...), 当异步代码失败时就会调用reject(...)
//在本例中,我们使用setTimeout(...)来模拟异步代码,实际编码时可能是XHR请求或是HTML5的一些API方法.
setTimeout(function(){
resolve("成功!"); //代码正常执行!
}, 250);
});
myFirstPromise.then(function(successMessage){
//successMessage的值是上面调用resolve(...)方法传入的值.
//successMessage参数不一定非要是字符串类型,这里只是举个例子
document.write("Yay! " + successMessage);
});
promise.then(onFulfilled, onRejected)
// above means that following
promise.then(onFulfilled).catch(onRejected)
function ajax(URL) {
return new Promise(function (resolve, reject) {
var req = new XMLHttpRequest();
req.open('GET', URL, true);
req.onload = function () {
if (req.status === 200) {
resolve(req.responseText);
} else {
reject(new Error(req.statusText));
}
};
req.onerror = function () {
reject(new Error(req.statusText));
};
req.send();
});
}
var URL = "/try/ajax/testpromise.php";
ajax(URL).then(function onFulfilled(value){
document.write('内容是:' + value);
}).catch(function onRejected(error){
document.write('错误:' + error);
});
getJSON("/post/1.json").then(function(post) {
return getJSON(post.commentURL);
}).then(function(comments) {
// some code
}).catch(function(error) {
// 处理前两个回调函数的错误
});
Promise.all 方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
Promise.all([p1,p2,p3]);
var p = Promise.race([p1,p2,p3]);
两种方法可以返回promise
var p = Promise.reject('出错了');
p.then(null, function (s){
console.log(s)
});
// 出错了
var p = Promise.resolve('Hello');
p.then(function (s){
console.log(s)
});
// Hello
Generator 函数,可以通过 yield 关键字,把函数的执行流挂起,为改变执行流程提供了可能,从而为异步编程提供解决方案。
f.next();
// one
// {value: "1", done: false}
f.next();
// two
// {value: "2", done: false}
f.next();
// three
// {value: "3", done: true}
f.next();
// {value: undefined, done: true}
function* foo(){
yield 1;
yield 2;
yield 3;
}
var f = foo();
f.next();
// {value: 1, done: false}
f.return("foo");
// {value: "foo", done: true}
f.next();
// {value: undefined, done: true}
throw 方法
throw 方法可以再 Generator 函数体外面抛出异常,再函数体内部捕获。
var g = function* () {
try {
yield;
} catch (e) {
console.log('catch inner', e);
}
};
var i = g();
i.next();
try {
i.throw('a');
i.throw('b');
} catch (e) {
console.log('catch outside', e);
}
// catch inner a
// catch outside b
异步操作.
async function name([param[, param[, ... param]]]) { statements }
function testAwait(){
return new Promise((resolve) => {
setTimeout(function(){
console.log("testAwait");
resolve();
}, 1000);
});
}
async function helloAsync(){
await testAwait();
console.log("helloAsync");
}
helloAsync();
// testAwait
// helloAsync
Copyright: 菜鸟教程。