let 和 const
//之前的方式
var flag = 123
console.log(flag)
//但是如果使用下面的方式
console.log(flag)//输出undefined
var flag = 123
因为浏览器在执行js语法的时候存在预解析,变量的声明会被提升,但是赋值不会被提升,所以这段代码在计算机看来其实是这样的
var flag
console.log(flag)
flag = 123
因为js是单线程处理的,很明显在执行到输出flag的时候,flag 并没有被人为赋值,所以它的值是默认的undefined
let 的特点介绍
-
let 声明的变量不存在预解析
console.log(flag)//直接报错,告知变量没有被定义
let flag = 123 -
let声明的变量不允许在同一个作用域重复声明
//之前
var flag = 123
var flag = 456
console.log(flag) //456
//let
var flag = 123
var flag = 456
console.log(flag) //flag has already been defined
首先来解释一个概念 es6新增的块级作用域
if(true){
var flag = 123
}
console.log(flag) //123
if(true){
let flag = 123
}
console.log(flag) //flag没有定义
块级作用域内部的let 外部是无法访问的
那么就有人问了: 如何形成一个块级作用域?
- 一个花括号也是块级作用域
{
let flag = 123
}
- 一个普通的if else代码块也是块级作用域
if (true) {
let flag = 123
}
- 一个普通的for循环也形成了一个块级作用域
for (let index = 0; index < array.length; index++) {
const element = array[index];
}
let在块级作用域中的特点
- 在块级作用域内部 let变量依然只能先声明后使用
在一个作用域内 let变量定义之前的代码区域叫做暂时性死区.这之前是不允许访问let定义的变量的,因为它没有声明提升
const的特点
- const 用来声明常量
- const 声明的常量不允许重复赋值
- const 常量必须在定义的时候进行赋值,否则会报错
const n = 1
//正确
const abc
//报错,没有进行常量的初始化
const abc = 1
abc = 2
//报错,abc是常量,不允许进行赋值操作
变量的解构赋值
数组的解构赋值
var a = 1
var b = 2
var c = 3
var a = 1, b = 2, c = 3
//以上这俩种方法都是一样的
es6解构赋值
var [a,b,c] = [1, 2, 3]
console.log(a,b,c) //1 2 3
以上这种赋值法叫做数组的赋值法
也可以空着不写,逗号隔开就可以
let [d, e, f] = [, 123, ]
console.log(d,e,f) // undefined 123 undefined
在定义的时候如下写法,可以给它赋默认值
let [d=111, e, f] = [, 123, ]//默认值
console.log(d,e,f) // 111 123 undefined
对象的解构赋值
let {foo, bar} = {foo : 'hello', bar : 'hi'}
根据变量的名称将相应的值赋给前方,因为变量名称固定所以后面赋值部分的顺序对值没有影响
let {foo, bar} = { bar : 'hi', foo : 'hello'}
这种写法和上面完全等价
- 对象属性别名
let {foo : abc, bar} = {bar : 'hi', foo : "hello"}
console.log(abc,bar)//hello hi
console.log(foo,bar)//foo未定义
//如此书写会改变, 对象属性的名字,之前的名字会被直接覆盖
//对象解构赋值还能这样用
let {cos, sin, random} = Math
console.log(typeof cos) //function
console.log(typeof sin) //function
console.log(typeof random) //function
这样写无需每次使用 math.
,直接取出 Math对象 中同名的方法和属性,使用时无需再加Math前缀
- 对象解构赋值的默认值
let {foo : abc = "oh", bar} = {bar : 'hi'}
console.log(abc, bar)// oh hi
//字符串的解构赋值
let [a, b, c, d, e] = 'hello'
console.log(a, b , c, d, e)//h e l l o
它会把字符串拆开 赋值给前方的变量
如果想获得字符串的长度,可以使用字符串对象中的length ,这样就需要用到对象方法,取出其length
let{length} = 'hello'
console.log(length)// 5
字符串相关扩展
新增字符串方法
主要有这三个新增方法
-
includes()
-
startWith()
-
endWith()
includes()
判断字符串中是否包含指定的子串
第一个参数为目标子串,第二个参数为开始找寻的索引
console.log('helloWorld'.includes('World',2))// true
//从index = 2 的位置开始匹配是否有目标子串
startWith()
判断字符串是否以特定子串开始
let url = 'admin/index.php'
console.log(url.startsWith('admin'))//true
endWith()
判断字符串是否以特定结尾结束
console.log(url.endsWith('.php'))//true
模板字符串
let obj = {
username : 'list',
age : 12,
gender: 'male'
}
let {username, age, gender} = obj
let tag = '<div><span>' + username + '</span><span>' + age + '</span><span>' + gender + '</span></div>'
console.log('');
以上是传统的做法,这样书写html代码可维护性很差
这里是模板字符串的做法,使用反引号表示模板字符串
let obj = {
username : 'list',
age : 12,
gender: 'male'
}
let tag = `
<div>
<span>${obj.username}</span>
<span>${obj.age}</span>
<span>${obj.gender}</span>
<span>${1 + 3}</span>
<span>${fn('nihao')}</span>
</div>
`
这样就可以直接把内容加入到html语法中, 格式清晰,和nodejs中的art-template插件的填充数据方式很类似
函数扩展
包括以下内容
- 参数默认值
- 参数解构赋值
- rest参数
- 扩展运算符
参数默认值
- es6之前默认值方法
function foo(params) {
var p = params || '默认'
console.log(p)
}
这样就可以让默认值为||之后的内容
- es6
function foo(params = '默认') {
var p = params
console.log(p)
}
这很类似于python的方法
参数的解构赋值
function ([uname = 'list', age = 12]) {
console.log(uname, age)
}
foo()
像这样 , 传递一个数组进入 , 函数 , 会自动进行解构赋值
function ({uname = 'list', age = 12}) {
console.log(uname, age)
}
foo()
但是这样写有个地方,如果不需要传参,()内不写一个空对象的话会报错.
可以这样解决
function ({uname = 'list', age = 12} = {}) {
console.log(uname, age)
}
在定义函数的时候,在后面默认让其等于空对象,这样在函数不需要传参的时候也不用手动去加空对象
rest参数
// 用来接收多余的参数
function foo(a, b, ...param){
console.log(a)
console.log(b)
console.log(param)
}
foo(1, 2, 3, 4, 5)
/*
1
2
[3, 4, 5]
*/
后面的参数就会被放入数组中
扩展运算符 …
作用是拆散数组拿出其中的元素
之前的方法
function foo(a, b, c, d, e){
console.log(a + b + c + d + e)
}
//如果传递的数据是数组
let arr = [1, 2, 3, 4, 5]
foo.apply(null, arr)
//es6方法
foo(...arr)
这样arr
会被 ...
运算符自动打散进入函数
...
和 rest
参数是互逆的
- 扩展运算符的运用,合并数组
let arr1 = [1, 2, 3]
let arr2 = [4, 5, 6]
let arr3 = [...arr1, ...arr2]
console.log(arr3) // [1, 2, 3, 4, 5, 6]
箭头函数
形式介绍
之前的函数调用方法
function foo(){
console.log('hello')
}
foo()
es6箭头函数
let foo = () => {
console.log('hello')
}
foo()
传参
传统方法
function foo(value) {
return value * 10
}
let ret = foo(10)
console.log(res) // 100
es6方法
let foo = value => value * 10
let ret = foo(10)
console.log(res) // 100
注意,如果参数为单个,箭头函数可以不用小括号,否则必须加上
传递多个参数
let foo = (a, b) => console.log(a + b)
foo(1, 3) //4
注意,如果函数代码块只有一句,可以不用 { } , 否则必须加上
let arr = [123, 456, 789]
arr.forEach(function (element, index) {
console.log(element, index)
})
箭头函数的方法
arr.forEach((element, index) => {
console.log(element, index)
})//可以看到这种方法更加简洁
箭头函数的注意事项
- 箭头函数中的this 取决于函数的定义,而不是调用(之前的this是取决于函数调用的)
function foo(){
console.log(this)
}
foo()
这样在函数调用之时 this 指向 window
欲改变this指向使用call
foo.call({num: 1})
//这样指向对象就变为{num: 1}
function foo(){
setTimeout( () => {
console.log(this)
}, 100)
}
foo.call({num : 1})//结果为 1
前面说过,箭头函数中的this
,取决于函数的定义
具象来说,就是看箭头函数的位置,箭头函数定义之时所处的位置决定了箭头函数的this
上面的代码要这样来解释
在调用foo()
的时候,foo的this指向被改为{num : 1}
所以在foo()
内部,this的指向是{num : 1}
因为箭头函数定义在foo()
内部,它的this
和定义位置有关
箭头函数的this就是foo()
的this
所以箭头函数的this指向{num : 1}
- 箭头函数不可以 new
let foo = () => {this.num = 123}
var a = new foo()
报错,foo()不是构造函数,箭头函数不能new
- 箭头函数不可以使用arguments 获取参数列表,只能使用…rest参数代替
let foo = (a, b) => {
console.log(a, b)
console.log(arguments) //输出内容是一个对象,而不是实参列表,和上面不同
}
let foo = (...param) => {
console.log(param[0], param[1])
}
这样 就可以类似实参列表的方式去获取参数