Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

30.ES6 笔记二 #31

Open
ccforward opened this issue Jul 21, 2016 · 0 comments
Open

30.ES6 笔记二 #31

ccforward opened this issue Jul 21, 2016 · 0 comments

Comments

@ccforward
Copy link
Owner

ccforward commented Jul 21, 2016

ES6 笔记二

ES6 笔记一

思维导图

1. 函数

1.1 函数参数默认值

  • 基本用法

    function log(x, y='Worlds'){
        console.log(x,y)
    }
    log('Hello'); // Hello, World
    
    function Poin(x=0,y=0){
        this.x = x;
        this.y = y;
    }
    
    var p = new Point();
    p // {x:0, y:0}

    参数变量是默认声明的,不能用 let const 再次声明

    function foo(x = 1){
        let x = 2; // error
        const x = 3; // error
    }
  • 与解构赋值默认值结合使用

    function fetch(url, {method='GET'}={}){
        console.log(method)
    }
    fetch('/index.html') // 'GET'
    
    function m({x=0,y=0}={}){
        console.log([x,y])
    }
    
    m() // [0,0]
    m({x:1, y:2}) // [1,2]
    m({x:3}) // [3,0]
    m({}) // [0,0]
    m({xx:1}) // [0,0]
  • 参数默认值位置

    通常把函数的尾参数作为有默认值的参数。因为这样比较容易看出到底忽略那些参数。

    function f(x,y=1,z){
        console.log([x,y,z])
    }
    
    f() // [undefined, 1, undefined]
    f(1) // [1, 1, undefined]
    f(1,,2) // 出错
    f(1,undefined,3) // [1, 1, 3]

    上面的函数,有默认值的参数不是尾参数,因此无法省略该参数而不省略其后的参数,除非显式的输入 undefined

  • 函数length属性

    指定了默认值后函数的 length 属性将返回没有指定默认值的参数个数

    (function(a){}).length // 1
    (function(a,b,c=1){}).length //2

    PS: rest参数不会计入 length 属性

  • 作用域

    如果参数默认值是一个变量,则该变量所处的作用域与其它变量的作用域规则是一样的,即先是当前函数的作用域,然后才是全局作用域。

    var x = 1;
    function f(x, y=x){
        console.log(y)
    }
    f(2)  // 2

    由于函数作用域内部的变量 x 已经生成所以 y 等于参数 x 而不是全局变量x。

    let x = 1;
    function f(y=x){
        let x= 2;
        console.log(y)
    }
    f() // 1

    上述代码函数调用时 y 的默认值变量 x 尚未在函数内部生成,所以 x 指向全局变量。

    如果函数A的默认值是函数B
    那么由于函数的作用域是其声明时的作用域,函数B的作用域就不是函数A,而是全局作用域。如下:

    let foo = 'outer';
    
    function bar(func = x => foo){
        let foo = 'inner';
        console.log(func());
    }
    bar(); // outer

    上述代码等同于

    let foo = 'outer';
    let f = x => foo;
    
    function bar(func = f) {
      let foo = 'inner';
      console.log(func()); 
    }
    bar(); // outer

    关于babel的一个bug

  • 应用

    可指定函数某一个参数不得省略,省略就会抛出错误

    function throwIfMissing() {
      throw new Error('Missing parameter');
    }
    
    function foo(mustBeProvided = throwIfMissing()) {
      return mustBeProvided;
    }
    
    foo()
    // Error: Missing parameter

    上述代码可知参数 mustBeProvided 的默认值等于 throwIfMissing 函数的运行结果。这说明函数参数的默认值不是在定义时执行,而是在运行时执行。

    如果把函数参数默认值设置为 undefined 则表示这个参数是可以省略的。

1.2 rest 参数

rest参数("...变量名"),用于获取函数的多余参数。rest参数搭配的变量是一个数组,该变量将多余的参数放入其中。

function push(array, ...items){
    items.forEach(item => {
        array.push(item);
    })
}
var a = [];
push(a,1,2,3)

rest 参数只能作为最后一参数。

