关于ES6的一些基础学习

let和var

1.作用域不同

var举例:

var a=[];
for(var i =0 ; i<10;i++){
    a[i]=function(){
        console.log(i);
    };
}
a[6]();//10  a[6]=function(){console.log(i);}  i在全局范围内有效

let:

var a=[];
for(let i=0;i<10;i++){
    a[i]=function(){
        console.log(i);
    };
}
a[6]();//6
//当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新变量,所以最后输出的是6

let不存在变量提升

//var的情况
console.log(foo);//输出undefined
//脚本开始运行时,变量foo已经存在了,但是没有值,所以会输出undefined
var foo = 2;

//let的情况
console.log(bar);//报错ReferenceError
let bar=2;

如果块级作用域内存在let命令.会绑定这个块级作用域,举例

var tmp =123;

if(true){
    tmp ='abc'//ReferenceError  后者绑定这个块级作用域
    let tmp;
}
//在代码块内,使用let命令声明变量之前,该变量都是不可用的.这在语法上,称为"暂时性死区"(temporal dead zone,简称 TDZ)

if(true){
    //TDZ开始
    tmp='abc';//ReferenceError
    console.log(tmp);ReferenceError
    
    let tmp;//TDZ结束
    console.log(tmp);//undefined
    
    tmp=123;
    console.log(tmp);//123
}

ES6一共6种声明变量的方法

var,function,let,const,import,class

  • var命令和function命令声明的全局变量,依旧是顶层对象的属性;另一方面规定,let命令,const命令,class命令声明的全局变量,不属于顶层对象的属性.也就是说,从ES6开始,全局变量将逐步与顶层对象的属性脱钩.

    var a= 1;
    //如果在Node的REPL环境,可以写成global.a,或者采用通用方法,写成this.a,window.a //1

    let b = 1;
    window.b //underfined
    //全局变量a由var命令声明,所以它是顶层对象的属性;全局变量b由let命令声明,所以它不是顶层对象的属性,返回undefined

global对象的问题

顶层对象,在各种环境里面的实现是不统一的:浏览器里面,顶层对象是window;Node里,顶层对象是global.

this变量的局限性:

-- 全局环境中,this会返回顶层对象.但是node模块和ES6模块,this返回的是当前模块.
-- 函数里面的this,如果函数不是作为对象的方法运行,而是单独作为函数运行,this会指向顶层对象.但是严格模式下.this会返回undefined.

垫片库system.global模拟global作为顶层对象,可以在所有环境拿到global:

//CommonJS写法
require('system.global/shim')();

//ES6模块的写法
import shim from 'system.global/shim';shim();
//这些代码可以保证各种环境里面,global对象都是存在的


//CommonJS 写法
var global = require('system.global')();

//ES6 模块的写法
import getGlobal from 'system.global';
const global = getGlobal();
//这些代码将顶层对象放入变量global。

解构赋值(Destructuring)

数组的解构赋值

本质上,这种写法属于"模式匹配",只要等号两边的模式相同,左边的变量就会被赋予对应的值.

let [foo, [[bar], baz]] = [1, [[2], 3]];
foo // 1
bar // 2
baz // 3

let [ , , third] = ["foo", "bar", "baz"];
third // "baz"

let [x, , y] = [1, 2, 3];
x // 1
y // 3

let [head, ...tail] = [1, 2, 3, 4];
head // 1
tail // [2, 3, 4]

let [x, y, ...z] = ['a'];
x // "a"
y // undefined
z // []

如果解构不成功,变量的值就等于undefined.

解构赋值允许指定默认值

let [foo =true] = [];
foo //true

let [x,y='b'] = ['a'];//x='a',y='b'
let [x, y = 'b'] = ['a', undefined]; // x='a', y='b'

注意,ES6内部使用严格相等运算符(===),判断一个位置是否有值,所以,如果一个数组成员不严格等于undefined,默认值是不会生效的.

let[x=1]=[undefined];
x //1

let[x=1]=[null];
x //null

对象的解构赋值

let{foo,bar}={foo:"aaa",bar:"bbb"};
foo //"aaa"
bar //"bbb"

本质
let {foo:baz}={foo:"aaa",bar:"bbb"};
baz // "aaa"
foo // error: foo is not defined 
//foo是匹配的模式,baz才是变量.真正被赋值的是变量baz,而不是模块foo.

字符串的解构赋值

const [a,b,c,d,e]='hello';
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"

类似数组的对象都有一个length属性,因此还可以对这个属性解构赋值.

let{length:len}='hello';
len //5

数值和布尔值的解构赋值

