一: let和const
let: let用法类似var,但是所声明的变量,只在let
命令所在的代码块内有效。对于for循环,有两个作用域,设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域,var变量在声明之前是undefined,let变量在声明之前是ReferenceError
// var 的情况
console.log(foo); // 输出undefined
var foo = 2;
// let 的情况
console.log(bar); // 报错ReferenceError
let bar = 2;
const: const声明一个只读的常量。一旦声明,常量的值就不能改变。
顶层对象: 在浏览器环境指的是window
对象,在 Node 指的是global
对象。ES5 之中,顶层对象的属性与全局变量是等价的。但是let,const,class声明的变量不属于顶层对象的属性
var a = 1;
// 如果在 Node 的 REPL 环境,可以写成 global.a
// 或者采用通用方法,写成 this.a
window.a // 1
let b = 1;
window.b // undefined
数组结构: let [a, b, c] = [1, 2, 3];
默认值: 解构赋值允许指定默认值,不论是数组还是对象
// 但是只有传值为undefined的时候才会给默认值,为null的时候都会给定null
let [foo = true] = [];
foo // true
let [x, y = 'b'] = ['a']; // x='a', y='b'
let [x, y = 'b'] = ['a', undefined]; // x='a', y='b'
惰性求值: 如果默认值是一个表达式,那么这个表达式是惰性求值的,即只有在用到的时候,才会求值。
对象的解构赋值: 与数组解构不同的是数组结构依靠的是下标来给对应的位置赋值的,对象则是靠属性名对应来赋值的
变量名与属性名不一致:
// 可以把foo变量改名为baz,foo其实是个中间过度变量,所以在外面是取不到foo的
let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
baz // "aaa"
let obj = { first: 'hello', last: 'world' };
let { first: f, last: l } = obj;
f // 'hello'
l // 'world'
// 假如给赋值的时候本身就有这个变量,那么可以直接取值,例如下面obj对象有p属性,那么在赋值的时候,可以直接取p: let {p} = obj, 下面赋值的时候第一个p是要被赋值的变量,而第二个p是中间过度的变量,目的是为了下面的x和y能够取到obj下面的p的属性
let obj = {
p: [
'Hello',
{ y: 'World' }
]
};
let { p, p: [x, { y }] } = obj;
x // "Hello"
y // "World"
p // ["Hello", {y: "World"}]
padStart(),padEnd()的作用:提示字符串格式:'09-12'.padStart(10, 'YYYY-MM-DD') // "YYYY-09-12"
判断是否是数字: Number.isFinite(0.8); // true,其他任何类型的数据都是false,是否是整数:Number.isInteger()
箭头函数没有this的问题: 由于没有this,所以call, apply, bind几个函数改变this指向的作用就对它无效
// 因为不能改变箭头函数的指向,所以id其实一直是最上层foo的id,所以一直是1
function foo() {
return () => {
return () => {
return () => {
console.log('id:', this.id);
};
};
};
}
var f = foo.call({id: 1});
var t1 = f.call({id: 2})()(); // id: 1
var t2 = f().call({id: 3})(); // id: 1
var t3 = f()().call({id: 4}); // id: 1
双冒号运算符: 函数绑定运算符是并排的两个冒号(::
),双冒号左边是一个对象,右边是一个函数。该运算符会自动将左边的对象,作为上下文环境(即this
对象),绑定到右边的函数上面。
foo::bar;
// 等同于
bar.bind(foo);
foo::bar(...arguments);
// 等同于
bar.apply(foo, arguments);
const hasOwnProperty = Object.prototype.hasOwnProperty;
function hasOwn(obj, key) {
return obj::hasOwnProperty(key);
}
尾递归: 正常递归可能会出现栈溢出的情况,尾递归可以避免这种情况,区别就是把想要的值也放在参数里面,在最后将这个参数返回
// 正常递归
function factorial(n) {
if (n === 1) return 1;
return n * factorial(n - 1);
}
// 尾递归
function factorial(n, total) {
if (n === 1) return total;
return factorial(n - 1, n * total);
}
获得对象的属性的描述:Object.getOwnPropertyDescriptor
let obj = { foo: 123 };
Object.getOwnPropertyDescriptor(obj, 'foo')
// {
// value: 123,
// writable: true,
// enumerable: true,
// configurable: true
//
目前,有四个操作会忽略enumerable
为false
的属性。
for...in
循环:只遍历对象自身的和继承的可枚举的属性。Object.keys()
:返回对象自身的所有可枚举的属性的键名。JSON.stringify()
:只串行化对象自身的可枚举的属性。Object.assign()
: 忽略enumerable
为false
的属性,只拷贝对象自身的可枚举的属性。
这就是为什么toString等原型方法是不会被遍历出来的
属性的遍历 :
for...in:for...in
循环遍历对象自身的和继承的可枚举属性(不含 Symbol 属性)。
Object.keys(obj): Object.keys
返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含 Symbol 属性)的键名。
Object.getOwnPropertyNames(obj):Object.getOwnPropertyNames
返回一个数组,包含对象自身的所有属性(不含 Symbol 属性,但是包括不可枚举属性)的键名。
Object.getOwnPropertySymbols(obj): Object.getOwnPropertySymbols
返回一个数组,包含对象自身的所有 Symbol 属性的键名。
Reflect.ownKeys(obj): Reflect.ownKeys
返回一个数组,包含对象自身的所有键名,不管键名是 Symbol 或字符串,也不管是否可枚举。
Object.getOwnPropertyDescriptors: 获得对象的书友属性的描述对象,Object.getOwnPropertyDescriptor是获得其中一个
Object.defineProperty()和Object.defineProperties(): 该方法直接在一个对象上一定一个或多个新的属性或是修改现有属性,并返回该对象,前者是只能修改一个后者可以修改一个或多个
注:Object.getOwnPropertyDescriptors
方法配合Object.defineProperties方法可以实现对set和get属性的拷贝
assign的简写:
let aClone = { ...a };
// 等同于
let aClone = Object.assign({}, a);
总结:
给定默认值: 在es6之前先要给定默认值需要用到||运算,例:y = y || ‘woshiY’ ,这种方法的缺陷在于要多写一行,然后||运算在判断左边代码是否等于false的时候会将空字符串或者Boolean类型的false算进去。为了避免这种情况又只能判断类型es6中的给函数参数给定默认值:function xxx(x, y = 'woshiY'){};函数结构默认值:function xxx(x, {y =‘’, z = ‘hehe’} = {}),当调用该方法时只传第一个参数的话,那么第二个参数的默认值会生效,这个时候第二个参数就会去解构赋值
属性的简洁表示法: function f (x, y) {return {x, y}}等同于: function f (x, y) {return {x: x, y: y}},方法也可以简写(get方法和set方法其实就是简写):
const o = {
method() {
return "Hello!";
}
};
// 等同于
const o = {
method: function() {
return "Hello!";
}
};
...运算符的使用:
(1)函数中使用... : 将函数的参数集合成一个数组里面,此例中将传过来3个参数放在values数组中,此外在函数中获取参数可以用arguments获取
function add(...values) {
let sum = 0;
for (var val of values) {
sum += val;
}
return sum;
}
add(2, 5, 3) // 10
(2)数组中使用...: 他可以理解成函数中使用的反向操作,将数组变成分开的变量。
应用场景:
复制: const a2 = [... a1],将a1赋值给a2
合并数组: [...arr1, ...arr2, ...arr3] 将arr1、arr2、arr3合并成一个
变成对象: {...Array}