ES6基础语法

ES6

​ ES6 ECMAScipt 第六版

​ ECMAScript 是js的一套标准化设置

let,const

let const 是es6新增的语法

作用:用来声明变量的 var作用一样

区别:

let同一作用域内可以重新赋值 但不能重复声明

const声明的是常量 声明后 不能重新声明或重新赋值

特点

1.let声明的常量 只在let所在的作用域内起作用(变量绑定)

var a = 123;
if(true){
    a = 456;
    console.log(a); // 报错
    let a;
} 

(1) 作用域问题

通常情况下 如果需要一个变量在规定的作用域内

可以使用function关键自搭建一个作用域

(function(){
    var a = 1;
}())
console.log(a);

es6中 let只要存在{} 就可以形成块级作用域 if结构 for循环结构 都有{}

{
    let b = 1
}
console.log(b);

(2) 变量提升问题

在if结构里使用var声明 {}不能形成块级作用域 那么if结构里的变量声明 就会覆盖全局变量

解决办法:在if结构里使用let声明变量 在if结构里形成块级作用域

var time = new Data();
function fn(){
    console.log(time);

    if(false){
        // var time = "...";
        let time = "...";
    }
}

(3) for循环中 循环变量泄露为全局变量的问题

// 使用var定义变量
// 定时器为异步操作 当执行定时器时 循环结束变量i=5
for (var i = 0; i < 5; i++) {
    setTimeout(function () {
        console.log(i);
    })
}
console.log("wrap");
// wrap
// 5 5 5 5 5

解决:

使用自执行函数创建块级作用域 但是外部可以访问到循环变量i

for (var i = 0; i < 5; i++) {
    (function (i) {
        setTimeout(function () {
            console.log(i);
        })
    }(i))
}
console.log("wrap");
console.log(i); // 5
// wrap
// 0 1 2 3 4

使用let定义块级作用域

for (let i = 0; i < 5; i++) {
    setTimeout(function () {
        console.log(i);
    })
}
console.log("wrap");
// console.log(i); // 报错
// wrap
// 0 1 2 3 4

js语言特性 : 单线程 同一时间内只做一件事

任务队列 : 所有的任务都要排队 前面一个执行完 才会执行后面的任务

事件循环 : 主线程会不断循环的去任务队列里读取任务(Event Loop)

当遇到异步任务 或某个事件被触发 那么相应的回调函数或函数体就会被拉到主线程里面去执行

数组解构赋值

基本用法

在ES6中 按照一定的模式 从数组中提取数值 对对应的变量赋值的操作 就叫结构赋值

本质上 解构赋值就是模式匹配

// 解构赋值
var [a,b,c] = [1,2,3]
console.log(a,b,c);  // 1,2,3

1.如果要解构成功 那么就必须保证两边模式完全一样

var [a,b,c] = [1,[2],{name:"lucy"}];
console.log(a,b,c);  // 1,[2],{name:"lucy"}

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

let [r] = [];
console.log(r); // undefined 就像var声明变量不赋值

3.不完全解构情况下 也可以进行解构 赋值 只是后面放数值的数组有多的数字

不完全解构 指的是等号左右两边模式一样 但是只匹配到右边的一部分数据

let[x,y] = [1,2,3]
console.log(x,y);    // 1,2

4.如果等号右边不是数组 那么就会报错

let [m] = 1;
let [m] = false;
let [m] = null;
let [m] = {};
console.log(m);     // {} is not iterable 因为{}不能使用下标进行迭代

注意点

1.解构赋值是允许设置默认值的 但是要启动默认值 这个变量的值就必须严格等于undefined

let[a,b] = [1,2]

let[a,b = "abc"] = [1,2]
console.log(a,b);   // 1,2

let[a,b = "abc"] = [1]
console.log(a,b);   // 1,"abc"

let [a, b = "abc"] = [1,null];
console.log(a,b);   // 1 "null"

let [a, b = "abc"] = [1,undefined];
console.log(a,b);   // 1 "abc"

let [a, b = "abc"] = [1,""];
console.log(a,b);   // 1 ""

2.如果设置的默认值是一个表达式 那么这个表达式只是在需要启动默认值是才会运算

function fn() {
    console.log(123);
    // return 123;
}
let [s = fn()] = [12];
console.log(s); // 12;

let [s = fn()] = [];
console.log(s); // undefined 这里的undefined是fn调用后返回值undefined

