【重读红宝书(JS高程4)】第10章-函数

37 阅读4分钟

这是重读红宝书(JavaScript高级程序设计 第4版)的第7篇文章,本节我们来看看函数:函数本质、函数名、理解参数、函数内部 本节涉及源码见仓库 github.com/adamswan/Re…

1、定义函数

函数是ECMAScript中最有意思的部分之一,这主要是因为函数实际上是对象,函数名就是指向函数对象的指针,而且不一定与函数本身紧密绑定

函数声明:

函数声明形式定义的函数,存在函数声明提升现象。在执行代码时,JavaScript 引擎会先执行一遍扫描, 把发现的函数声明提升到源代码树的顶部,所以可以在声明行之前编写调用代码

function sum (num1, num2) { 
 return num1 + num2; 
} 

函数表达式/匿名函数:

表达式无函数声明提升现象

let sum = function(num1, num2) { 
  return num1 + num2; 
}; 

箭头函数:

let sum = (num1, num2) => { 
  return num1 + num2; 
}; 

箭头函数注意点:

1、 没有 prototype

2、 没有 arguments

3、 也没有自己的 this ,箭头函数中的 this 是所在环境的,并且在声明时就确定了指向,而非调用时

4、 用 call、apply、bind 无法修改箭头函数中的 this 指向

5、 对象上的方法,不推荐用箭头函数定义,因为 this 可能不指向该对象

4、 和普通 function 最大的区别:箭头函数只能被调用,不能作为构造函数 new 一个对象,因此说它消除了普通 function 的二异性,因为普通 function 既可以直接调用,又可以 new 对象

为何箭头函数不能用来 new 对象?TC39 这个组织将箭头函数设计得很精妙,箭头函数没有 prototype,这样就无法通过它挂载公共方法;没有自己的 this,这样就无法给对象添加属性

2、函数名

函数名本质就是一个指针。把 sum 设置为 null 之后,就切断了它与函数之间的关联。而 anotherSum()还是可以照常调用,没有问题

function sum(num1, num2) { 
  return num1 + num2; 
} 

console.log(sum(10, 10)); // 20 

let anotherSum = sum; 

console.log(anotherSum(10, 10)); // 20 

sum = null; 

console.log(anotherSum(10, 10)); // 20 

所有,用 function 声明相同函数,后者会覆盖前者

function addSomeNumber(num) { 
  return num + 100; 
} 

function addSomeNumber(num) { 
  return num + 200; 
} 

let result = addSomeNumber(100); // 300

3、理解参数

ECMAScript 函数既不关心传入的参数个数,也不关心这些参数的数据类型。定义函数时要接收两个参数,并不意味着调用时就传两个参数。你可以传一 个、三个,甚至一个也不传,解释器都不会报错。

之所以会这样,主要是因为 ECMAScript 函数的参数在内部表现为一个类数组。函数被调用时总会接 收一个类数组,但函数并不关心这个类数组中包含什么。

在使用 function 关键字定义函数时,可以在函数内部访问 arguments 对象,它就是类数组,从中取得传进来的每个参数值:第一个参数是 arguments[0],第二个参数是 arguments[1],通过 arguments.length 属性可以知道传入了多少个参数

函数传值的本质,就是用将传入的值,填充 arguments 类数组的槽位

function sayHi() {
  console.log("Hello " + arguments[0] + ", " + arguments[1]);
}

sayHi("高圆圆", "刘亦菲");
//  Hello 高圆圆, 刘亦菲

// 当然,也可以混用:
function sayHi2(name) {
  console.log(name + ", " + arguments[1]);
}

sayHi2("赵丽颖", "艾玛沃特森");
//  赵丽颖, 艾玛沃特森

4、函数内部

arguments

arguments 类数组除了能存储参数外,还有一个 callee 属性,指向类数组所在的函数

function getName() {
  // callee 指向所在的函数
  console.log(arguments.callee);
  // f getName
}

getName();

this

this的指向、修改this指向的方法,不再次赘述


new.target

new.target 属性,用于检测是否通过 new 关键字调用的

如果函数是正常调用的,new.target 的值是 undefined;

如果是使用 new 关键字调用的,则 new.target 的值指向构造函数

function gaoyuanyuan() {
  console.log("高圆圆", new.target);
  // 高圆圆 undefined
}
gaoyuanyuan();

function Liuyifei() {
  console.log("刘亦菲", new.target);
  // 刘亦菲 ƒ Liuyifei
}
new Liuyifei();