let & const
let的特点是,没有变量生命提升,而且会统治作用域(也就是不可以重复声明),在块级作用域内部声明的变量同样外部不能访问
const在满足以上所有的特点的同时,const声明的是常量(改变会报错),但这个常量指的是指向的内存地址不可以改变
{
var a = 1;
let b = 1;/
}
console.log(a);
console.log(b);复制代码
访问不到b
依靠let这个特性我们可以解决闭包问题,那什么是闭包?闭包就是有权访问另一个函数作用域中变量的函数,我自己对闭包的理解其实就是“额外”保存了完整的作用域链内变量(记住是变量不是this)的一个函数,就常见来说函数内再套用一层函数并返回就可能会产生闭包
比如closure就是一个闭包。通常,函数的作用域及其所有变量都会在函数执行结束后被销毁。但是,在创建了一个闭包以后,这个函数的作用域就会一直保存到闭包不存在为止。
function func(){
var a = 1,b = 2;
function closure(){
return a+b;
}
return closure;
}复制代码
那举个万年例子
var arr = Array(10);
for(var i = 0; i < 10; i++) {
//let i = i;
arr[i] = function () {
return i;
}
}
for(var a = 0; a < 10; a++) {
console.log(arr[a]());
}复制代码
打印10个10对吧,最常见的闭包问题,这里第一个for循环产生了闭包,这个闭包保留了目前自己所在的作用域链内完整的变量,在for里面的var i = 0这个变量声明是在全局里声明的(因为var不做用于块级作用域),我们执行每一个arr内部的函数的时候,这个函数会沿着自己的作用域链寻找i,就视图上看,从子域一步一步向外寻找i,直到var i = 0这里(即在全局声明的变量i),找到了i,可是这个i是在全局里声明的,那么在第十次循环的时候,我们已经令i = 10了,所以打印出来10个10;
那我们用let解决的话,其实可以理解成我们在块级作用域的每一次循环里添加了这一步
let i = i;//看上面位置复制代码
由于是let,所以我们在每一个块里都声明了i,在访问的时候,每一个arr内的元素沿着自己的作用域链访问到自己的块作用域是就会拿到i,而不像var在最后全局才能拿到。
此外啊,为什么说闭包保存的是整条作用域链里的变量,而不是this呢,再举个栗子
var name = 'window';
var obj = {
name: 123,
getName: function () {
//var that = this
return function () {
//return that.name;
return this.name;
}
}
}
console.log(obj.getName()())复制代码
这里打印的是window,因为虽然产生闭包,但是this不会沿着所谓的作用域链老老实实的找,而是由于这个匿名函数是在全局被调用的,所以this指向的就是window对象,解决方法就是用一个that代替this,如上注释
解构赋值
两边模式一样的话,就会相应的进行匹配,比如我们在获取后台的json数据的时候,我们一点一点的获取,赋值等等操作是一件很麻烦的事,所以解构赋值为我们提供了方便,我们直接拿到后台传过来的json数据的格式,然后解构赋值就可以了
let [a, b, c, [d, f]] = [1, 2, 3, [1, 2]];复制代码
几种结构失败或者报错方式有以下几种
let [a, b, c, d] = [1, 2, 3]//d是undefined
复制代码
let [a, b, c, d] = [1, 2, 3, 4, 5]//5赋值不进去复制代码
let [a, b, c, d] = 1;//或者undefined,null,{}等,会报错,这是迭代器iterator的问题
//Uncaught TypeError: {} is not iterable
复制代码
对象不行是因为解构赋值这里其实是一个for of循环,对象不支持,以后会详细说明iterator
默认赋值
let [a = 1, b = 2, c = 3] = [];复制代码
如果严格a b c 严格等于("===")undefined,那么就会触发默认赋值
惰性求值
let fn = function () {
return 10
};
let [a = fn(), b = 2, c = 3] = [];复制代码
赋值顺序
解构赋值从右向左,如
var [a = 1, b = a] = [];console.log(a,b) // 1 1
var [a = b, b = 1] = [];console.log(a,b) // undefined 1复制代码
第二种情况,如果是let声明,记得let没有变量提升的作用,会报错a is not defined
对象的解构赋值
var {name, age, sex} = {
name: 'name',
age: '18',
sex: 'male'
}复制代码
与数组情况类似
给属性值模式匹配
var {name: aName} = {
name: "name"
}
console.log(name)//undefined
console.log(aName)//name复制代码
字符串的结构赋值
var {length} = "hello" console.log(length)// 5
把hello转化成类数组,然后进行匹配复制代码
var {toString} = "hello" //先变成类数组,再转化成对象
console.log(toString)//ƒ toString() { [native code] }复制代码
数值和布尔值得解构赋值 如果等号左边是数值或是布尔值,则会先转为对象
let {toString: a} = 123;
let {toString: b} = true;
console.log( a === Number.prototype.toString);// true
console.log( b === Boolean.prototype.toString);// true
// undefined 和 null 不能转成对象所以会出错
//let {toString: x } = undefined;
//let {toString: y } = null
复制代码
几个小题
function move({x = 0,y = 0} = {}) {
console.log([x,y]);
}
move({x:3,y:8}); // 3 8
move({x:3});// 3 0
move({});// 0 0
move();// 0 0复制代码
function deal({x,y} = {x: 0 , y: 0}) {
console.log([x,y]);
}
deal({x: 3,y: 8}); // 3 8
deal({x: 3}); // 3 undefined
deal({}); // undefined undefined
deal(); // 0 0复制代码
变量交换
var x = 1;
var y = 2;
var [x, y] = [y, x];复制代码
从函数返回多个值
function example () {
return [1,2,3];
}
var [a, b, c] = example(); 复制代码
函数参数的定义
function f2({x,y,z}) {
console.log(x,y,z);
}
f2({z:3,x:2,y:1});复制代码
4.提交json数据
var jsonData= {
id: 42,
status: 'ok',
data:[888,999]
}
let {id,status,data:number} = jsonData;
console.log(id, status,number);复制代码
数组的扩展
Array.from()
把arguments,DOM等类数组转换成数组,并返还一个新数组,但这是一个浅拷贝,比如
var arr = [1, 2, [1, 2]];
var arr1 = Array.from(arr);
//let arr1 = Array.prototype.slice.call(arrayLike); 也是转化类数组为数组
arr[2].push(3);
console.log(arr1)复制代码
var arr1 = Array.from(arr, function (item) {
return item + "a";
})
//类似于map方法复制代码
Array.of()
把一组数转化成数组,用于处理new Array(10)的问题