3.默认值可以使用其他变量 但是前提是赋值的这个变量必须是提前声明过的

let d = 3;
let [x = d] = [];
console.log(x); // 3

let [x = 2, y = x] = [];
console.log(x,y);   // 2,2

let [x = y, y = 2] = [];
console.log(x,y);   // 报错
// ||
let x = y;
let y = 2;

let [x = 2,y = x] = [8];
console.log(x,y);   // 8,8

扩展运算符(…)

使用扩展运算符 就是将一个数组转为用逗号分隔的参数序列。

// 直接赋值 修改了原数组 arr1里的值也会发生改变 因为他们指向同一块内存空间
var arr = [100, 200, 300];
var arr1 = arr;
arr[1] = 20;
console.log(arr);   // [100, 20, 300]
console.log(arr1);  // [100, 20, 300]

// 使用扩展运算符 不会影响新数组
var arr = [100, 200, 300];
var arr2 = [...arr]
arr[0] = 10;
console.log(arr);   // [10, 200, 300]
console.log(arr2);  // [100,200,300]

扩展运算符的作用

1.数组合并

concat方法 扩展运算符 都是浅拷贝 如果修改了原数组的指向 就会同步到新数组 就是浅拷贝

var arr1 = [1,2,3];
var arr2 = [10,20,30];
// ES5 的合并数组
var arrCon = arr1.concat(arr2);
console.log(arrCon);	// [1, 2, 3, 10, 20, 30]
// ES6 的合并数组
var arrSp = [...arr1,...arr2];
console.log(arrSp);		// [1, 2, 3, 10, 20, 30]

// 这里证明了合并后的数组和合并前的数组指向同一片内存空间(第一个数组) 所以证明了都是浅拷贝
console.log(arrCon[0] === arr1[0]); // true
console.log(arrSp[0] === arr1[0]);  // true

// 修改原数组后 会同步反映到新数组
arr1[2] = 33;
console.log(arrCon);	// [1, 2, 33, 10, 20, 30]
console.log(arrSp);		// [1, 2, 33, 10, 20, 30]

2.数组的拷贝

// 浅拷贝	修改原数组 会同步反映到新数组
var arr = [1, [2], { name: "zhang" }];
var arrSp = [...arr];
arr[2].name = "张三";
console.log(arrSp); // name = "张三"

// 深拷贝 修改原数组 不会影响新数组
var arr = [1, [2], { name: "zhang" }];
var str = JSON.stringify(arr);
var arr2 = JSON.parse(str);
arr[2].name = "张三";
console.log(arr2);  // name = "zhang"

3.与解构赋值结合

如果是普通变量 那么就按照模式赋值 如果使用了扩展运算符 那么就是后面的值依次放入数组

var [s, ...t] = [1, 2, 3, 4, 5];
console.log(s, t);   // 1    [2,3,4,5]

// 如果等号右边是空数组 那么仅仅是声明 变量值是undefined 数组是[]
var [s, ...t] = [];
console.log(s, t);   // undefined    []

// 如果扩展运算符用于数组赋值 只能放在参数最后
// let [a, ...b, c] = [1, 2, 3, 4, 5];     // 报错 Rest element must be last element

4.扩展运算符可以将字符串转成真正的数组

// ES5
var str = "hello";
console.log(str.split("")); //  ["h", "e", "l", "l", "o"]
// ES6
console.log([..."hello"]);	//  ["h", "e", "l", "l", "o"]

**注意:**只有函数调用时 扩展运算符才可以放在圆括号里 否则就会报错

var arr = [100, 200, 300];
// (...arr1);  // 报错 
// console.log([...arr]);
// console.log((...arr));  // 报错
console.log(...arr);	// 100 200 300

对象的扩展

1.对象的简单赋值

ES6在{} 允许直接写入变量名 解析时 变量名作为属性名 变量值作为属性值

var name = "lucy";
var age = 18;
var obj2 = {
    name,		// 属性名 属性值相同时可以简写
    age,
    study() {	// 函数的简写
        console.log("好好学习");
    }
}
console.log(obj2);
obj2.study();

// 示例
function count(x, y) {
    x = Math.pow(x, 2);
    y = Math.sqrt(y);
    // return {
    //     x: x,
    //     y: y
    // }
    return { x, y }
}
var res = count(3, 9)
console.log(res);   // {x: 9, y: 3}

2.super关键字

super关键字 指的是 指向当前对象的原型对象 _ proto _

注意点:

super关键字只能在对象的方法中使用 在其他地方使用就会报错

super关键字目前只能在对象方法的简写方式里去用 常规写法就会报错

var stu = {
    name: "张三",
    age: 12,
    // super不能用在对象里
    // type:super.type,    // 'super' keyword unexpected here

    // super关键字目前只能在对象的方法简写时才能用
    // learn:function(){
    //     console.log("我是" + super.type + "我爱学习");
    // },
    learn() {
        console.log("我是" + super.type + "我爱学习");
    }
}
stu.__proto__ = {
    type: "学生",
    run: function () {
        console.log("跑步");
    }
}

setPrototypeOf() 方法 :设置对象的原型

上面案例可以还可以这样写

var stu = {
    name: "张三",
    age: 12,
    learn() {
        console.log("我是" + super.type + "我爱学习");
    }
}
var proto = {
    type: "学生"
}
Object.setPrototypeOf(stu, proto);
stu.learn();    // 我是学生我爱学习

setPrototypeOf() 方法

Object.setPrototypeOf方法的作用与__proto__相同,用来设置一个对象的原型对象(prototype),返回参数对象本身

// 父亲类
function Father(){
    this.name = "父亲";
    this.age = "50";
}
// 父亲类原型上的方法
Father.prototype.type ="人类";
// 自定义原型对象
var proto = {
    job:"教师"
}
// 实例化对象
var f1 = new Father();
// 使用setPrototypeOf方法替换掉了f1的原型
Object.setPrototypeOf(f1,proto);
console.log(f1.job);    // 教师
// f1的原型对象 已经被修改 所以不再拥有Father.prototype里面的属性 所以访问的type是undefined
console.log(f1.type);  // undefined

var f2 = new Father();
// 因为f1的原型对象被重新设置 所以f1可以访问到 job属性 
// 但是f2依然指向Father.prototype 访问不到proto对象的属性
console.log(f2.job);    // undefined
console.log(f2.type);  // 人类

3.属性名表达式

ES6 允许字面量定义对象时,用表达式作为对象的属性名,即把表达式放在方括号内。

var n1 = "name"
var n2 = "age"
var n3 = "hobby"
function getGender(){
    return "gender"
}
var obj = {
    [n1] : "学生",
    [n2] : 20,
    [n3] : function(){
        console.log("爱好");
    },
    [n3](){
        console.log(this[n1] + "---" + this[n2]);
    },
    [getGender()]:"男"
}
obj[n3]();  // 学生 --- 20
console.log(obj[getGender()]);  // 男

注意:

1.属性名表达式与简洁表示法,不能同时使用,会报错。

// 报错
const foo = 'bar';
const bar = 'abc';
const baz = { [foo] };

// 正确
const foo = 'bar';
const baz = { [foo]: 'abc'};

2.属性名表达式如果是一个对象,默认情况下会自动将对象转为字符串[object Object]

const A = { a: 1 };
const B = { b: 2 };
const myObject = {
    [A]: 'valueA',
    [B]: 'valueB'
};
console.log(myObject); // {[object Object]: "valueB"}

上面代码中,A和B得到的都是[object Object],所以B会把A覆盖掉,

所以myObject最后只有一个[object Object]属性。

Object对象新增的方法

1.Object.is() 严格判断

这个方法和js的严格判断(===)用法基本一致 只是在对于-0 和 +0 以及NaN的判断上做了改善

console.log(-0 === +0);     // true
console.log(NaN === NaN);   // false

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

2.Object.assign() 合并对象

参数1 目标对象 参数2… 需要合并的对象

特点

1.返回值是传入的第一个对象 会把所有的对象合并上去再返回

var userName = { "name": "张三" }
var age = { "age": 18 }
var gender = { "gender": "男" }

console.log(Object.assign(age,userName,gender));// {age: 18, name: "张三", gender: "男"}
console.log(objAssign === age);         // true
console.log(objAssign === userName);    // false
console.log(objAssign === gender);      // false

2.第一个参数必须是对象 如果不是对象 就会转成对象

3.第一个参数如果是undefined 或 null 就不能转成对象

// 这里的字符串转成了包装类对象
console.log(Object.assign("", userName, age, gender));
// String {"", name: "张三", age: 18, gender: "男", length: 0}
console.log(Object.assign({}, userName, age, gender));
// {name: "张三", age: 18, gender: "男"}

