ES6收录

2018-05-09  本文已影响0人  Vampire丶_L

我只想说学习ES6真的好痛苦,但是为了加深印象,巩固基础我还是自己整理一下吧,如果要很详细的整理,我觉得会很累,所以我就很随意啦。

一,let,const

1,不存在变量提升:先使用再let声明 会报错 ReferenceError
2,暂时性死区:在let声明变量完成之前,都属于变量的死区,任何使用这个变量的都会抛出一个错误,例如let x = x;//ReferenceError这个就错在还没有声明的时候就调用x,一定要等到声明完成之前
3,不允许重复声明:不允许在相同作用域中重复声明同一个变量function(){var a;let a;}(例如:不可以在函数内部重新声明参数)
4,es5只有全局作用域和函数作用域;es6新增了块级作用域,es6允许块级作用域的任意嵌套
内层作用域无法读取外层作用域的变量,内层作用域可以定义外层作用域的同名变量
例如:

    function a() {
        let i = 1;
        if (true) {
            let i = 2;//内层定义外层的同名变量
        }
        console.log(i);//1
    }
    a();
    function a() {
        var i = 1;
        if (true) {
            var i = 2;
        }
        console.log(i);//2
    }
    a();

5,const一旦声明必须初始化,而且不能改变,否则会报错,const的作用域和let命令的作用域一样,都是块级作用域,const声明的常量也是不提升,存在死区,只在声明的块级作用域内有效,不可重复声明
6,全局变量和顶层对象的属性的关系:在es5中这两者是相等的(此处是被认为是js设计的最大败笔之一)。es6中全局变量和顶层对象的属性将逐渐脱钩(但是为了保持兼容性):var命令和function命令声明的全局变量依旧是顶层对象的属性,另一方面规定:let const class命令声明的全局变量,不属于顶层对象的属性。

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

let b = 1;
window.b // undefined

二,变量的解构赋值

1,数组的解构赋值:
(1)let [a,b,c] = [1,2,3] //a=1 b=2 c=3只要等号两边的模式相同,左边的变量就会被赋予对应的值,如果解构不成功变量的值就是undefined。
(2)还有一种是不完全解构:
let [x, y] = [1, 2, 3];x // 1 y // 2这样也是可以成功的

数组的解构赋值等号右边的如果不是数组就会报错(准确来说不是可遍历的结构)

2,对象的解构:
对象的解构赋值是按照属性名的变量名进行赋值的,并不是数组那样固定位置的模式。

注意:对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者。

let { foo: baz } = { foo: "aaa", bar: "bbb" };
baz // "aaa"
foo // error: foo is not defined

上面代码中,foo是匹配的模式,baz才是变量。真正被赋值的是变量baz,而不是模式foo。
3,字符串的解构赋值:
字符串在解构赋值时,字符串被转换成类似数组的对象。来看代码:

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

4,数值和布尔值的解构赋值:
数值和布尔值在解构赋值时,(如果在等号右边的不是对象或者数组,都会转换成对象),会被转换成对象,在被包装成的对象都有toString属性,看代码:

let [toString:s] = 123;
s === Number.prototype.toString //true
let [toString:s] = true;
s === Boolean.prototype.toString //true

由于undefined 和null无法转换成对象,所以对这两个进行解构赋值会报错

说了这么多,变量的解构赋值有什么用呢?

1,交换变量的值
以前的交换变量的方法:

let x=1;
let y=2;
let z;
z=x;
x=y;
y=z;

可能这样的代码对早已熟悉的你来讲可能一点难度都没有,但是现在有一种很简单粗暴的方式:

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

这样的代码看起来是不是很清楚,一眼就可以看出你的代码是干什么用的;
2,从函数返回多个值
函数只能返回一个值,要返回多个值只能利用数组和对象,利用解构赋值的方法就可以非常方便的取出这些值

    function a(){
        return[1,2,3];
    }
    let [a,b,c] = a();

    function a(){
        return{
            foo:1,
            bar:2
        };
    }
let {foo,bar} = a();

3,提取jason数据

    let jasonData = {
        id:1508010228,
        status:"ok",
        data:[13,465]
    }
    let {id,status,data:number} = jasonData;
    console.log(id,status,number);//42, "ok",[13,465]

