一、函数的创建

变量声明的方式

变量声明的方式,function关键字后面没有函数名,后面跟的是一个匿名函数,而且必须先声明再调用,不存在声明提前。

  1. var go = function(a){
  2. console.log(a)
  3. }
  4. /*变量声明的方式,function关键字后面没有函数名,后面跟的是一个匿名函数,
  5. 而且必须先声明再调用,不存在声明提前。*/

直接量的方式

直接量的函数声明的方式,解析器会率先读取函数声明,并使其在执行任何代码之前可用。

  1. go(1); //可以在函数前面调用
  2. function go(a){
  3. console.log(a)
  4. }

以上代码可以正常执行,因为再代码开始之前,解析器就已经通过一个名为函数声明提升的过程,读取并将函数声明添加到执行环境中。

二、函数的内部属性

在函数内部有两个特殊的对象:arguments和this。

agruments

JavaScript函数的一个独特之处在于你可以给函数传递任意数量的参数却不会造成错误。那是因为函数参数实际上被保存在一个成为arguments的类似数组的对象中。如同一个普通的JavaScript数组,arguments可以自由增长来包含任意个数的值,这些值可以通过数字索引来引用。

  1. function go(a,b,c){
  2. console.log(arguments)
  3. }
  4. go(10,10,30);

image.png
需要注意的是,虽然arguments很像数组,但是它是一个对象。数组专有的方法(比如slice和forEach),不能在arguments对象上直接使用。
如果要让arguments对象使用数组方法,解决方法是将arguments转为真正的数组。

转换为数组

下面是两种常见的转换方法:slice方法和逐一填入新数组
slice方法

  1. var args = Array.prototype.slice.call(arguments);

逐一填入新数组

  1. var args = [];
  2. //注意这里不能使用for in方法,因为arguments还不是数组,是一个对象
  3. for(var i=0;i<arguments.length;i++){
  4. args.push(arguments[i])
  5. }

解构参数

  1. //这里运用了es6新语法,解构参数,这样得到的args就是一个纯数组了
  2. function getAge(...args) {
  3. console.log(args instanceof Array); //true
  4. }

callee属性

image.png
仔细观察发现,arguments对象有一个名为callee的属性,该属性是一个指针,指向拥有这个arguments对象的函数

this

函数内部的另一个特殊对象是this,其行为与java和C#中的this大致类似。
换句话说,this引用的是函数据以执行的环境对象——或者
也可以说this值(当在网页的全局作用域调用函数时,this对象引用的就是window)
来看下面的例子

  1. window.color = "red";
  2. var o = {color:"blue"};
  3. function sayColor(){
  4. alert(this.color);
  5. }
  6. sayColor(); //"red"
  7. o.sayColor = sayColor;
  8. o.sayColor(); //"blue"

上面这个函数sayColor()是在全局作用域中定义的,它引用了this对象。由于在调用函数之前,this的值不确定,因此this可能会再代码执行过程中引用不同的对象。当在全局作用域调用sayColor()时,this引用的是全局对象window,换句话说,对this.color求值会转换成对时,this引用的是对象o,因此对this.color求值会转换成对o.color求值。

三、改变this

在JavaScript中,使用和操作函数中this的能力是良好地面向对象编程的关键。函数会在各种不同上下文中被使用,它们必须到哪都能正常工作。一般this会被自动设置。但是你可以改变它的值来完成不同的目标。有3种函数方法允许你改变this的值。(记住函数是对象,而对象可以有方法,所以函数也有)

call()方法

  1. /* call(thisObj,params) */
  2. function sayName(label){
  3. console.log(label+":"+this.name)
  4. }
  5. var name ="window";
  6. var cheng = {
  7. name:"cheng"
  8. }
  9. var wang = {
  10. name:"wang"
  11. }
  12. sayName("window")
  13. sayName.call(cheng,"chengchao")
  14. sayName.call(wang,"女孩子")

call()的第一个参数指定了函数执行时this的值,其后所有的参数都是需要被传入函数的参数。call绑定的函数会马上执行。

apply()方法

apply()的工作方式和call()完全一样,但它只接受两个参数:this值和一个数组或者类似数组的对象。

  1. /* bind,call,apply之间的区别
  2. bind改变函数的上下文执行环境,不会马上执行
  3. 而被call和apply绑定的函数会马上执行
  4. */
  5. function go(name,age){
  6. console.log(this.name+":"+this.age)
  7. console.log(name)
  8. console.log(age)
  9. }
  10. var li = {
  11. name:"李梦瑶",
  12. age:23
  13. }
  14. var zhang = {
  15. name:"张三丰",
  16. age:100
  17. }
  18. go.apply(li,["li",24])
  19. go.call(zhang,"zhang",111)

bind()方法

ES5中新加的一个方法:bind()。这个方法会创建一个函数的实例,其this值会被绑定到传给bind()函数的值

  1. window.color = "red";
  2. var o = {color:"blue"};
  3. function sayColor(){
  4. console.log(this.color);
  5. }
  6. var objSayColor = sayColor.bind(o);
  7. objSayColor(); //blue

函数执行了这个方法不会立即执行,它只是改变了函数内部this执行的上下文环境

  1. /* 改变函数内部的this关键字的指向 */
  2. var name ="cheng";
  3. var obj ={
  4. name:"li"
  5. }
  6. /* bind()改变了函数内部this执行的上下文环境 */
  7. var test = function(){
  8. console.log(this.name)
  9. }.bind(obj);
  10. test(); //输出li

四、箭头函数

箭头函数

  1. function show(x){
  2. return x;
  3. }
  4. /* 参数只有一个可以不用小括号,输出语句只有一行可以不用大括号 */
  5. var go=x=>x;
  6. var test = z=>console.log(z);
  7. var getInfo=(x,y)=>{
  8. console.log(x);
  9. console.log(x+y)
  10. }
  11. console.log(go(10))
  12. test(20)

箭头函数的好处

  1. /* 解决了函数内部this关键字的指向问题
  2. 当函数直接调用时,this指向window
  3. */
  4. var test = document.getElementById("test");
  5. test.onclick = function(){
  6. setTimeout(()=>{
  7. console.log(this)
  8. },300)
  9. }