解构赋值时,如果等号右边是数值和布尔值,则会转为对象.

let{toString:s}=123;
s === Number.prototype.toString //true

let {toString:s}=true;
s === Bollean.prototype.toString //true
//因为数值和布尔值的包装对象都有toString属性,因此变量s都能取到值.

由于undefined和null无法转为对象,所以对它们进行解构赋值,都会报错.

let{prop:x}=undefined;//TypeError
let{prop:y}=null;//TypeError

函数参数的解构赋值

function add([x,y]){
    return x+y;
}

add([1,2]);//3

解构赋值的用途举例

  • (1)交换变量的值

    let x= 1;
    let y =2;

    [x,y]=[y,x];

  • (2)从函数返回多个值

  • (3)函数参数的定义

  • (4)提取json数据

  • (5)设置函数参数的默认值

  • (6)遍历Map结构

  • (7)输入模块的指定方法

函数的扩展

函数参数的默认值

function log(x,y='World'){
    console.log(x,y);
}

log('Hello') //Hello World
log('Hello','China')//Hello China
log('Hello','')//Hello

rest参数

ES6引入rest参数(形式为…变量名),用于获取函数的多余参数,这样就不需要使用arguments对象了.rest参数搭配的变量是一个数组,该变量将多余的参数放入数组中.

function add(...values){
    let sum = 0;
    for (var val of values){
        sum+=val;
    }
    return sum;
}
add(2,5,3)//10

rest参数之后不能再有其他参数(即只能是最后一个参数),否则会报错.

函数的length属性不包括rest参数,不包括有默认值的参数,也不包括有默认值参数后面的参数

箭头函数

const full = ({first,last}) => first + '' + last;
const isEven = n => n%2 ===0;
const square = n => n*n;
[1,2,3].map(x=>x*x);

箭头函数使用注意点:

(1)函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象.
**this对象的指向是可变的,但是在箭头函数中,它是固定的.**
(2)不可以当做构造函数,也就是说不可以使用new命令,否则会抛出一个错误.
(3)不可以使用arguments对象,该对象在函数体内不存在.如果要用,可以用rest参数代替.
(4)不可以用yield命令,因此箭头函数不能用作Generator函数.
**this指向的固定化,并不是因为箭头函数内部有绑定this的机制,实际原因是箭头函数根本没有自己的this,导致内部的this就是外层代码块的this.正是因为它没有this,所以也就不能用作构造函数.**

双冒号运算符

函数绑定运算符是并排的两个冒号(:😃,双冒号左边是一个对象,右边是一个函数.该运算符会自动将左边的对象,作为上下文环境(即this对象),绑定到右边的函数上面.

foo:bar;
//等同于
bar.bind(foo);

foo:bar(...arguments);
//等同于
bar.bind(foo,arguments)

数组的扩展

扩展运算符

扩展运算符(spread)是三个点(…)。它好比rest参数的逆运算,将一个数组转为用逗号分隔得参数序列.

扩展运算符的应用

(1)复制数组

const a1=[1,2];
//写法一
const a2=[...a1];
//写法二
const [...a2]=a1;

(2)合并数组

//ES6的合并数组
[...arr1,...arr2,...arr3]
//['a','b','c','d','e']

(3)与解构赋值结合

const[first,...rest]=[1,2,3,4,5];
first //1
rest //[2,3,4,5]

如果将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错.

Array.from()

作用:用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括ES6新增的数据结构Set和Map).

let arrayLike = {
'0': 'a',
'1': 'b',
'2': 'c',
length: 3
};
//****类似数组的对象:本质特征只有一点,即必须有length属性
// ES5的写法
var arr1 = [].slice.call(arrayLike); // ['a', 'b', 'c']

// ES6的写法
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']

常见用法:

//NodeList对象
let ps = document.querySelectorAll('p');
Array.from(ps).forEach(function(p){
    console.log(p);
})
//arguments对象
function foo(){
    var args=Array.from(arguments);
}

只要是部署了Iterator接口的数据结构,Array.from都能将其转为数组.

扩展运算符(…)也可以将某些数据结构转为数组.

//arguments对象
function foo(){
    const args=[...arguments];
}
//NodeList对象
[...document.querySelectorAll('div')]

Array.from还可以接受第二个参数,作用于数组的map方法,用于对每个元素进行处理,将处理后的值放入返回的数组.

Array.from(arrayLike, x => x*x);
//等同于
Array.from(aerayLike).map(x => x*x);

Array.from([1,2,3], (x) => x*x);

Array.of()

作用:用于将一组值,转换为数组.