函数的 length 属性不包括 rest 参数。

(function(...a){}).length // 0
(function(a, ...b){}).length // 1

1.3 扩展运算符

  • 含义

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

    console.log(...[1,2,3])
    // 1 2 3
    
    console.log(1, ...[2,3,4], 5)
    // 1 2 3 4 5

    该运算符主要用于函数的调用

    function add(x,y){
        console.log(x+y);
    }
    var arr = [1,10];
    add(...arr); // 11  ...运算符把arr数组变为参数序列
  • 替代数组的 apply 方法

    由于扩展运算符可以展开数组,所以不需要apply将数组转为函数的参数了。

    // ES5
    function f(x,y,z){}
    var args = [1,2,3];
    f.apply(null, args)
    // ES6
    function f(x,y,z){}
    var args = [1,2,3];
    f(...args)
    • 使用 Math.max 求数组最大元素

      // ES5
      Math.max.apply(null, [1,2,3,4])
      // ES6
      Math.max(null, ...[1,2,3,4])
    • 通过 push 将一个数组添加到另一数组尾部

      // ES5
      var arr1 = [0,1,2];
      var arr2 = [3,4,5];
      Array.prototype.push.apply(arr1,arr2)
      // ES6
      var arr1 = [0,1,2];
      var arr2 = [3,4,5];
      arr1.push(...arr2)
  • 扩展运算符的应用

    1. 合并数组

      //ES5
      [1,2].concat(more);
      arr1.concat(arr2,arr3)
      
      //ES6
      [1,2, ...more]
      [...arr1, ...arr2, ...arr3)
    2. 与解构赋值结合生成数组

      // ES5
      a = list[0], rest = list.slice(1)
      // ES6
      [a, ...rest] = list
      
      const [first, ...rest] = [1, 2, 3, 4, 5];
      first // 1
      rest  // [2, 3, 4, 5]
      
      const [first, ...rest] = [];
      first // undefined
      rest  // []:
      
      const [first, ...rest] = ["foo"];
      first  // "foo"
      rest   // []

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

    3. 字符串
      将字符串转为真正的数组

      [...'hello']
      // [ "h", "e", "l", "l", "o" ]

      还有一个好处是 能够正确识别32位的Unicode字符

      其中Unicode字符如: 'x\uD83D\uDE80y' 也就是 "x🚀y"

      'x\uD83D\uDE80y'.length // 4
      [...'x\uD83D\uDE80y'].length // 3

      JavaScript会将32位Unicode字符,识别为2个字符,采用扩展运算符就没有这个问题。因此可以这样来写:

      function length(str) {
        return [...str].length;
      }
      
      length('x\uD83D\uDE80y') // 3

      凡是涉及到操作32位Unicode字符的函数,都有这个问题。因此,最好都用扩展运算符改写。

      let str = 'x\uD83D\uDE80y';
      
      str.split('').reverse().join('')
      // 'y\uDE80\uD83Dx'
      
      [...str].reverse().join('')
      // 'y\uD83D\uDE80x'
    • 类似数组的对象

      任何类似数组的对象都可以用扩展运算符转为真正的数组

      [...document.quertSelectorAll('div')];
      // [<div>, <div>, <div>]
    • Map和Set结构,Generator函数

      扩展运算符内部调用的是数据结构的Iterator接口,因此只要具有Iterator接口的对象,都可以使用扩展运算符。

      let map = new Map([
            [1, 'one'],
            [2, 'two'],
            [3, 'three'],
          ]);
      
      let arr = [...map.keys()]; // [1, 2, 3]

      Generator函数运行后,返回一个遍历器对象,因此也可以使用扩展运算符。

1.4 name 属性

name属性返回该函数的函数名

匿名函数:

var fn = function(){}

//ES5
fn.name // ""

//ES6
fn.name // fn

Function构造函数返回的函数实例,name属性的值为“anonymous”。

(new Function).name // "anonymous"

bind返回的函数,name属性值会加上“bound ”前缀。

function foo() {};
foo.bind({}).name // "bound foo"

