字面量增强的写法(Enhanced object literals)
- 属性的简写:Property Shorthand
- 方法的简写:Method Shorthand
- 计算属性名:Computed Property Names
var name = "why"
var age = 18
// 1.property shorthand(属性的简写) 普通写法
var obj = {
name: name
age: age
}
// 增强写法
var obj = {
name,
age
}
// 2.method shorthand(方法的简写) 普通写法
var obj = {
foo: function() {}
}
// 增强写法
var obj = {
foo() {},
}
//和这种写法不一样 这种写法指的是使用箭头函数
baz: () => {
console.log(this) //windows
},
// 3.computed property name(计算属性名)
var name = "test";
var obj = {}
obj[name] = "123"; //如果key是一个变量 不能用.的方式 要使用[]
// 增强写法
var obj = {
[name]: "123"
}
解构(Destructuring)
数组的解构
var names = ["abc", "cba", "nba"]
// var item1 = names[0]
// var item2 = names[1]
// var item3 = names[2]
// 对数组的解构: []
var [item1, item2, item3] = names
console.log(item1, item2, item3)
// 解构后面的元素
var [, , itemz] = names
console.log(itemz)
// 解构出一个元素,后面的元素放到一个新数组中
var [itemx, ...newNames] = names
console.log(itemx, newNames)
// 解构的默认值
var [itema, itemb, itemc, itemd = "aaa"] = names
console.log(itemd)
// 解构出后面两个元素
var [, item2, item3] = names
console.log(item4)
var ages = [1, 2, 3, 4, 5];
// 合并两个数组
var arr = [...names, ...ages]
对象的解构
var obj = {
name: "why",
age: 18,
height: 1.88
}
// 对象的解构: {}
var { name, age, height } = obj
console.log(name, age, height)
var { age } = obj
console.log(age)
var { name: newName } = obj
console.log(newName)
var { address: newAddress = "广州市" } = obj
console.log(newAddress)
function foo(info) {
console.log(info.name, info.age)
}
foo(obj)
function bar({name, age}) {
console.log(name, age)
}
bar(obj)
// // 解构出某个key 并且重新命名 var { name, age: newAge, height } = obj;
// 不用delete 如何删除 对象的某个属性delete obj.a
var { name, ...rest } = obj // 删除了name , obj = rest;
// 合并两个对象 相同的属性会被覆盖 var obj2 = {...obj, ...obj1}
var/let/const的基本使用
var
- 没有块级作用域,是弱类型,支持变量提升。
- 可以重复声明,没有报错和警告
- ES5中只有全局作用域和函数作用域,大部分人会采用闭包来解决ES5的问题
let
- 只有块级作用域 window无法访问
- 不存在变量提升(解析阶段会被创建出来,但是不能被访问)
> Reference(引用)Error: Cannot access 'foo' before initialization(初始化)
> let/const他们是没有作用域提升
> foo被创建出来了, 但是不能被访问 作用域提升: 能提前被访问
>
- 存在暂时性死区(TDZ)
```
//"暂时性死区"也意味着**typeof**不再是一个百分之百安全的操作。
//在没有**let**之前,**typeof**是百分百安全的,现在这一点不成立了.
typeof x; //ReferenceError: a is not defined.
let x;
```
- 不允许重复声明
const
- 只有块级作用域 window无法访问
- 不存在变量提升(解析阶段会被创建出来,但是不能被访问)
- 存在暂时性死区(TDZ)
- 不允许重复声明
- 声明时必须赋值,且后续不允许修改,如果是对象,对象的属性可以被修改。const本质上是传递的值不可以修改 但是如果传递的是一个引用类型(内存地址), 可以通过引用找到对应的对象, 去修改对象内部的属性,
- 实际开发如何选择
建议优先使用const,尤其是在全局环境,不应该设置变量,只应设置常量。 一个是const可以提醒阅读程序的人,这个变量不应该改变;另一个是const比较符合函数式编程思想,运 算不改变值,只是新建值,而且这样也有利于将来的分布式运算;最后一个原因是 JavaScript 编译器会对>const进行优化,所以多使用const,有利于提高程序的运行效率,也就是说let和const的本质区>别,其实是编译器内部的处理不同.
let-const和window关系
变量被保存到VariableMap中,比如v8中其实是通过VariableMap的一个hashmap来实现它们的存储的。
那么window对象呢?而window对象是早期的GO对象,在最新的实现中其实是浏览器添加的全局对象,并且 一直保持了window和var之间值的相等性;现在window是在浏览器实现,v8不实现
// var foo = "foo"
// var message = "Hello World"
// console.log(window.foo)
// console.log(window.message)
// window.message = "哈哈哈"
// console.log(message)
let foo = "foo"
作用域
在ES6之前,只存在全局作用域和函数作用域。
var text = "全局作用域";
function foo() { var bar = "函数作用域,只有在这个函数才能访问" }
ES6的时候有了块级作用域
{
var x = 123;
let y = 456;
const z = 789;
}
// 只有x 能正常被打印 console.log(x,y,z);
// 对let/const/function/class声明的类型是有效
{
let foo = "why"
function demo() {
console.log("demo function")
}
class Person {}
}
// console.log(foo) // foo is not defined
// 不同的浏览器有不同实现的(大部分浏览器为了兼容以前的代码, 让function是没有块级作用域)
// demo()
var p = new Person() // Person is not defined
if-switch-for块级代码
// if语句的代码就是块级作用域
// if (true) {
// var foo = "foo"
// let bar = "bar"
// }
// console.log(foo)
// console.log(bar)
// switch语句的代码也是块级作用域
// var color = "red"
// switch (color) {
// case "red":
// var foo = "foo"
// let bar = "bar"
// }
// console.log(foo)
// console.log(bar)
// for语句的代码也是块级作用域
// for (var i = 0; i < 10; i++) {
// // console.log("Hello World" + i)
// }
// console.log(i)
for (let i = 0; i < 10; i++) {
}
console.log(i)
块级作用域的应用场景
const btns = document.getElementsByTagName('button')
// for (var i = 0; i < btns.length; i++) {
// (function(n) {
// btns[i].onclick = function() {
// console.log("第" + n + "个按钮被点击")
// }
// })(i)
// }
// console.log(i)
for (let i = 0; i < btns.length; i++) {
btns[i].onclick = function() {
console.log("第" + i + "个按钮被点击")
}
}
// console.log(i)
块级作用域的补充
const names = ["abc", "cba", "nba"]
// 不可以使用const
// for (let i = 0; i < names.length; i++) {
// console.log(names[i])
// }
// {
// let i = 0
// console.log(names[i])
// }
// {
// let i = 1
// console.log(names[i])
// }
// {
// let i = 2
// console.log(names[i])
// }
// for...of: ES6新增的遍历数组(对象)
for (const item of names) {
console.log(item)
}
// {
// const item = "abc"
// console.log(item)
// }
// {
// const item = "cba"
// console.log(item)
// }
// console.log(item)
暂时性死区
在ES6中,我们还有一个概念称之为暂时性死区: p它表达的意思是在一个代码中,使用let、const声明的变量,在声明之前,变量都是不可以访问的; p我们将这种现象称之为 temporal dead zone(暂时性死区,TDZ)
var foo='foo'
if(true){
console.log(foo)
let foo ="bar"
}
//函数暂时性死区
function bar(){
console.log(boo)
let boo='123'
}
var、let、const的选择
那么在开发中,我们到底应该选择使用哪一种方式来定义我们的变量呢? 对于var的使用:
我们需要明白一个事实,var所表现出来的特殊性:比如作用域提升、window全局对象、没有块级作用域等都是一些 历史遗留问题;
其实是JavaScript在设计之初的一种语言缺陷;
当然目前市场上也在利用这种缺陷出一系列的面试题,来考察大家对JavaScript语言本身以及底层的理解;
但是在实际工作中,我们可以使用最新的规范来编写,也就是不再使用var来定义变量了;
对于let和const来说,是目前开发中推荐使用的;
我们会优先推荐使用const,这样可以保证数据的安全性不会被随意的篡改;
只有当我们明确知道一个变量后续会需要被重新赋值时,这个时候再使用let;
这种在很多其他语言里面也都是一种约定俗成的规范,尽量我们也遵守这种规范