Array.of(3,1,2);//[3,1,2]
Array.of(3);//[3]
Array.of(3).length;//1

数组实例的copyWithin()

作用:在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组.也就是说,使用这个方法,会修改当前数组.

Array.prototype.copyWithin(target,start=0,end=this.length)
  • 三个参数:

    • target(必需):从该位置开始替换数据.
    • start(可选):从该位置开始读取数据,默认为0.如果为负值,表示倒数.
    • end(可选):到该位置前停止读取数据,默认等于数组长度.如果为负值,表示倒数.这三个参数都应该为数值,如果不是,会自动转为数值.

    [1,2,3,4,5].copyWithin(0,3)
    //[4,5,3,4,5]

数组实例的find()和findIndex()

作用:数组实例的find方法,用于找出第一个符合条件的数组成员.它的参数是一个回调函数,所以有数组成员依次执行该回调函数,直到找出第一个返回true的成员,然后返回该成员.如果没有符合条件的成员,则返回undefined.

[1,4,-5,10].find((n) => n<0)
// -5

find方法的回调函数可以接受三个参数,依次为当前的值,当前的位置和原数组.

[1,5,10,15].find(function(value,index,arr){
    return value > 9;
})  //10

findIndex方法和find方法的用法非常类似,返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1.

[1, 5, 10, 15].findIndex(function(value, index, arr) {
  return value > 9;
}) // 2

另外,这两个方法都可以发现NaN,弥补了数组的indexOf方法的不足.

[NaN].indexOf(NaN)
// -1

[NaN].findIndex(y => Object.is(NaN, y))
// 0
// findIndex方法可以借助Object.is方法做到。

数组实例的fill()

作用:fill方法使用给定值,填充一个数组.

['a','b','c'].fill(7);
// [7,7,7]

new Array(3).fill(7)
// [7,7,7]

fill方法还可以接受第二个和第三个参数,用于指定填充的起始位置和结束位置.

['a','b','c'].fill(7,1,2);
//['a',7,'c']

数组实例的entries(),keys()和values()

ES6提供三个新的方法 ----entries(),keys()和values()----用于遍历数组,它们都返回一个遍历器对象,可以用for…of循环进行遍历,唯一的区别是:keys()是对键名的遍历.values()是对键值的遍历,entries()是对键值对的遍历.

for (let index of ['a','b'].keys()) {

console.log(index);
}
//0
//1

for (let item of ['a','b'].values()) {
   console.log(item);
} 
// 'a'
// 'b'

for (let [index, elem] of ['a','b'].entries()) {
    console.log(index,elem);
}
//0 'a'
//1 'b'

数组实例的includes()

Array.prototype.includes方法返回一个布尔值,表示某个数组是否包含给定的值.

[1, 2, 3].includes(2)     // true
[1, 2, 3].includes(4)     // false
[1, 2, NaN].includes(NaN) // true

该方法的第二个参数表示搜索的起始位置,默认为0.如果第二个参数为负数,则表示倒数的位置,如果这时它大于数组长度(比如第二个参数为-4,但数组长度为3),则会重置为从0开始.

[1, 2, 3].includes(3, 3);  // false
[1, 2, 3].includes(3, -1); // true

对象的扩展

属性的简洁表示法

ES6允许直接写入变量和函数,作为对象的属性和方法.

let ms = {};

function getItem (key) {
   return key in ms ? ms[key] :nill;
}

function setItem (key,value) {
   ms[key] = value;
}

function clear () {
   ms = {};
}

module.exports = {getItem,setItem,clear};
//等同于
module.exports = {
  getItem:getItem,
  setItem:setItem,
  clear:clear
}

方法的name属性

函数的name属性,返回函数名,对象方法也是函数,因此也有name属性.

Object.is()

作用:用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致.

Object.is('foo','foo')
// true
Object.is({},{})
// false

不同之处只有两个:(1)+0不等于-0,(2)NaN等于自身.

+0 === -0 // true
NaN === NaN // false

Object.is(+0,-0)  //false
Object.is(NaN,NaN) //true

Object.assign()

作用:Object.assign方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target).

const target = {a: 1};

const source1 = {b: 2};
const source2 = {c: 3};

Object.assign(target,source1,source2);
target //{a:1,b:2,c:3}

Object.assign方法的第一个参数是目标对象,后面的参数都是源对象.

注意:如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性.

_proto_属性,Object.setPrototypeOf(),Object.getPrototypeOf()

_proto_属性

用来读取和设置当前对象的prototype对象.

// es6 的写法
const obj = {
  method: function() { ... }
};
obj.__proto__ = someOtherObj;

Object.setPrototypeOf()