(function(){}).bind({}).name // "bound "

1.4 箭头函数

  • 基本用法

    var f = v => v;
    
    var f = function(v){
        return v;
    }

    由于大括号被解释为代码块,所以如果箭头函数要返回一个对象,必须在对象外面加上括号。

    var getTempItem = id => ({ id: id, name: "Temp" });

    与解构赋值一起使用:

    const full = ({first, last}) => first + ',' + last;
    //等同于
    function full(person){
        return person.first + person.last;
    }

    简化回调函数:

    [1,2,3,4].map(x => x * x);
    //等同于
    [1,2,3,4].map(function(x){
        return x * x;
    });
    
    var result = values.sort((a,b) => a-b);
    //等同于
    var result = values.sort(function(a,b){
        return a - b;
    })

    结合rest参数使用

    const numbers = (...nums) => nums;
    numbers(1,2,3,4,5)
    // [1,2,3,4,5]
    
    
    headAndTail(1, 2, 3, 4, 5);
    // [1,[2,3,4,5]]
  • 注意点

    1. 函数体内的this对象就是定义时所在的对象,而不是使用时所在的对象
    2. 不可以当做构造函数。也就是不可使用new命令,否则会抛出一个错误。
    3. 不可以使用arguments对象,该对象在函数体内不存在。如果用,可以用rest参数代替。
    4. 不可以适用于 yeild 命令,因此箭头函数不可以用于 Generator 函数。

    第一条尤其重要。this对象的指向是可变的,但是在箭头函数内部是固定的。

    function foo(){
        setTimeout(() => {
            console.log("id:" + this.id)
        },1000)
    }
    foo.call({id:11})
    
    //转为ES5代码
    function foo() {
        var _this = this;
        setTimeout(function () {
            console.log("id:" + _this.id);
        }, 1000);
    }

    如果是个普通函数,执行的this应该指向 window 对象,但是箭头函数导致this对象总是指向函数所在的对象。

    再比如:

    function Timer(){
        this.seconds = 0;
        setInterval(()=>this.seconds++,1000)
    }
    
    var t = new Timer();
    setTimeout(()=>console.log(t.seconds), 3100);
    
    // 3

    Timer函数内的 setInterval 调用了 this.seconds 属性,通过箭头函数让this总是指向Timer的实例对象。否则输出0 而不是3.

    this 指向的固定化,并不是因为箭头函数内部有绑定this的机制,实际原因是箭头函数根本没有自己的this,导致内部的this就是外层代码块的this。正因为没有this,所以不能用作构造函数。

    除了this,arguments super new.target 这三个变量在箭头函数中也是不存在的,分别指向外层函数对应的变量。

    function foo() {
      setTimeout(() => {
        console.log('args:', arguments);
      }, 100);
    }
    
    foo(2, 4, 6, 8)
    // args: [2, 4, 6, 8]

    箭头函数内部的变量 arguments 其实是函数 foo 的 arguments 变量。

    由于箭头函数没有自己的this,所以当然也就不能用call()、apply()、bind()这些方法去改变this的指向。

    (function() {
      return [
        (() => this.x).bind({ x: 'inner' })()
      ];
    }).call({ x: 'outer' });
    
    // outer

    上面的代码中箭头函数没有自己的 this,所以 bind 无效,内部的 this 指向外部的 this。

  • 函数绑定

    ES7提出了『函数绑定』运算符用来取代 call apply bind 调用。

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

    foo::bar;
    bar.bind(foo);
    
    foo::bar(...arguments);
    bar.apply(foo, arguments);
    
    var foo = {ss: 112}
    var bar = function(){console.log(this.ss)};
    foo::bar(); // 112

    双冒号运算符返回的还是原对象,因此可以采用链式写法

    let { find, html } = jake;
    
    document.querySelectorAll("div.classA")
    ::find("p")
    ::html("this is p");
@ccforward ccforward changed the title 30. ES6 笔记二 30.ES6 笔记二 Jul 21, 2016
@ccforward ccforward self-assigned this Jul 21, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant