五、ES6
ECMAScript 6简介
ECMAScript 6.0(以下简称 ES6)是 JavaScript 语言的下一代标准,已经在 2015 年 6 月正式发布了。它的目标,是使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言。
babel
- 是一个编译器、可以将ES6的代码转换为ES5的代码,从而让浏览器获得支持;
- 处理js文件中的高级语法,可以让浏览器兼容ES6
一、let和const命名
- 建议使用let和const
特点 | ||
---|---|---|
var | 可以变量提升;全局作用域;能重复定义,已最后一个声明值为主 | |
let | 不能变量提升、块级作用域(大括号内)、不能重复定义 | |
const | 不能变量提升、块级作用域(大括号内)、不能重复定义 常量、一旦声明,无法修改; 可以修改对象中的属性:person.name='张',不能对对象直接进行修改:person= |
- 以下打印结果为10,因为var会变量提升,循环到最后i的值为10;所以最后数组中所有的方法返回值都为10
- 解决方法:将var改为let
<script>
var arr = [];
for (var i = 0; i < 10; i++) {
arr[i] = function() {
return i;
}
}
console.log(arr[1]());//10
</script>
二、模版字符串
- `` 符号在Tab键上面、键盘上1的左边
- 使用``包裹字符串;
- 使用${a} 包裹变量名;
传统的 JavaScript 语言,输出模板通常是这样写的
//样式
<div class="box"></div>
const oBox = document.querySelector('.box');
// 模板字符串
let id = 1,name = '小马哥';
let htmlTel = "<ul><li><p>id:" + id + "</p><p>name:" + name + "</p></li></ul>";
oBox.innerHTML = htmlTel;
上面的这种写法相当繁琐不方便,ES6引入了模板字符串解决这个问题
let htmlTel = `<ul>
<li>
<p>id:${id}</p>
<p>name:${name}</p>
</li>
</ul>`;
三、函数的扩展—...、默认值、箭头函数
1、带参数默认值
- ES5的写法:ES6之前,不能直接为函数的参数指定默认值,只能采用变通的方法
<script>
//ES5的写法
function add(a, b) {
//默认值
a = a || 10;
b = b || 5;
return a + b;
}
console.log(add());
</script>
- ES6的写法:ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面。
<script>
function add(a = 4, b = 3) {
//默认值
return a + b;
}
console.log(add());
</script>
2、剩余运算符 ...
- 把多个独立的合并到一个数组中
ES6 引入 rest 参数(形式为...变量名
),用于获取函数的多余参数,这样就不需要使用arguments
对象了。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。
function add(...values) {
let sum = 0;
for (var val of values) {
sum += val;
}
return sum;
}
add(2, 5, 3) // 10
上面代码的add
函数是一个求和函数,利用 rest 参数,可以向该函数传入任意数目的参数。
3、扩展运算符...
- 将一个数组分割,并将各个项作为分离的参数传给函数
对象的扩展运算符(...
)用于取出参数对象的所有可遍历属性,拷贝到当前对象之中。
let z = { a: 3, b: 4 };
let n = { ...z };
n // { a: 3, b: 4 }
扩展运算符可以用于合并两个对象。
let ab = { ...a, ...b };
// 等同于
let ab = Object.assign({}, a, b);
4、箭头函数
- 使表达更加简洁
- 简化回调函数
- 没有this绑定
- 箭头函数中没有arguments对象
- 箭头函数不能使用new关键字来实例化对象
使表达更加简洁、简化回调函数
let f = v=>v;
//等同于
let f = function(v){
return v;
}
// 有一个参数
let add = value => value;
// 有两个参数
let add = (value,value2) => value + value2;
let add = (value1,value2)=>{
return value1 + value2;
}
// 无参数
let fn = () => "hello world";
let doThing = () => {
}
//如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错。
let getId = id => ({id: id,name: 'mjj'}) //注意
let obj = getId(1);
箭头函数不能使用new关键字来实例化对象
let Person = ()=>{}
let p1 = new Person();// Person is not a constructor
四、函数参数解构赋值
1、数组解构
在以前,为变量赋值,只能直接指定值
let a = 1;
let b = 2;
let c = 3;
ES6允许我们这样写:
let [a,b,c] = [1,2,3];
如果解构不成功,变量的值就等于
undefined
let [foo] = [];
let [bar, foo] = [1];
foo`的值都会等于`undefined
2、对象解构
解构可以用于对象
let node = {
type:'identifier',
name:'foo'
}
let {type,name} = node;
console.log(type,name)//identifier foo
对象的解构赋值时,可以对属性忽略和使用剩余运算符
let obj = {
a:{
name:'张三'
},
b:[],
c:'hello world'
}
//可忽略 忽略b,c属性
let {a} = obj;
//剩余运算符 使用此法将其它属性展开到一个对象中存储
let {a,...res} = obj;
console.log(a,res);
3、函数参数解构赋值
直接看例子
function add([x, y]){
return x + y;
}
add([1, 2]); // 3
使用默认值
function addCart(n,num=0){
return n+num;
}
addCart(10);//10
addCart(10,20); //30
五、Map和Set
1、Set
- Set是类似数组的数据结构,和数组最大的区别是,
Set
中所有的成员都是唯一的。可以把Set
想象成是一个既没有重复元素,也没有顺序概念的数组 - Set本身是一个构造函数,用来生成 Set 数据结构
const s1 = new Set()
s1.add(1)
s1.add(2)
s1.add(1)
// s1 的值为 {1, 2}
Set ()
函数可接受一个可循环的数据结构,如数组、类数组、含有 iterable
接口的其他数据结构等作为参数
const s2 = new Set([1,2,1,4,3,4])
// s2 的值为 {1, 2, 3, 4}
常用属性和方法
const s2 = new Set(['a','b','c','d']);
// s2 的值为 {"a", "b", "c", "d"}
//查看成员数量
s2.size // 4
//用于向set 中添加一个值,返回Set结构本身,所以可以使用链式调用
s2.add(value)
//用于删除一个值,返回布尔值表示是否成功删除
s2.delete(value)
//用于查找 `Set`中是否包含某个值,返回布尔值
s2.has(value)
// 用于清除所有成员,无返回值
s2.clear()
set循环相关方法
keys():返回键名的遍历器
values():返回键值的遍历器
entries():返回键值对的遍历器
forEach():使用回调函数遍历每个成员
Set
结构没有键名,只有键值(或者说键名和键值是同一个值),所以key
方法和value
方法的行为完全一致。
entries
方法返回的同时包括键名和键值,所以每次输出一个数组,它的两个成员完全相等。
const s2 = new Set(['a', 'b', 'c', 'd']);
//转化成数组
let arry=[...s2];//['a', 'b', 'c', 'd']
//返回键名
s2.keys(item => {
console.log(item);// s2 的值为 {"a", "b", "c", "d"}
})
//返回键值
s2.values(item => {
console.log(item);// s2 的值为 {"a", "b", "c", "d"}
})
//返回所有键值对成员
s2.entries(item => {
console.log(item);// s2 的值为 {"a", "b", "c", "d"}
})
2、Set 对象作用:去重、交集等
//1 数组去重
let a=[1,2,3,4,5,6,7,1,2,3];
let b=new Set([...a]);
b=[...b];
// 2 求交集 并集 差集
let a=new Set[1,2,3];
let b=new Set[3,4,5];
//交集
let c=new Set([...a,...b]);
//并集
let d=new Set([...a].filter(x=>b.has[x]));
//差集
let d=new Set([...a].filter(x=>!b.has[x]));
3、Map
-
类似于对象,也是键值对的集合,但是
-
“键”的范围不限于字符串,各种类型的值都可以当作键
Map对象的属性
- size:返回Map对象中所包含的键值对个数
Map对象的方法:
- set(key, val): 向Map中添加新元素
- get(key): 通过键值查找特定的数值并返回
- has(key): 判断Map对象中是否有Key所对应的值,有返回true,否则返回false
- delete(key): 通过键值从Map中移除对应的数据
- clear(): 将这个Map中的所有元素删除
遍历方法:
- keys():返回键名的遍历器
- values():返回键值的遍历器
- entries():返回键值对的遍历器
- forEach():使用回调函数遍历每个成员
const map = new Map([['a', 1], ['b', 2]])
for (let key of map.keys()) {
console.log(key)
}
// "a"
// "b"
for (let value of map.values()) {
console.log(value)
}
// 1
// 2
for (let item of map.entries()) {
console.log(item)
}
// ["a", 1]
// ["b", 2]
// 或者
for (let [key, value] of map.entries()) {
console.log(key, value)
}
// "a" 1
// "b" 2
// for...of...遍历map等同于使用map.entries()
for (let [key, value] of map) {
console.log(key, value)
}
// "a" 1
// "b" 2
4、map与其他数据结构的互相转换
- map转换为数组(使用扩展运算符)
const arr = [[{'a': 1}, 111], ['b': 222]]
const myMap = new Map(arr)
[...myMap] // map转数组。 [[{'a': 1}, 111], ['b': 222]]
- Map与对象的互换
const obj = {}
const map = new Map([['a', 111], ['b', 222]])
for(let [key,value] of map) {
obj[key] = value
}
console.log(obj) // {a:111, b: 222}
JSON字符串要转换成Map可以先利用JSON.parse()转换成数组或者对象,然后再转换即可。
六、数组
参考地址:https://www.jianshu.com/p/e4fee40a52a9
Array.of()
Array.of()用于将一组参数转换为数组实例
Array.of(element0[, element1[, ...[, elementN]]])
// elementN 任意个参数,将按顺序成为返回数组中的元素。
Array.of(1, 2, 3); // [1, 2, 3]
Array.of(undefined); // [undefined]
Array.from()
Array.from()从一个类似数组或可迭代对象创建一个新的,浅拷贝的数组实例。
Array.from(arrayLike[, mapFn[, thisArg]])
/*
arrayLike 想要转换成数组的伪数组对象或可迭代对象。
mapFn 可选 如果指定了该参数,新数组中的每个元素会执行该回调函数。
thisArg 可选 可选参数,执行回调函数 mapFn 时 this 对象。
*/
Array.from('shan'); // [ "s", "h", "a", "n" ]
const set = new Set(["Mercury","Venus","Earth"]);
Array.from(set); // ["Mercury","Venus","Earth"]
Array.from([1, 2, 3], x => x + x); // [2, 4, 6]
排序方法
数组有两个方法可以用来对元素重新排序: reverse()和 sort()。默认情况下, sort()会按照升序重新排列数组元素,即最小的值在前面,最大的值在后面。
-
reverse():
-
sort():
var digit = [0, 1, 2, 3, 4, 5];
//从大到小
digist.reverse();
//从小到大
digist.sort(compare);
操作方法:
-
concat():可以接收多个参数,不会改变原来的数组。合并数组:
-
slice()方法:不会改变原始数组,返回截取的数组。
-
splice()方法:会改变原始数组,从何处添加/删除元素。
arr1.concat(arr2, arr3, ..., arrX)
// 注意范围,end表示从何处结束选取,不包括end,也就是[start, end)
array.slice(start, [end]);
// index表示从何处添加/删除元素
// howmany应该删除多少元素
// item1,.....,itemX新添加的元素
array.splice(index,howmany,item1,.....,itemX)
迭代方法
- every(): 对数组每一项都运行传入的函数,如果对每一项函数都返回 true, 则这个方法返回 true。
- filter():对数组每一项都运行传入的函数,函数返回 true 的项会组成数组之后返回。
- forEach():对数组每一项都运行传入的函数,没有返回值。
- map():对数组每一项都运行传入的函数,返回由每次函数调用的结果构成的数组。
- some():对数组每一项都运行传入的函数,如果有一项函数返回 true,则这个方法返回 true。
注意,这些方法都不改变调用它们的数组!
// every()
var digist1=[1,2,3,4,5,6];
var everyDigist=digist1.every(function(currentValue,index,arr){
return (currentValue>5);
});
console.log(everyDigist); //false
//filter()
var digist2=[1,2,3,4,5,6];
var filterRestult=digist2.filter(function(currentValue,index,arr){
return (currentValue>4);
});
console.log(filterRestult); //[ 5, 6 ]
// forEach()
var digist3=[5,6];
digist3.forEach(function(currentValue,index,arr){
console.log(currentValue+"_"+index+"_"+arr);
//5_0_5,6
//6_1_5,6
});
// map()
var digist=[1,2,3,4,5,6];
var mapResult=digist.map(function(currentValue,index,arr){
return currentValue-1;
});
console.log(mapResult); //[ 0, 1, 2, 3, 4, 5 ]
// some()
var digist=[1,2,3,4,5,6];
var someResult=digist.some(function(currentValue,index,arr){
return (currentValue>5);
});
console.log(someResult); //true
七、Promise 对象
异步编程模块在前端开发中,显得越来越重要。从最开始的XHR到封装后的Ajax都在试图解决异步编程过程中的问题。随着ES6新标准的到来,处理异步数据流又有了新的解决方案。在传统的ajax请求中,当异步请求之间的数据存在依赖关系的时候,就可能产生不优雅的多层回调,俗称”回调地域“(callback hell),这却让人望而生畏,Promise的出现让我们告别回调地域,写出更优雅的异步代码。
回调地狱带来的负面作用有以下几点:
- 代码臃肿。
- 可读性差。
- 耦合度过高,可维护性差。
- 代码复用性差。
- 容易滋生 bug。
- 只能在回调里处理异常。
在实践过程中,却发现Promise并不完美,Async/Await是近年来JavaScript添加的最革命性的的特性之一,Async/Await提供了一种使得异步代码看起来像同步代码的替代方法。接下来我们介绍这两种处理异步编程的方案。
什么是Promise
Promise 是异步编程的一种解决方案:
从语法上讲,Promise是一个对象,通过它可以获取异步操作的消息;
从本意上讲,它是承诺,承诺它过一段时间会给你一个结果。
promise有三种状态:pending(等待态),fulfilled(成功态),rejected(失败态);
状态一旦改变,就不会再变。
创造promise实例后,它会立即执行。
看段习以为常的代码:
// Promise是一个构造函数,自己身上有all,reject,resolve,race方法,原型上有then、catch等方法
let p = new Promise((resolve,reject)=>{
// 做一些异步操作
setTimeout(()=>{
/* let res = {
ok:1,
data:{
name:"张三"
}
} */
let res = {
ok:0,
error:new Error('有错')
}
if(res.ok === 1){
resolve(res.data);
}else{
reject(res.error.message)
}
}, 1000)
})
Promise的状态和值
Promise
对象存在以下三种状态
- Pending(进行中)
- Fulfilled(已成功)
- Rejected(已失败)
状态只能由
Pending
变为Fulfilled
或由Pending
变为Rejected
,且状态改变之后不会在发生变化,会一直保持这个状态。
Promise
的值是指状态改变时传递给回调函数的值
上面例子中的参数为resolve和reject,他们都是函数,用他们可以改变Promise的状态和传入的Promise的值
resolve` 和 `reject
resolve
: 将Promise对象的状态从Pending(进行中)
变为Fulfilled(已成功)
reject
: 将Promise对象的状态从Pending(进行中)
变为Rejected(已失败)
resolve
和reject
都可以传入任意类型的值作为实参,表示Promise
对象成功(Fulfilled)
和失败(Rejected)
的值
then方法
p.then((data)=>{
console.log(data);
return data;
},(error)=>{
console.log(error)
}).then(data=>{
console.log(data);
})
promise的then方法返回一个promise对象,所以可以继续链式调用
上述代码我们可以继续改造,因为上述代码不能传参
function timeout(ms) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('hello world')
}, ms);
})
}
timeout(1000).then((value) => {
console.log(value);
})
复制代码
then方法的规则
then
方法下一次的输入需要上一次的输出- 如果一个promise执行完后 返回的还是一个promise,会把这个promise 的执行结果,传递给下一次
then
中 - 如果
then
中返回的不是Promise对象而是一个普通值,则会将这个结果作为下次then的成功的结果 - 如果当前
then
中失败了 会走下一个then
的失败 - 如果返回的是undefined 不管当前是成功还是失败 都会走下一次的成功
- catch是错误没有处理的情况下才会走
then
中不写方法则值会穿透,传入下一个then
中
Promise封装XHR对象
const getJSON = function (url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.onreadystatechange = handler;
xhr.responseType = 'json';
xhr.setRequestHeader('Accept', 'application/json');
xhr.send();
function handler() {
console.log(this.readyState);
if (this.readyState !== 4) {
return;
}
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
}
}
})
}
getJSON('https://free-api.heweather.net/s6/weather/now?location=beijing&key=4693ff5ea653469f8bb0c29638035976')
.then((res) => {
console.log(res);
}, function (error) {
console.error(error);
})
//then方法的链式调用
getJSON('https://free-api.heweather.net/s6/weather/now?location=beijing&key=4693ff5ea653469f8bb0c29638035976')
.then((res)=>{
return res.HeWeather6;
}).then((HeWeather6)=>{
console.log(HeWeather6);
})
复制代码
catch方法
catch(err=>{})`方法等价于`then(null,err=>{})
getJSON('https://free-api.heweather.net/s6/weather/now?location=beijing&key=4693ff5ea653469f8bb0c29638035976')
.then((json) => {
console.log(json);
}).then(null,err=>{
console.log(err);
})
//等价于
getJSON('https://free-api.heweather.net/s6/weather/now?location=beijing&key=4693ff5ea653469f8bb0c29638035976')
.then((json) => {
console.log(json);
}).catch(err=>{
console.log(err);
})
复制代码
resove()
resolve()
方法将现有对象转换成Promise对象,该实例的状态为fulfilled
let p = Promise.resolve('foo');
//等价于 new Promise(resolve=>resolve('foo'));
p.then((val)=>{
console.log(val);
})
复制代码
reject()
reject()
方法返回一个新的Promise实例,该实例的状态为rejected
let p2 = Promise.reject(new Error('出错了'));
//等价于 let p2 = new Promise((resolve,reject)=>reject(new Error('出错了)));
p2.catch(err => {
console.log(err);
})
复制代码
all()方法
all()方法提供了并行执行异步操作的能力,并且再所有异步操作执行完后才执行回调
试想一个页面聊天系统,我们需要从两个不同的URL分别获得用户的的个人信息和好友列表,这两个任务是可以并行执行的,用Promise.all实现如下
let meInfoPro = new Promise( (resolve, reject)=> {
setTimeout(resolve, 500, 'P1');
});
let youInfoPro = new Promise( (resolve, reject)=> {
setTimeout(resolve, 600, 'P2');
});
// 同时执行p1和p2,并在它们都完成后执行then:
Promise.all([meInfoPro, youInfoPro]).then( (results)=> {
console.log(results); // 获得一个Array: ['P1', 'P2']
});
复制代码
race()方法
有些时候,多个异步任务是为了容错。比如,同时向两个URL读取用户的个人信息,只需要获得先返回的结果即可。这种情况下,用Promise.race()实现:
let meInfoPro1 = new Promise( (resolve, reject)=> {
setTimeout(resolve, 500, 'P1');
});
let meInfoPro2 = new Promise( (resolve, reject)=> {
setTimeout(resolve, 600, 'P2');
});
Promise.race([meInfoPro1, meInfoPro2]).then((result)=> {
console.log(result); // P1
});
复制代码
Promise.all接受一个promise对象的数组,待全部完成之后,统一执行success;
Promise.race接受一个包含多个promise对象的数组,只要有一个完成,就执行success
举个更具体的例子,加深对race()方法的理解
当我们请求某个图片资源,会导致时间过长,给用户反馈
用race给某个异步请求设置超时时间,并且在超时后执行相应的操作
function requestImg(imgSrc) {
return new Promise((resolve, reject) => {
var img = new Image();
img.onload = function () {
resolve(img);
}
img.src = imgSrc;
});
}
//延时函数,用于给请求计时
function timeout() {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject('图片请求超时');
}, 5000);
});
}
Promise.race([requestImg('images/2.png'), timeout()]).then((data) => {
console.log(data);
}).catch((err) => {
console.log(err);
});
八、sync 函数
异步操作是JavaScript编程的麻烦事,很多人认为async函数是异步编程的解决方案
Async/await介绍
- async/await是写异步代码的新方式,优于回调函数和Promise。
- async/await是基于Promise实现的,它不能用于普通的回调函数。
- async/await与Promise一样,是非阻塞的。
- async/await使得异步代码看起来像同步代码,再也没有回调函数。但是改变不了JS单线程、异步的本质。(异步代码同步化)
Async/await的使用规则
-
凡是在前面添加了async的函数在执行后都会自动返回一个Promise对象
async function test() { } let result = test() console.log(result) //即便代码里test函数什么都没返回,我们依然打出了Promise对象 复制代码
-
await必须在async函数里使用,不能单独使用
async test() { let result = await Promise.resolve('success') console.log(result) } test() 复制代码
-
await后面需要跟Promise对象,不然就没有意义,而且await后面的Promise对象不必写then,因为await的作用之一就是获取后面Promise对象成功状态传递出来的参数。
function fn() { return new Promise((resolve, reject) => { setTimeout(() => { resolve('success') }) }) } async test() { let result = await fn() //因为fn会返回一个Promise对象 console.log(result) //这里会打出Promise成功后传递过来的'success' } test() 复制代码
Async/Await的用法
- 使用await,函数必须用async标识
- await后面跟的是一个Promise实例
function loadImg(src) {
const promise = new Promise(function (resolve, reject) {
const img = document.createElement('img')
img.onload = function () {
resolve(img)
}
img.onerror = function () {
reject('图片加载失败')
}
img.src = src
})
return promise
}
const src1 = 'https://hcdn1.luffycity.com/static/frontend/index/banner@2x_1574647618.8112254.png'
const src2 = 'https://hcdn2.luffycity.com/media/frontend/index/%E7%94%BB%E6%9D%BF.png'
const load = async function () {
const result1 = await loadImg(src1)
console.log(result1)
const result2 = await loadImg(src2)
console.log(result2)
}
load()
复制代码
当函数执行的时候,一旦遇到 await 就会先返回,等到触发的异步操作完成,再接着执行函数体内后面的语句。
async/await的错误处理
关于错误处理,如规则三所说,await可以直接获取到后面Promise成功状态传递的参数,但是却捕捉不到失败状态。在这里,我们通过给包裹await的async函数添加then/catch方法来解决,因为根据规则一,async函数本身就会返回一个Promise对象。
const load = async function () {
try{
const result1 = await loadImg(src1)
console.log(result1)
const result2 = await loadImg(src2)
console.log(result2)
}catch(err){
console.log(err);
}
}
load()
复制代码
为什么Async/Await更好?
Async/Await较Promise有诸多好处,以下介绍其中三种优势:
-
简洁
使用Async/Await明显节约了不少代码。我们不需要写.then,不需要写匿名函数处理Promise的resolve值,也不需要定义多余的data变量,还避免了嵌套代码。
-
中间值
在前端编程中,我们偶尔会遇到这样一个场景:我们需要发送多个请求,而后面请求的发送总是需要依赖上一个请求返回的数据。对于这个问题,我们既可以用的Promise的链式调用来解决,也可以用async/await来解决,然而后者会更简洁些
const makeRequest = () => {
return promise1()
.then(value1 => {
return promise2(value1)
.then(value2 => {
return promise3(value1, value2)
})
})
}
复制代码
使用async/await的话,代码会变得异常简单和直观
const makeRequest = async () => {
const value1 = await promise1()
const value2 = await promise2(value1)
return promise3(value1, value2)
}
复制代码
- 提高可读性
下面示例中,需要获取数据,然后根据返回数据决定是直接返回,还是继续获取更多的数据。
const makeRequest = () => {
return getJSON()
.then(data => {
if (data.needsAnotherRequest) {
return makeAnotherRequest(data)
.then(moreData => {
console.log(moreData)
return moreData
})
} else {
console.log(data)
return data
}
})
}
复制代码
代码嵌套(6层)可读性较差,它们传达的意思只是需要将最终结果传递到最外层的Promise。使用async/await编写可以大大地提高可读性:
const makeRequest = async () => {
const data = await getJSON()
if (data.needsAnotherRequest) {
const moreData = await makeAnotherRequest(data);
console.log(moreData)
return moreData
} else {
console.log(data)
return data
}
}
复制代码
九、Class的基本用法
简介
JavaScript语言中,生成实例对象的传统方法是通过构造函数
function Person(name,age) {
this.name = name;
this.age = age;
}
Person.prototype.sayName = function() {
return this.sayName;
}
let p = new Person('小马哥',18);
console.log(p);
复制代码
上面这种写法跟传统的面向对象语言(比如 C++ 和 Java)差异很大,很容易让新学习这门语言的程序员感到困惑
ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过class
关键字,可以定义类。
基本上,ES6 的class
可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class
写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。上面的代码用 ES6 的class
改写,就是下面这样
class Person {
// constructor方法 是类的默认方法,通过new命令生成对象实例时,自动调用该方法,一个类必须有constructor方法,如果没有定义,会被默认添加
constructor(name, age) {
this.name = name;
this.age = age;
}
//等同于Person.prototype = function sayName(){}
sayName(){
return this.name;
}
}
console.log(Person===Person.prototype.constructor)
复制代码
类的方法内部如果含有
this
,它默认指向类的实例
十、Module 模块化
概述
历史上,JavaScript 一直没有模块(module)体系,无法将一个大程序拆分成互相依赖的小文件,再用简单的方法拼装起来。其他语言都有这项功能,比如 Ruby 的require
、Python 的import
,甚至就连 CSS 都有@import
,但是 JavaScript 任何这方面的支持都没有,这对开发大型的、复杂的项目形成了巨大障碍。
在 ES6 之前,社区制定了一些模块加载方案,最主要的有 CommonJS 和 AMD 两种。前者用于服务器,后者用于浏览器。ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。
ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。比如,CommonJS 模块就是对象,输入时必须查找对象属性。
export命令
模块功能主要由两个命令构成:export
和import
。export
命令用于规定模块的对外接口,import
命令用于输入其他模块提供的功能。
一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取。如果你希望外部能够读取模块内部的某个变量,就必须使用export
关键字输出该变量
//module/index.js
export const name = 'zhangsan ';
export const age = 18;
export const color = 'red ';
export const sayName = function() {
console.log(fristName);
}
//也可以这样
const name = 'zhangsan ';
const age = 18;
const color = 'red ';
const sayName = function() {
console.log(fristName);
}
export {name,age,color,sayName}
复制代码
import命令
使用export
命令定义了模块的对外接口以后,其他 JS 文件就可以通过import
命令加载这个模块。
//main.js
import {name,age,color,sayName,fn} from './modules/index.js';
复制代码
如果想为输入的变量重新取一个名字,import
命令要使用as
关键字,将输入的变量重命名
import * as obj from './modules/index.js';
console.log(obj);
复制代码
export default 命令
使用export default
命令为模块指定默认输出
//export-default.js
export default function(){
console.log('foo');
}
//或者写成
function foo() {
console.log('foo');
}
export default foo;
复制代码
在其它模块加载该模块时,import
命令可以为该匿名函数指定任意名字
//import-default.js
import customName from './export-default.js'
customNmae();//foo
复制代码
如果想在一条import语句中,同事输入默认方法和其他接口,可以写成下面这样
import customName,{add} from 'export-default.js'
对应上面export
语句如下
//export-default.js
export default function(){
console.log('foo');
}
export function add(){
console.log('add')
}
export default
也可以用来输出类。
// MyClass.js
export default class Person{ ... }
// main.js
import Person from 'MyClass';
let o = new Person();
复制代码
十一、ES6模块化
Node.js 中通过bable 体验ES6模块化
npm install --sava-dev
1、如果只想导入执行代码,导入方式
import './js'
2、导入
import 模块名称 from ‘包名’
3、暴露成员:
通过export default 和export向外暴露成员
注意:
(1)在一个模块中只能有一个export default进行暴露:默认导出
引入:
import login from ‘../login.js’
注:login为一个对象:export default中的对象
(2)通过暴露成员变量export :可以使用多次 按需导出
module中的代码:
export var title='星星'
引入:
引入:import {title} from '../login.js'
或者 import {title as title123,info} from '../login.js'
(3)在一个模块中,可以同时使用export和export default向外暴露成员
组合引入:
import login ,{title} from '../js'