函数上链接: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引擎中,尾调用函数与其他函数调用类似,创建一个新的栈。在循环调用中,每一个未用完的栈都会保存在内存中,调用栈过大就会引起一些问题。
尾调用的优化可以帮助函数保持一个更小的调用栈,从而减少内存的使用,避免溢栈出错。