三,函数的扩展

1,箭头函数:

 let f = function(num1,num2){
            return num1+num2;
        }
//上面的函数等同于下面的箭头函数
        let f = (num1,num2)=>{
           return num1+num2;
        }
//或者
 let f = (num1,num2)=> num1+num2;
        

如果箭头函数里要返回一个对象的话,对象要加一个圆括号:

 let person = (name,age)=>({name:'ld',age:21})
    console.log(person().name)//ld
    console.log(person().age)//21  

箭头函数有几个需要注意的:

   function foo(){
        setTimeout(function(){
            console.log("id="+this.id)
        },100)
    }
    id = 21;
    foo.call({id:42});
    //控制台会打印出 id=21

我们知道在普通函数里 this是使用时候的对象 ,上面代码中 this对象所在的匿名函数是setTimeout函数的参数,所以当100毫秒后执行到setTimeout函数的时候(也就是使用的时候)this对象指向的是全局对象window 我们在全局作用域中为id赋值为21 所以控制台打印的是id=21
但是将setTimeout函数里面的匿名函数改为箭头函数时:

    function foo(){
        setTimeout(()=>{
            console.log("id="+this.id)
        },100);
    }
    id = 21;
    foo.call({id:42});
//控制台会打印出 id=42

setTimeout的参数时一个箭头函数,这个例子中箭头函数定义生效是在foo函数生成时,真正执行时在100毫秒以后,因为箭头函数中this时定义时的对象,所以this.id指向的时本例中的42
接下来再换个花样:

    let foo=()=>{
        setTimeout(()=>{
            console.log("id="+this.id)
        },100);
    }
    id = 21;
    foo.call({id:42});
    //控制台会打印出id=21

我将上面的第二个例子又改了改,将foo函数的定义也换成箭头函数的形式,结果和第一个例子一样输出id=21;我是这样理解的:

setTimeout里的参数是箭头函数,所以this指向的是箭头函数定义生效时的对象,也就是foo函数,但是foo函数也是箭头函数形式的,它所在的对象应该是全局对象window 所以就变成了第一个例子。

2,嵌套的箭头函数
将一个元素插入到数组里的逻辑代码实现

//插入一个指定元素到数组中得逻辑 将什么插入到什么得那个位置里 
        function insert(value) {
            return {
                into: function (array) {
                    return {
                        after: function (afterValue) {
                            array.splice(array.indexOf(afterValue) + 1, 0, value);
                            //splice():改变原本数组,向数组中添加或删除项目,返回被删除的项目
                            return array;
                        }
                    }
                }
            }
        }
        let a = [1, 3, 4];
        insert(2).into(a).after(1);
        console.log(a);

splice函数详细用法见详细见http://www.w3school.com.cn/jsref/jsref_splice.asp

 //es6写法
        let insert = (value) => ({
            into: (array) => ({
                after: (afterValue) => {
                    array.splice(array.indexOf(afterValue) + 1,0 , value);
                    return array;
                }
            })
        })
        let a = [1,3,4];
        insert(2).into(a).after(1);
        console.log(a);
        insert(5).into(a).after(4);
        console.log(a)

3,尾递归和尾调用优化
尾调用优化:函数调用时,在内存中会形成一个调用帧,用来保存调用位置和内部变量的信息,例如,在A函数里调用B函数,在A函数上方就会形成一个B函数的调用帧,以此类推,就会形成一个调用栈。
由于尾调用是在函数的最后操作,所以不需要保留外层函数的调用帧,以为调用位置和内部变量等信息不会在用到了,只要直接调用内层函数的调用帧就可以了(注意:内层函数如果使用到外层函数的变量,则外层函数的调用帧不会被内层函数的调用帧取代,也就是不会进行尾调用优化)

//尾调用优化
function a(){
   return b();
}

上面的尾调用优化是一个函数调用另一个函数,递归则是自己调用自己;

尾递归:一般的递归,在内存中会产生大量的调用帧,有可能会造成栈溢出,如果使用尾递归 则不会发生这种情况,只会产生一个调用帧

上一篇 下一篇

猜你喜欢

热点阅读