ES6基础:函数(下)

311 阅读5分钟

函数上链接:juejin.cn/post/684490…

函数(下)

1.明确函数的多重用途

假如有一个构造函数Person。总所周知我们要通过new关键字来调用改造含函数创建对象,如果没有new关键字来调用Person那么最后会返回undefined。 JavaScript函数有两个不同的内部方法,[[call]]和[[construct]],当new关键字调用的时候执行的是后者,负责创建一个作实例Person的新对象作,再执行函数体,将this绑定到实例上;如果没有new关键字就执行[[call]]函数,从而直接执行代码中的函数体。

具有【construct】方法的函数被统称为构造函数,不是所有的函数都有【construct】方法,因此不是所有的函数都能够被new,例如后面要说的箭头函数。

1.1在es5中判断函数是否被调用的方法

最流行的方法是用instanceof,this in stanceof Person 先检查this值,看是否是构造函数的实例,但是有一个问题就是不用new关键字也可以将this绑定到构造函数上面

var personA = Person.call(person,"fff");

虽然没有new关键字,但是参数person就将this绑定到person实例上面。所以不知道到底是call,apply方法还是new关键字调用得到的person实例。 为了解决这个问题,es6中引入了new.target这个元属性。

元属性new.target

当调用函数【construct】方法时,new.target被赋值为new操作符的目标,,通常是被创建对象的实例,如果调用call方法,new.target的值就是undefined。

function Person(name) {
    if(new.target === person) {
        this.name = name;
    }else{
        throw new Error("必须通过new关键字调用person");
    }
}
function AnotherPerson() {
    Person.call(this, name);
}
var person = new Person("vih");
var anootherPerson = new AnotherPerson("vih");

在函数外使用new.target是一个错误的语法

2.块级函数

在es5严格模式中,在代码块内部声明函数时程序会抛出错误,在es6严格模式中,将函数看成一个块级声明,在定义函数的代码块中访问和调用,一旦代码块结束执行,函数将不再存在。

2.1块级函数使用的场景

块级函数与let函数表达式类似,只在代码块中起作用。在该代码块中,二者的区别主要是块级函数会被提升至块的顶部,而let不会。所以一般不需要提升至代码块顶部选择let表达式。

2.2非严格模式下的块级函数

在es6中,即使处于非严格模式下,但是函数不会提升到代码块的顶部,而是提升到全局作用域的顶部或外围函数顶部。

3.箭头函数

在es6中,箭头函数是其中最有趣的新增特性。与传统的JavaScript函数有些不同

3.1箭头函数语法

由参数,箭头,函数体组成,箭头左边是参数,右边时函数体并,右侧表达式被求值后立即返回。

let sun = ()=>num1+num2;
//==
let sun = function() {
    return num1+num2;
}

还有像返回对象字面量,传入参数的个数,创建空函数等等。总而言之箭头函数灵活多变,语法多变,使用场景也有多种形式。

3.2创建立即执行函数表达式

创建一个匿名函数并立即调用,只需要将箭头函数包裹在小括号里面

let persson = ((name)=> {
    //函数体
})(传入的参数)
console.log(...);

3.3 箭头函数没有this绑定

箭头函数中没有this绑定,必须通过查找作用域链来决定其值。如果箭头函数被非箭头函数包含,则this绑定到最近一层非箭头函数的this。否则this会被设置为全局对象。

箭头函数缺少prototype属性,没有[[construct]]方法,所以不能以new关键字混用

箭头函数不能通过apply,call,bind方法来改变this值,this值不受这些方法的影响

3.4箭头函数没有arguments绑定

3.4.1箭头函数的this指向全局,使用arguments会报未声明的错误

如果箭头函数的this指向window(全局对象)使用arguments会报错,未声明arguments。

let b = () => {
  console.log(arguments);
};
b(1, 2, 3, 4); // Uncaught ReferenceError: arguments is not defined

如果你声明了一个全局变量为arguments,那就不会报错了,但是你为什么要这么做呢? 箭头函数的this指向普通函数时,它的argumens继承于该普通函数

3.4.2上面是第一种情况:箭头函数的this指向全局对象,会报arguments未声明的错误。

第二种情况是:箭头函数的this如果指向普通函数,它的argumens继承于该普通函数

function bar() {
  console.log(arguments); // ['外层第二个普通函数的参数']
  bb('外层第一个普通函数的参数');
  function bb() {
    console.log(arguments); // ["外层第一个普通函数的参数"]
    let a = () => {
      console.log(arguments, 'arguments继承this指向的那个普通函数'); // ["外层第一个普通函数的参数"]
    };
    a('箭头函数的参数'); // this指向bb
  }
}
bar('外层第二个普通函数的参数');

那如果我们就是要访问箭头函数的参数呢?

你可以通过命名参数或者 rest 参数的形式访问参数:

let nums = (...nums) => nums;

3.5箭头函数总结

  • 没有this,super,argument,new.target绑定
  • 不能通过new关键字调用
  • 没有原型
  • 不可以改变this绑定
  • 不支持arguments对象
  • 不支持重复的命名参数
  • 箭头函数也有name属性i

4.尾调用优化

尾调用是指函数a作为作为另一个函数b的最后一条语句被调用.在es5引擎中,尾调用函数与其他函数调用类似,创建一个新的栈。在循环调用中,每一个未用完的栈都会保存在内存中,调用栈过大就会引起一些问题。

尾调用的优化可以帮助函数保持一个更小的调用栈,从而减少内存的使用,避免溢栈出错。