this对象
全局环境中的this指向全局对象,也就是window
this.abc="abc"
console.log(window.abc) //'abc'
注意:全局环境下的this不推荐使用,使用的话更倾向于使用window
函数中的this对象,取决于函数是如何被调用的,大致分为:简单调用,对象方法调用,call和apply调用,构造函数调用;
简单调用
就是直接用()的调用
function f(){
console.log(this) //window,如果是在严格模式下,还会报undefined
}
f()
对象方法调用
let obj={
f(){
console.log(this) //obj
}
}
也就是说作为对象方法调用,指向的是对象本身
这边有一个坑,示例如下
let a={
aa() {
console.log(this); //Obejct
function aaa(){
console.log(this); //window
}
aaa();
}
}
a.aa()
这边的函数aaa()里的this指向的是window,而不是自以为的Object,这边往往会出错,修正使用call和apply
let a={
aa() {
console.log(this); //Obejct
function aaa(){
console.log(this); //Object
}
aaa.call(this);
}
}
a.aa()
构造函数调用
function B(){
this.a='a'
};
let ba=new B();
console.log(ba.a); //'a'
- 函数作为普通函数使用,那么函数中的this指向的是全局对象,如果没有显示的值,返回的是undefined
- 函数作为构造函数使用,那么函数中的this指向的是实例对象,如果没有显示的值,默认返回的是this对象
函数的双重功能
ES6中的函数内部有两个不同的方法:[[Call]]和[[Constructor]],当使用普通方式调用函数时,[[call]]会被执行,当使用构造函数调用时,[[constructor]]方法会被执行。
new.target元属性
当以构造函数的形式调用函数时,new.target指向的是构造函数本身,因此当我们需要把函数仅作为构造函数时,不希望作为普通函数调用时,可以设置,如下示例
function Person(name) {
if(new.target === Person){
this.name = name
}
else{
throw new Error("必须使用New关键字")
}
}
let person=new Person("John");
console.log(person);
let person1=Person("john"); //报错
console.log(person1);
函数参数的传递方式
函数的参数,分按值传递和按引用传递
- 按值传递:参数形参的值是调用函数所传入实参的副本,也就是后面对形参的修改不会影响外面的实参
- 按引用传递:函数形参的值是调用函数所传入实参的引用,后面对形参的修改会影响实参
函数应用(重点)
详细见单独一章
箭头函数
- 没有自己的this,super,arguments和new.target,它们是离该箭头函数最近的非箭头函数的绑定
- 不能使用new来调用
- 没有原型对象
- 内部的this无法改变,意味这不能使用call,apply等方法修改this的指向
- 形参名称不能重复
箭头函数有几种变体,但大致都是有 参数=>函数体 组成的,不需要return关键字,就代表返回
示例代码1:
var double=value=>value*2;
//等同于
var double=function(value){
return value*2
}
示例代码2:
var double=(a,b)=>a*b;
//等同于
var double=function(a,b){
return a*b
}
示例代码3:
var double=()=>'john';
//等同于
var double=function(){
return 'john'
}
示例代码4:
var double=()=>{};
这是的参数为空,函数体也为空,那么函数体需要用大括号括起来,否则会报错
示例代码5:
var double=(a,b)=>({a:a,b:b});
这是将参数返回一个对象,对象需要用大括号括起来,否则会报错
箭头函数的语法是宽松绑定
var double = x=>(x % 2) === 0? x : 0
箭头的优先级比其他所有的都要低,因此注意加括号
var double = (x=>(x % 2) === 0)? x : 0
因此可以看出函数体如果是一个表达式,则不需要加{},如果是语句则要加{}
示例6
let value=[2,3,1];
let result=value.sort(function(a,b) {
return a-b
})
let result=value.sort((a,b)=>(a-b))
console.log(result);
尾调用优化
在执行某个函数时,如果最后一部是一个函数调用,并且被调用函数的返回值直接被当作函数返回,就被称为尾调用
传统函数调用
function e(x) {
return x;
}
function f(x) {
const y=x+1;
return e(y)
}
console.log(f(2));
尾调用的要求
- 尾调用不需要访问当前栈中的变量,也就是没有闭包
- 返回到尾调用处时,不用再做其他事情
- 尾调用的返回值,直接返回给调用它所在函数的调用者
尾递归
前面知道了,如果函数调用本身时是一个尾调用,则称之为尾递归
function abd(n) {
if(n===1||n===2){
return 1;
}
return abd(n-1)+abd(n-2)
}
改成尾调用
function abd2(n,a=0,b=1) {
if(n===0){
return 1
}
if(n===2){
return b;
}
return abd2(n-1,b,a+b)
}