Object.setPrototypeOf()方法的作用与_proto_相同,用来设置一个对象的prototype对象,返回参数对象本身,它是ES6正式推荐的设置原型对象的方法.

//格式
Object.setPrototypeOf(object,prototype)
//用法
const o = Object.setPrototypeOf({},null);

例子:

let proto = {};
let obj = {x: 10};
Object.setPrototypeOf(obj, proto);

proto.y = 20;
proto.z = 40;

obj.x //10
obj.y //20
obj.z //40
//将proto对象设为obj对象的原型,所以从obj对象可以读取proto对象的属性。

Object.getPrototypeOf()

该方法与Object.setPrototypeOf方法配套,用于读取一个对象的原型对象。

Object.getPrototypeOf(obj);

super关键字

this关键字总是指向函数所在的当前对象

super关键字指向当前对象的原型对象.

const proto = {
  foo: 'hello'
};

const obj = {
  find() {
    return super.foo;
  }
};

Object.setPrototypeOf(Obj,proto);
obj.find()  //"hello"

Object.keys(), Object.values(),

Object.entries()

let {keys, values, entries} = Object;
let obj = {a:1,b:2,c:3};

for (let key of keys(obj)) {
   console.log(key); //'a','b','c'
}

for (let value of values(obj)) {
   console.log(value); //1,2,3
}

for (let [key, value] of entries(obj)) {
   console.log([key,value]); //['a',1],['b',2],['c',3]
}

对象的扩展运算符

let z = {a:3,b:4};
let n = {...z};
n //{a:3,b:4}

Null传导运算符?

举例:要读取message.body.user.firstName

const firstName = (message
  && message.body
  && message.body.user
  && message.body.user.firstName) || 'default';

引入“Null传导运算符“ ?. 简化上面的写法

const firstName = message?.body?.user?.firstName || 'default';

Symbol

ES6引入了一种新的原始数据类型Symbol,表示独一无二的值.它是JS语言的第七种数据类型,前六种:undefined.null.布尔值(Boolean),字符串(String),数值(Number),对象(Object).

let s = Symbol();

typeof s   //"symbol"

注意:Symbol函数前不能使用new命令,否则会报错,这是因为生成的Symbol是一个原始类型的值,不是对象.

Symbol函数可以接受一个字符串作为参数,表示对Symbol实例的描述,主要是为了在控制台显示,或者转为字符串,比较容易区分.

let s1 = Symbol('foo');
let s2 = Symbol('bar');

s1 // Symbol(foo)
s2 // Symbol(bar)

s1.toString() // "Symbol(foo)"
s2.toString() // "Symbol(bar)"

如果Symbol的参数是一个对象,就会调用该对象的toString方法,将其转为字符串,然后才生成一个Symbol值.

const obj = {
  toString() {
    return 'abc';
  }
};
const sym = Symbol(obj);
sym // Symbol(abc)

Symbol值不能与其他类型的值进行运算,会报错.但是,Symbol值可以显示转为字符串,也可以转为布尔值,但是不能转为数值.

作为属性名的Symbol

let mySymbol = Symbol();
//第一种写法
let a={};
a[mySymbol]='Hello!';
//第二种写法
let a={
    [mySymbol]:'Hello!'
};
//第三种写法
let a={};
Object.defineProterty(a,mySymbol,{value:'Hello!'});
//以上写法都得到同样结果
a[mySymbol]//"Hello!"

属性名的遍历

Symbol作为属性名,该属性不会出现在for…in,for…of循环中,也不会被Object.keys(),Object.getOwnPropertyNames(),JSON.stringify()返回.但是,它也不是私有属性,有一个Object.getOwnPropertySymbols方法,可以获取指定对象的所有Symbol属性名.

Object.getOwnPropertySymbols方法返回一个数组,成员是当前对象的所有用作属性名的Symbol值.

cont obj = {};
let a = Symbol('a');
let b = Symbol('b');
obj[a] = 'Hello';
obj[b] = 'World';
const objectSymbols = Object.getOwnPropertySymbols(obj);
objectSymbols
//[Symbol(a),Symbol(b)]

另一个新的API,Reflect.ownKeys方法可以返回所有类型的键名,包括常规键名和Symbol键名.

let obj = {
   [Symbol('my_key')]:1,
   enum:2,
   nonEnum:3
};
Reflect.ownKeys(obj)
// ["enum","nonEnum",Symbol(my_key)]

作用:由于以Symbol值作为名称的属性,不会被常规的方法遍历到.我们可以利用这个特性,为对象定义一些非私有的,但又希望只用于内部的方法.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值