目录
ES6基础入门之let、const
变量声明
var声明
var name='hiyori'
直接使用
window.location.href===location.href
说明:这种不加var声明的定义方式,相当于在全局对象window上挂了一个属性,会造成全局对象污染 ,不建议这么做
let和var的主要区别
- let声明的变量只在当前(块级)作用域内有效
- let声明的变量不能被重复声明
- let不存在变量提升
ES6之前的作用域
全局作用域、函数作用域、eval作用域
块级作用域
- 通俗的讲,就是一堆花括号中的区域{...}
- 块级作用域可以嵌套
/* let声明的变量只在当前(块级)作用域内有效 */
//example1
{
var a = 1;
let b = 2;
}
console.log(a); //打印a
console.log(b); //出了块级作用域,报错信息为未定义
//example2
{
let a = 1;
{
console.log(a); //这里属于a的作用范围,打印1
let b = 2;
}
console.log(b); //这里不属于b的作用范围,报错
}
//example3
for (let i = 0; i < 3; i++) {
};
console.log(i); //报错
for (var i = 0; i < 3; i++) {
console.log(i);
};
console.log(i); //打印3,var定义的i并不会被释放,因为初始化时定义的i相当于是在循环语句的作用域之外定义的
/* 使用let或者const声明的变量不能被重新声明 */
var dad = '我是爸爸!';
var dad;
console.log(dad); //重新声明,没有被赋值,该变量还是'我是爸爸'
var dad = '我才是爸爸!';
console.log(dad); //修改为新的值
let son = '我是儿子';
let son; //报错,提示son已被声明过
let son = '我才是儿子'; //报错,提示son已被声明过
/* let不存在变量提升 */
console.log(dad); //undefined
var dad='我是爸爸';
//以上情形就是因为变量提升
//预解析时会先解析带有var关键字的变量,并赋值为undefined,执行代码时才赋真正的值
//相当于
var dad; //变量声明被拉到最前面,而后赋值
console.log(dad);
dad='我是爸爸';
console.log(dad); //报错,提示未定义该变量
let dad='我是爸爸';
//用let定义的变量不会把声明拉到最前面,因此不具有变量提升
//暂存死区
var monkey = '我是美猴王';
{
console.log(monkey); //我是..
var monkey = '我觉得我还能再抢救一下!';
}
console.log(monkey); //我觉得..
let monkey = '我是美猴王';
{
console.log(monkey); //报错
let monkey = '我觉得我还能再抢救一下!';
}
console.log(monkey);
let面试常见例子
q: 生成十个按钮 每个按点击的时候弹出1 - 10
var i = 0;
for (i = 1; i <= 10; i ++) {
(function(i) {
var btn = document.createElement('button');
btn.innerText = i;
btn.onclick = function() {
alert(i)
};
document.body.appendChild(btn);
})(i);
}
//在外面加上自执行匿名函数,就会形成独立的函数作用域,i作为参数传进来,在作用域中获得的就是实时更新的i
//如果没有定义一个自执行的function,那么这里每次点击弹出的都是11
//因为发生点击事件后要去寻找i,本层找不到就会到上一层去找,上一层的i在循环结束后值为11
//因此事件中获取到的i的值也是11
//使用let定义,可以实现和添加自执行函数一样的效果
for (let i = 1; i <= 10; i ++) {
var btn = document.createElement('button');
btn.innerText = i;
btn.onclick = function() {
alert(i)
};
document.body.appendChild(btn);
}
const
常量——不可改变的量
和声明变量一样,基本只是关键字的区别
常量必须在声明的时候赋值,否则报错:Missing initiaizer in const declaration
const a; //不赋值,会报错
var b=2;
let c;
const与let类似的特性
不能重复声明
不存在变量提升
只在当前(块级)作用域内有效
常量不可变
一旦声明常量,就不能再改变
但常量为引用类型的时候,不能保证不可变
const NAME='xh';
NAME='xm'; //报错,提示常量不能被修改
说明:const只能保证引用常量的地址不变,不能保证它里面的值不变。
//对象
const xiaoming={
age:14;
name:'xm';
}
xiaoming.age=22; //允许修改
xiaoming={}; //报错,不允许被修改,因为相当于把一个新的地址给了xiaoming这个常引用对象
//数组
const ARR=[];
ARR.push(1); //允许添加
ARR=[]; //报错,不允许被修改,还是相当于把一个新的地址给了ARR这个常引用数组
怎么解决引用类型的常量可以被修改的问题?
Object.freeze()
const xiaoming = {
age: 14,
name: '小明'
};
Object.freeze(xiaoming); //xiaoming对象中的值也不允许被修改了
console.log(xiaoming);
xiaoming.age = 22; //不报错,但不会发生改变
xiaoming.dd = 11; //不报错,但不会发生改变
console.log(xiaoming);
const ARR = [];
Object.freeze(ARR); //ARR数组中的值也不允许被修改了
ARR.push(1); //报错,提示不能被扩展
console.log(ARR);
ES6之前怎么声明常量?
用var声明,假装是常量
var BASE_COLOR = '#ff0000';
Object.defineProperty();
var CST = {a: 1};
Object.defineProperty(CST, 'a', {
writable: false
});
//writable:只读,不能被修改
Object.seal(CST); //只能防止被扩展
//Object.seal+writable:false才能达到freeze()的效果
自行封装一个freeze()
// 1. 遍历属性和方法
// 2. 修改遍历到的属性的描述
// 3. Object.seal()
Object.defineProperty(Object, 'freezePolyfill', {
value: function(obj) {
var i;
for (i in obj) {
if (obj.hasOwnProperty(i)) {
Object.defineProperty(obj, i, {
writable: false //每个属性都不能被修改
});
}
}
Object.seal(obj); //整个对象不能被扩展
}
});
const xiaoming = {
age: 14,
name: '小明',
obj: {
a: 1
}
};
Object.freezePolyfill(xiaoming);
ES6变量的解构赋值
解构赋值语法是一个JavaScript表达式,这使得可以将值从数组或属性从对象提取到不同的变量中
数组的解构赋值
数组的解构赋值
// 数组的解构赋值
const arr = [1, 2, 3, 4];
let [a, b, c, d] = arr;
//会根据元素在数组中所在的位置一一对应匹配,a=1,b=2……
更复杂的匹配规则
// 更复杂的匹配规则
const arr = ['a', 'b', ['c', 'd', ['e', 'f', 'g']]];
const [ , b] = arr; //只返回b
const [ , , g] = ['e', 'f', 'g'] //只返回g
const [ , , [ , , g]] = ['c', 'd', ['e', 'f', 'g']]; //只返回g
const [ , , [ , , [ , , g]]] = arr; //只返回g
扩展运算符 ...
// 扩展运算符 ...
const arr1 = [1, 2, 3];
const arr2 = ['a', 'b'];
const arr3 = ['zz', 1];
const arr4 = [...arr1, ...arr2, ...arr3]; //可以把arr1、arr2、arr3合并起来
const arr = [1, 2, 3, 4, 5, 6];
const [a, b, ...c] = arr; //会把3,4,5,6作为一个数组赋给c
const [a, b, ...c,d] = arr; //错误
注意:带扩展运算符的变量后面不能再有其他变量,因为扩展运算符就是用来取最后几个元素进行合并的
默认值
// 默认值
const arr = [1, null, undefined];
const [a, b = 2, c, d = 'aaa'] = arr; //设置默认值,当匹配到undefined时,就使用默认值
//a=1,b=null,c=undefined,d='aaa'
说明:解构赋值时没有匹配到的就被赋值为undefined
交换变量
// 交换变量
let a = 20;
let b = 10;
//方法①
let temp;
temp = a;
a = b;
b = temp;
//方法②,利用解构赋值
[a, b] = [b, a];
接收多个 函数返回值
// 接收多个 函数返回值
function getUserInfo(id) {
// .. ajax
return [
true,
{
name: '小明',
gender: '女',
id: id
},
'请求成功'
];
};
const [status, data, msg] = getUserInfo(123);
//status匹配到函数返回值第一项布尔值,data匹配到函数返回值第二项对象,msg匹配到函数返回值第三项文字
对象的解构赋值
对象的解构赋值与数组的解构赋值相似
等号左右两边都为对象结构
const {a,b}={a:1,b:2};
左边的{}中为需要赋值的变量
右边为需要解构的对象
对象解构赋值的用法
// 对象的解构赋值
const obj = {
saber: '阿尔托利亚',
archer: '卫宫'
};
const { saber, archer1 } = obj; //archer属性名不匹配,archer1的值是undefined
注意:对象的解构赋值要求不同对象的属性名要一致,才能匹配。因为数组是有序的,而对象是无序的,必须用属性名去匹配,而不是用位置去匹配。
稍微复杂的解构条件
// 稍微复杂的解构条件
const player = {
nickname: '感情的戏∫我没演技∆',
master: '东海龙王',
skill: [{
skillName: '龙吟',
mp: '100',
time: 6000
},{
skillName: '龙卷雨击',
mp: '400',
time: 3000
},{
skillName: '龙腾',
mp: '900',
time: 60000
}]
};
const { nickname } = player; //返回感情的戏∫我没演技∆
const { master } = player; //返回东海龙王
const { skill: [ skill1, { skillName }, { skillName: sklName } ] } = player;
//保持格式一致,就可以解构skill里的属性
//对数组进行赋值时变量名可以随便起,skill1获得skill数组的第一项,skillName获得数组第二项中的skillName龙卷雨击
//这时如果想要获得skill数组第三项的skillName再次使用{skillName}是不行的,因为const声明的变量中已经有skillName了
//我们需要另外起一个名字,只要在属性名后面加上:新名字,就可以解决这个问题,意思是把skillName中的值赋给sklName
const { skill } = player; //返回一个数组
const [ skill1 ] = skill; //skill是一个数组,skill1是数组元素的第一项——龙吟,100,6000
const { skill:[skill1] }=player; //与上一行完全一样,但不会取到skill
扩展运算符 ...
// 结合扩展运算符
const obj = {
saber: '阿尔托利亚',
archer: '卫宫',
lancer: '瑟坦达'
};
const { saber, ...oth } = obj; //会把除了saber以外的剩下所有项赋值给oth
const obj1 = {
archer: '卫宫',
lancer: '瑟坦达'
}
const obj = {
saber: '阿尔托利亚',
...obj1, //会把obj1所有属性合并成一个对象
};
如何对已经申明了的变量进行对象的解构赋值
// 如何对已经申明了的变量进行对象的解构赋值
let age;
const obj = {
name: '小明',
age: 22
};
({ age } = obj);
//{age}外面如果不加(),会被编译器理解成块级作用域,然后报错。
//最好的方法是带上变量声明
const obj = {
name: '小明',
age: 22
};
let {age}=obj;
默认值
// 默认值
let girlfriend = {
name: '小红',
age: undefined,
};
let { name, age = 24, hobby = ['学习'] } = girlfriend;
//name='小红',age=24
对象的解构赋值的主要用途
提取对象属性
// 提取对象属性
const { name, hobby: [ hobby1 ], hobby } = { //把hobby值赋给hobby1,再定义一个hobby就获取到对象的hobby属性值
name: '小红',
hobby: ['学习']
};
使用对象传入乱序的函数参数
function AJAX({ //传参时解构赋值,可以不按顺序赋值,如果type没有设置默认为get
url,
data,
type = 'get'
}) {
// var type = option.type || 'get';
// console.log(option);
console.log(type);
};
AJAX({
data: {a: 1},
url: '/getinfo',
});
获取多个函数返回值
// 获取多个 函数返回值
function getUserInfo(uid) {
// ...ajax
return {
status: true,
data: { name: '小红' },
msg: '请求成功'
};
};
const { status, data, msg: message } = getUserInfo(123);
字符串的解构赋值
// 字符串的结构赋值
const str = 'I am the bone of my sword'; // 我是剑骨头
const [ a, b ,c, ...oth ] = str;
//a=I,b= ,c=a,oth=m the bone of my sword,注意这里oth是一个数组,每个字符是一个数组元素
const [ ...spStr1 ] = str; //spStr1是一个数组,每个数组元素是str的一个字符
const spStr2 = str.split(''); //同上一句效果一样
const spStr3 = [ ...str ]; //同上一句效果一样
// 提取属性
const { length, split } = str;
数值与布尔值的解构赋值
// 数值与布尔值的解构赋值
const { valueOf: vo } = 1;
const { toString: ts } = false;
函数参数的解构赋值
// 函数参数的解构赋值
function swap([x, y]) {
return [y, x];
};
let arr = [1, 2];
arr = swap(arr);
//定义一个Computer的构造函数
function Computer({
cpu,
memory,
software = ['ie6'],
OS = 'windows 3.5'
}) {
console.log(cpu);
console.log(memory);
console.log(software);
console.log(OS);
};
//声明一个Computer对象
new Computer({
memory: '128G',
cpu: '80286',
OS: 'windows 10'
});