最典型的例子就是如下案例:
//首先是用var
var arr = [];
for (var i = 0; i < 10; i++) {
arr[i] = function(){
console.log(i);
}
}
arr[8]();//结果是: 10
//但是改为用let声明以后
var arr = [];
for (var i = 0; i < 10; i++) {
arr[i] = function(){
console.log(i);
}
}
arr[8]();//结果是: 8
用let声明的的变量仅仅作用在块级作用域中
//用var定义的变量存在变量提升 所以结果是undedined
var a = 1;
(function() {
console.log(a);
var a = 8;
})() //undefined
//用let声明的变量 在块级作用域中是封闭的 不存在变量提升 必须先声明后使用
var a = 1;
(function() {
console.log(a);
let a = 8;
})() //a is not defined
{
var a = 1;
let a = 1;
}// a has already been declared 报错
{
let a = 1;
let a = 2;
}// a has already been declared 报错
** 注意函数内不能用let重新声明函数的参数
//函数内不能用let重新声明函数的参数
//var
function fn (age) {
var age = 10;
console.log(age);
}
fn(20);//10
//let
function fn (age) {
let age = 10;
console.log(age);
}
fn(20);//age has already been declared
//注意本实例在HTML文件中测试
<script>
var a = 10;
console.log(this.a);
//10
let a = 10;
console.log(this.a);
//undefined
script>
const是constan(常量)的缩写,用const声明的的变量的值是不能修改的,即为常量。
//声明以后不能修改
const age = 10;
age = 20;
//报错
const age;
//报错, 必须赋值
const Person = {
"name": "张三"
}
Person.name = '李四';
Person.age = 20;
console.log(Person);
//结果输出
//{name:'李四', age: 20}
首先,赋值分为传值赋值和传址赋值,因为对象是引用类型,所以上例中用的是传址赋值,Person实际上存储的是地址,由const定义后Person的地址是不能变的,但是内容同之前一样。如下:
const Person = {
name: '张三',
age: 10
}
const Student = {
name: "lili"
}
Person = Student;
//报错
针对ES6的兼容性问题,很多团队为此开发出了多种语法解析转换工具,把我们写的ES6语法转换成ES5,相当于在ES6和浏览器之间做了一个翻译官。比较通用的工具方案有babel,jsx,traceur,es6-shim等。此外,浏览器自身也加快速度兼容ES6的新特性,其中对ES6新特性最友好的是Chrome和Firefox浏览器。
var [a, b, c] = [1, 2, 3];
console.log(a);//1
console.log(b);//2
console.log(c);//3
var [a, b, [c, d]] = [1, 2, [3, 4]];
console.log(a);//1
console.log(b);//2
console.log(c);//3
console.log(d);//4
var [a, b] = [1, 2, [3, 4]];
console.log(a);//1
console.log(b);//2
console.log(c);//c is not defined
var [a, b, c = 5] = [1, 2];
console.log(a);//1
console.log(b);//2
console.log(c);//5
var [a, b, c = 5] = [1, 2, 3];
console.log(a);//1
console.log(b);//2
console.log(c);//3
//需要用{}来进行解构
var {a, b, c} = {"a": "aa", "b": "bb", "c": 10};
console.log(a);//aa
console.log(b);//bb
console.log(c);//10
** 需要注意的是,变量的名字需要和对象的属性一样
//我们将b 和 c 换了一下位置
var {a, c, b} = {"a": "aa", "b": "bb", "c": 10};
console.log(a);//aa
console.log(b);//bb
console.log(c);//10
//输出的还是对应的值,并不影响
//如果变量的名字和对象的名字不对应的话,就会输出undefined
var {a} = {"b": 10};
console.log(a);//undefined
//如果我们想用别的名字对对象进行解构赋值的话,可以对变量起一个别名
var {b:a} = {"b": 10};
//10
赋值方式同数组解构赋值相似。
var {a:{b}} = {"a":{"b": 10}};
console.log(b);//10
var {a, b = 3} = {"a": "hello", "b": 10};
console.log(a);//hello
console.log(b);//10
var [a, b, c] = "hello";
console.log(a);//h
console.log(b);//e
console.log(c);//l
需要注意的是,字符串的解构赋值需要用[ ]
传统的做法是通过第三个变量,但是用ES6的新属性会更加的便捷。
var a = 10;
var b = 20;
[b, a] = [a, b];
console.log(a);//20
console.log(b);//10
用{ }来获取。
function demo(){
var a = "hello";
var b = 10;
return {"a": a, "b": b};
}
var {a, b} = demo();
console.log(a);//hello
console.log(b);//10
需要注意的是变量的名字需要和返回对象的属性名字一样。
function demo({a, b}){
console.log(a);
console.log(b);
}
demo({a:10, b: 20, c:30});
通过这种方法就能很方便的拿到JSON中想要的参数,其余的参数就会省略,比如c。
function demo({a = "hello"}){
consle.log(a);
}
demo({});//hello
需要注意的是,函数的参数需要传空对象。
简单的来说就是这是一种定义字符串的新形式,简化了拼接字符串的操作。
var name = "lili";
console.log(`hello ${name}`);//hello lili
var fn = function (){
return `lili`;
}
console.log(`hello ${fn()}`);//hello lili
var a = 10;
var b = 20;
console.log(`result is ${a + b}`);//result is 30
不改变原变量的值。
var a = "x";
var b = a.repeat(3);
console.log(a);//x
console.log(b);//xxx
当前字符串中是否包含xx,包含返回true,否则返回false。
var a = "hello";
var b = "l";
console.log(a.includes(b));
//true
可以指定搜索开始的位置。
var a = "hello";
var b = "e";
console.log(a.includes(b, 2));
//false
用于判断指定字符串是否出现在目标字符串的开始(结束)的位置,返回true(false)。
var a = "hello";
console.log(a.startsWith("h"));//true
console.lgo(a.startsWith("e", 1));//true
需要注意的是,starts(ends)的s。
使转义字符失去意义。
console.log(`hello \n world`);
//hello
//world
console.log(string.raw`hello \n world`);//hello \n world
需要注意的是raw后跟的不再是(),而是字符串,并且得是模板字符串()格式。
ES5中,isNaN(),isFinite(),parseInt(),parseFloat()等,都是全局函数,直接用就行,但是在ES6中,他们被移植到了Number对象上。使用之前需要指明Number对象。
ES5中,isNaN()会将非数值参数转换成数值再进行判断,而Number.isNaN()只对数值类型有效,非数值一律返回false.
console.log(isNaN("x"));//true 无法将字符串转换成数值=>NaN
console.log(isNaN(1));//false 1不是NaN
console.log(Number.isNaN("x"));//false
判断是否是一个非无穷数。无穷返回false,有穷返回true。
console.log(Number.isFinite(Infinity));//false Infinity是一个无穷数
console.log(Number.isFinite(1));//true 1是有穷数
注意是非。
console.log(Number.isFinite("a"));//false "a"是字符串,返回的是false
需要注意的是,返回false的时候,一个是无穷数,另一个是字符串。
解析字符串,返回整数。同ES5中的一样。
判断是否是一个整数。
但是需要注意的是在javascript中,浮点数和整数在内存中的存储方式是一样的,如果小数点后都是0,就会被认为是整数。
console.log(Number.isInteger(10.00));//true
用于去除一个小数的小数部分,返回一个整数。
console.log(Math.trunc(3.45));//3
*需要注意的是这里是Math对象的方法
用于判断一个数是正数,负数,和0。
console.log(Math.sign(10));//1
console.log(Math.sign(0));//0
console.log(Math.sign(-10));//-1
将一组值转换成数组。
console.log(Array.of(1,2,3));//[1,2,3]
将类数组对象,可遍历对象转换成数组。
获取DOM的时候,返回的是一个对象,但是是类数组的对象,因为通过ele.length
可以获取其长度。
var ele = document.getElementsByTagName('div');
console.log(Array.from(ele) instanceof Array);//true
我们还可以很方便的将字符串转换成数组。
var str = "hello";
console.log(Array.from(str));//["h","e","l","l","o"]
找出数组中符合条件的第一个值,并返回其值。
var arr = [1,3,5,7];
console.log(arr.find(function(value){
return value > 3;
}));//5
如果没有符合的值得话,就会返回undefined。
找出数组中符合条件的第一个值,并返回其下标。
var arr = [1,3,5,7];
console.log(arr.findIndex(function(value){
return value > 3;
}));//2
填充数组
var arr = [1, 1, 1];
console.log(arr.fill(2));//[2,2,2]
第二,三个参数可以设置其填充的范围。
var arr = [1, 1, 1];
console.log(arr.fill(2, 1, 3));//[1,2,2]
注意参数规定的是左闭右开区间。
对数组的键和值进行遍历,返回一个遍历器,然后可以用for…of…对这个遍历器进行遍历。
var arr = [2,4,6];
for (var [index, value] of arr.entries()) {
console.log(`index:${index} value:${value}`);
}
//index:0 value:2
//index:1 value:4
//index:2 value:6
既然它返回一个遍历器,那么我们也可以这样写,把遍历器单独存放在一个变量里。
var arr = [2,4,6];
var arrEn = arr.entries();
for (var [index, value] of arrEn) {
console.log(`index:${index} value:${value}`);
}
//index:0 value:2
//index:1 value:4
//index:2 value:6
上边例子中的arrEn打印出来的结果是Object [Array Iterator] {}。
同entries()相似,返回的都是一个遍历器,不同的是keys()只返回数组的下标遍历器。用法同上。
同entries()相似,返回的都是一个遍历器,不同的是values()只返回数组的值遍历器。用法同上
var name = "lili";
var age = 10;
var Person = {name, age};
console.log(Person);//{name: "lili", age:10}
var demo = {
fn(){
console.log("hi");
}
}
demo.fn();//hi
var a = 'hi';
var b = 10;
var Person = {
[a + b]:"lili"
}
console.log(Person);//{hi10: 'lili'}
相当于全等(===)。
var a = '1';
var b = 1;
console.log(Object.is(a, b));//false
可以用于给对象添加属性,方法,克隆对象,合并多个对象,为对象的属性设置默认值。同时没有的属性将会添加,相同属性名的则会覆盖。
//添加属性 合并
var a = {
a: 'a'
}
var b = {
b: 'b'
}
Object.assign(a, b);
console.log(a);//{a: "a", b: "b"}
//覆盖
var a = {
say(){
console.log("hi")
}
}
var b = {
say(){
console.log('hello')
}
}
Object.assign(a, b);
a.say();//hello
获取对象的prototype属性。
function a (){};
a.prototype = {
say(){
console.log('hi');
}
}
var aa = new a();
console.log(Object.getPrototypeOf(aa));//{say:f}
上例是获取对象的prototype属性,本方法是设置对象的prototype属性。
function a (){};
a.prototype = {
say(){
console.log('hi')
}
};
var aa = new a();
aa.say();//hi
Object.setPrototypeOf(aa, {
say(){
console.log("hello");
}
})
aa.say();//hello
function a (name = 'hi', age = 10){
console.log(name, age);
}
a();//hi 10
需要注意的是,如果函数有多个参数,但只有部分需要指定默认值,另一部分不需要的话,那么,设定默认值的参数一定要放在最后。
function a (name, age = 10){
console.log(name, age);
}
a("hi");//hi 10
参数中的 …+变量名 ,用于获取剩余的参数。
注意要将 … 放在参数的最后使用。
function a(a, ...res) {
console.log(res);
}
a(2,3,4,5,6);//[3,4,5,6]
将数组中的内容拆解出来。
var arr = [1,2,3];
console.log(...arr);//1 2 3
//传统写法
function a (a){
return a
}
//箭头函数
var a = a => a;
需要注意的是,如果函数的参数,或者是函数体的语句超过1的话,就需要将相应的(),{ }加上。
//传统写法
function a (name, age){
console.log(name);
console.log(age);
}
//箭头函数
var a = (name, age) => {
cosnle.log(name);
console.log(age);
}
普通函数和箭头函数this:
JavaScript有6中数据类型,分别是:String字符串类型;Number数字类型;Object对象类型;Boolean布尔值类型;Null空值;Undefined 未定义;而symbol是新增的数据类型。初衷是为了解决对象属性命名冲突的问题。
let a = Symbol();
let b = Symbol();
console.log(a == b);//false
console.log(a === b);//false
console.log(a);//Symbol()
console.log(b);//Symbol()
他的值是独一无二的。
它可以接受参数。
let a = Symbol("a");
let b = Symbol("b");
console.log(a);//Symbol(a)
console.log(b);//Symbol(b)
接受参数的Symbol相当于加上了描述。
即是参数一样,值也是不一样的。
let a = Symbol("a");
let b = Symbol("a");
console.log(a === b);//false
用Symbol()当做对象的属性名的时候,只能用[ ]来获取属性的值。另外注意,通过[ ]来获取属性的时候,Symbol和普通属性名的区别,一个是变量的一个是字符串。
let name = Symbol('name');
let demo = {
[name]: "lili",
age: 20
}
console.log(demo.name);//undefined
console.log(demo[name]);//lili
console.log(demo['age'])//20
用Symbol做属性名的时候,for…of…/for…in…/Object.keys()是获取不到的。
通过它可以获取symbol定义的属性,并返回一个数组。
let a = Symbol('a');
let b = Symbol('b');
let demo = {
[a]: 'a',
[b]: 'b'
}
console.log(Object.getOwnPropertySymbols(demo));
//[Symbol(a), Symbol(b)]
次函数既可以获取普通的属性,也可以获取用Symbol()定义的属性。用法同上。
根据参数名,去全局环境中搜索Symbol值,存在就返回这个Symbol值,不存在就创建。
Symbol.for ( )创建的值是存在于全局环境中的,Symbol.for()是可以搜索到的,而Symbol()创建的变量却不能被找到。
let a = Symbol('a');
let b = Symbol.for("a");
console.log(a === b);//false
它用来获取,存在于全局环境变量中的symbol值的key,也就是这个symbol值是由Symbol.for() 创建的。如果找不到就返回undefined。
let a = Symbol.for('a');
console.log(Symbol.keyFor(a));//a
get方法拦截了对象的读取操作,set方法拦截了改写操作。
var demo = {
name: 'hello',
age: 20
}
var pro = new Proxy(demo, {
get: function(target, property){
if (property == 'name'){
return 'hi'
}else {
return target[property];
}
},
set:function (target, property, value){
if (property == 'age'){
target[property] = '88';
}
}
})
//通过代理访问、
console.log(pro.name);//hi
pro.age = 'test';
console.log(pro.age);//88
拦截Object.keys()对对象属性的遍历操作。
var demo = {
name: 'hello',
age: 20
}
var pro = new Proxy(demo, {
ownKeys: function (target) {
return ['name'];
}
})
console.log(Object.keys(demo));
//[ 'name', 'age' ]
console.log(Object.keys(pro));
//[ 'name']
拦截key in object的操作,返回一个bool值。
var demo = {
name: "lili",
age: 10
}
var pro = new Proxy(demo , {
has: function (target, pro){
if (pro == 'name'){
return false;
}else {
return true;
}
}
})
console.log('name' in pro);//false
console.log('age' in pro);//true
拦截函数的调用。
function fn(){
console.log("hi");
}
var app = new Proxy(fn, {
apply: function (){
console.log('hello');
}
})
app();//hello
如果创建了代理之后又想取消代理的话,我们可以用Proxy.revocable( )函数来实现,它会返回一个对象,对象中含有一个proxy属性,它就是Proxy的代理实例对象;还有一个revoke属性,它是一个方法,用于取消代理
var demo = {
name:'hi'
}
var pro = {
get:function(tar, pro){
return 'hello'
}
}
var demoPro = Proxy.revocable(demo,pro);
console.log(demoPro.proxy.name);//hello
demoPro.revoke();
console.log(demoPro.proxy.name);//报错, 代理被取消
需要注意的是,Proxy.revocable()函数返回的是一个对象,包含的内容如下:
var demo = {
name:'hi'
}
var pro = {
get:function(tar, pro){
return 'hello'
}
}
var demoPro = Proxy.revocable(demo,pro);
console.log(demoPro);
//{ proxy: Object [hello] { name: 'hi' }, revoke: [Function] }
for…of 一种用于遍历数据结构的方法。它可遍历的对象包括数组,对象,字符串,set和map结构等具有iterator 接口的数据结构。
注意的是,用for in 遍历的得到的是字符串类型,返回的是索引,而用for of遍历得到的是数字类型,返回的是值。
for of还支持类数组的遍历,如DOM list.
for of 不遍历对象是因为,对象没有提供遍历器(iterator).
当可遍历对象被for…of遍历的时候,Symbol.iterator就会被调用,返回一个iterator对象。其中还有一个很重要的方法:next( );
for…of的原理就是:先调用可遍历对象的Symbol.iterator方法,得到一个iterator遍历器对象,然后就在遍历器上不断调用next( )方法,直到done的值为true的时候,就表示遍历完成结束了。
var arr = [1,2,3];
var ite = arr[Symbol.iterator]();
console.log(ite.next());//{ value: 1, done: false }
console.log(ite.next());//{ value: 1, done: false }
console.log(ite.next());//{ value: 1, done: false }
console.log(ite.next());//{ value: undefined, done: true }
done是是否结束的标志,如果结束的话就是true。
ES6新增的一种数据结构,类似于数组,但成员是唯一的,且无序,没有重复的值。
set本身是一种构造函数,用来生成set数据结构。
set对象允许存储任何类型的唯一值。
需要注意的是,向 Set 加入值的时候,不会发生类型转换,所以5
和"5"
是两个不同的值。Set 内部判断两个值是否不同,使用的算法叫做“Same-value-zero equality”,它类似于精确相等运算符(===
),但与精确运算符的主要的区别是NaN
等于自身,而精确相等运算符认为NaN
不等于自身。
var arr = [1,1,2,2]
var set = new Set(arr);
console.log([...set]);//[ 1, 2 ]
console.log(set.size);//2
拥有size属性。
删除set中的指定值。返回bool值。
var arr = [1,1,2,2]
var set = new Set(arr);
console.log(set);//Set { 1, 2 }
set.delete(1);
console.log(set);//Set { 2 }
清除所有成员。
判断是否包含指定值。返回bool值。
返回一个键值对的遍历器。
需要注意的是set遍历器返回的键值是同一个值。
keys( )方法:返回键名的遍历器。
values( )方法:返回键值的遍历器。
遍历每一个成员。
forEach(callbackFn, Arg):用于对集合成员执行callbackFn操作,如果提供了 Arg 参数,回调中的this会是这个参数,没有返回值
可以用于数组去重。
var arr = [1,1,2,2,3,3];
var set = new Set(arr);
console.log(Array.from(set));//[ 1, 2, 3 ]
WeakSet 对象允许你将弱引用对象储存在一个集合中。
WeakSet 与 Set 的区别:
方法:
是一组键值对的结构,具有极快的查找速度。
操作方法:
遍历方法:
WeakMap 对象是一组键值对的集合,其中的键是弱引用对象,而值可以是任意。
注意,WeakMap 弱引用的只是键名,而不是键值。键值依然是正常引用。
WeakMap 中,每个键对自己所引用对象的引用都是弱引用,在没有其他引用和该键引用同一对象,这个对象将会被垃圾回收(相应的key则变成无效的),所以,WeakMap 的 key 是不可枚举的。
方法:
集合 与 字典 的区别:
Set:
WeakSet:
Map:
WeakMap:
let pro = new Promise(function (resolve, reject){
//...
})
Promise对象是全局对象,你也可以理解为一个类,创建Promise实例的时候,要有那个new关键字。参数是一个匿名函数,其中有两个参数:resolve和reject,两个函数均为方法。resolve方法用于处理异步操作成功后业务;reject方法用于操作异步操作失败后的业务。
let pro = new Promise(function(resolve,reject){
//实例化后状态:pending
if('操作成功'){
resolve();
//resolve方法调用,状态为:fulfilled
}else{
reject();
//reject方法调用,状态为:rejected
}
});
用于绑定处理操作后的处理程序。
pro.then(function (res) {
//操作成功的处理程序
},function (err) {
//操作失败的处理程序
});
对于操作异常的程序,Promise专门提供了一个实例方法来处理:catch( )方法
pro.then(function (res) {
//操作成功的处理程序
}).catch(function (error) {
//操作失败的处理程序
});
因为then方法和catch方法调用后,都会返回promise对象,所以可以使用链式语法。
//用new关键字创建一个Promise实例
let pro = new Promise(function(resolve,reject){
//假设condition的值为true
let condition = true;
if(condition){
//调用操作成功方法
resolve('操作成功');
//状态:pending->fulfilled
}else{
//调用操作异常方法
reject('操作异常');
//状态:pending->rejected
}
});
//用then处理操作成功,catch处理操作异常
pro.then(function (res) {
//操作成功的处理程序
console.log(res)
}).catch(function (error) {
//操作失败的处理程序
console.log(error)
});
//控制台输出:操作成功
接受一个数组作为参数,数组的元素是Promise实例对象,当参数中的实例对象的状态都为fulfilled时,Promise.all( )才会有返回。
//创建实例pro1
let pro1 = new Promise(function(resolve){
setTimeout(function () {
resolve('实例1操作成功');
},5000);
});
//创建实例pro2
let pro2 = new Promise(function(resolve){
setTimeout(function () {
resolve('实例2操作成功');
},1000);
});
Promise.all([pro1,pro2]).then(function(result){
console.log(result);
});
//打印结果:["实例1操作成功", "实例2操作成功"]
它的参数要求跟Promise.all( )方法一样,不同的是,它参数中的promise实例,只要有一个状态发生变化(不管是成功fulfilled还是异常rejected),它就会有返回。
使用super注意:
作为一个模块,它可以选择性地给其他模块暴露(提供)自己的属性和方法,供其他模块使用。
作为一个模块,可以根据需要,引入其他模块的提供的属性或者方法,供自己模块使用。
//A
export var name = 'hi';
//B
import {name} from "./A.js";
//A
var a = 'a';
var b = 'b';
var c = 'c';
export {a, b, c};
//B
import {a, b, c} from "./A.js";
import {name as a} from "./A.js";
import * as a from "./A.js";
a.say();
//A
export default function(){
console.log('hi');
}
//B
//取任意名字即可
import a from './A.js'