// 第一个参数如果是undefined 或 null 就不能转成对象
// var stu = Object.assign(undefined,userName,age,gender) // 报错
// var stu = Object.assign(null,userName,age,gender)    // 报错

4.如果在需要合并的多个对象里面有同名属性 后面的就会覆盖

var stu1 = {"name":"张三"}
var stu2 = {"name":"李四","age":18}
var stu3 = {"gender":"男","age":20}

console.log(Object.assign(stu1,stu2,stu3)); // {name: "李四", age: 20, gender: "男"}

5.如果undifined 和 null 不是第一个参数 就不会报错

var stu = {name : "张三"}
console.log(Object.assign(stu, undefined)); // {name: "张三"}
console.log(Object.assign(stu, null)); // {name: "张三"}
console.log(Object.assign(stu, {})); // {name: "张三"}

6.assign方法是浅拷贝

var stu = {name : "张三"}
console.log(Object.assign({}, stu) === stu); // false

var a = {
    b: { c: 1 }
}
var s = Object.assign({}, a)
console.log(s.b.c); // 1
// 修改原对象的值 新对象也会改变
a.b.c = 2;
console.log(s.b.c); // 2Object,setPrototypeOf  给一个对象设置原型对象

3.Object.setPrototypeOf() getPrototypeOf()

Object,setPrototypeOf 给一个对象设置原型对象

参数1 : 目标对象 参数2 : 原型对象

Object.getPrototypeOf() 获取对象原型的方法

参数1 : 目标对象

var obj = {
    n1: 10,
    n2: 20
}
var obj1 = {
    n3: 30,
    n4: 40
}
var a = Object.setPrototypeOf(obj, obj1);
// setPrototypeOf 返回参数1 
// 参数1的__proto__指向了参数2
console.log(a); // {n1: 10, n2: 20}
console.log(a === obj); // true
console.log(obj.__proto__); // {n3: 30, n4: 40}

// getPrototypeOf 返回参数的__proto__
console.log(Object.getPrototypeOf(obj));    // {n3: 30, n4: 40}

4 .Object.values() Object.keys()

Object.keys() 是将对象所有的属性名遍历 然后返回一个数组

Object.values() 是将对象所有的属性值遍历 然后返回一个数组

var stu = {
    name: "张三",
    age: 18,
    work: "学生"
}
console.log(Object.keys(stu));  //  ["name", "age", "work"]
console.log(Object.values(stu));    // ["张三", 18, "学生"]

Class关键字

传统的构造函数的缺点 :

  1. 构造函数和原型方法属性分离 不便于维护 降低了可读性
  2. 原型对象的成员可以遍历
  3. 默认情况下构造函数也是可以被当做普通函数来调用的 所以 功能性不明显
function Person(name, sex) {
    this.name = name;
    this.sex = sex;
    return { name, sex }
}
// 给Person原型中添加toString方法
Person.prototype.toString = function () {
    return this.name + "," + this.sex
}

var p1 = new Person("张三", "男")
// console.log(new p1.toString()); // Person.toString {}

var p2 = new Person("李四", "女")
// console.log(p2.toString()); // 李四,女

console.log(Person.prototype);

// 遍历对象原型中的属性
for (let attr in Person.prototype) {
    console.log(attr);	// toString
}

// 当做普通函数调用
console.log(Person());     // {name: undefined, sex: undefined}

class基本用法

  1. class就是一个语法糖 本质就是一个函数 就是使用ES5里面的函数封装的
  2. 在类里面去定义方法的时候 可以省略function
  3. class类的数据类型是function
// 定义一个Person类
class Person {
    // 构造方法
    constructor(name, sex) {
        // this关键字则代表实例对象
        this.name = name;
        this.sex = sex;
    }
    // 定义一个toString方法
    toString() {
        return this.name + "," + this.sex
    }
}
var p1 = new Person("张三", "男");
console.log(p1.toString()); // 张三,男

console.log(typeof Person); // function

class类使用注意点

1.类是不可枚举的

for (let k in Person) {
    console.log(k);
}

2.类的用法很let和const一样 都有暂时性死区 必须先定义 再使用

new Person();  // 报错
class Person{
}

3.类里面的方法不能做为构造函数来使用

var s1 = new p2.toString();
console.log(s1);    // 报错

4.类使用的时候 必须配合new关键字进行使用 否则就会报错

Person(); // 报错

你可能感兴趣的:(ES6)