5.5 Function类型

1 概念

1.1 定义

函数实际上是对象,每个函数都是Function 类型的实例。

  • 函数声明:JavaScript 引擎能把函数声明提升到顶部,解析器会率先读取函数声明
  • 函数表达式:等到解析器执行到它所在的代码行,才会真正被解释执行
alert(sum(10,10));//20
function sum (num1, num2) {
    return num1 + num2;
}
alert(sum(10,10));//报错
var sum = function(num1, num2){//在把函数当成值来使用的情况下,都可以使用匿名函数。不过,这并不是匿名函数唯一的用途。
    return num1 + num2;
};
  • 块级作用域与函数声明(es6) 
//源代码
function f() { console.log('I am outside!'); }
(function () {
  if (false) {
    // 重复声明一次函数f
    function f() { console.log('I am inside!'); }
  }
  f();
}());

// ES5 环境
function f() { console.log('I am outside!'); }
(function () {
  function f() { console.log('I am inside!'); }
  if (false) {
  }
  f();//“I am inside!”:函数f会被提升到函数头部
}());

// 浏览器的 ES6 环境
function f() { console.log('I am outside!'); }
(function () {
  var f = undefined;
  if (false) {
    function f() { console.log('I am inside!'); }
  }
  f();// Uncaught TypeError: f is not a function:允许在块级作用域内声明函数,会提升到全局作用域或函数作用域、块级作用域的头部
}());

// 块级作用域内部,优先使用函数表达式
{
  let a = 'secret';
  let f = function () {
    return a;
  };
}

1.2返回值

  • 位于return 语句之后的任何代码都永远不会执行
  • return 语句也可以不带有任何返回值。在这种情况下,函数在停止执行后将返回undefined
function sayHi(name, message) {
    return;
    alert("Hello " + name + "," + message); //永远不会调用
}

1.3 参数默认值(es6)

1.3.1用法

  • 参数默认值是惰性求值
  • 传入的参数严格等于undefined,才会触发该参数等于默认值
function log(x, y = 'World') {//普通函数
  console.log(x, y);
}
log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello

function Point(x = 0, y = 0) {//构造函数
  this.x = x;
  this.y = y;
}
const p = new Point(); // { x: 0, y: 0 }

function fetch(url, { body = '', method = 'GET', headers = {} } = {}) {
  console.log(method);//与解构赋值的默认值结合起来使用
}
fetch('http://example.com')// "GET":没有第二个参数时,函数参数的默认值就会生效,然后才是解构赋值的默认值生效

let x = 99;
function foo(p = x + 1) {
  console.log(p);
}
foo() // 100:参数默认值不是传值的,而是每次都重新计算默认值表达式的值。也就是说,参数默认值是惰性求值的。
x = 100;
foo() // 101

function foo(x = 5, y = 6) {
  console.log(x, y);
}

foo(undefined, null)// 5 null:如果传入undefined,将触发该参数等于默认值,null则没有这个效果

[1, undefined, 3].map((x = 'yes') => x);
// [ 1, 'yes', 3 ]:undefined就会触发函数参数的默认值。

 1.3.2 作用域

一旦设置了参数的默认值,函数进行声明初始化时,参数会形成一个单独的作用域(context)。等到初始化结束,这个作用域就会消失。

var x = 1;
function f(x, y = x) {
  console.log(y);
}
f(2) // 2:调用函数f时,参数形成一个单独的作用域。在这个作用域里面,默认值变量x指向第一个参数x

let x = 1;
function f(y = x) {
  let x = 2;
  console.log(y);
}
f() // 1:函数f调用时,参数y = x形成一个单独的作用域。这个作用域里面,变量x本身没有定义,所以指向外层的全局变量x

1.3.3  尾逗号(es6)

ES2017 允许函数的最后一个参数有尾逗号

function clownsEverywhere(//定义
  param1,
  param2,
) { /* ... */ }

clownsEverywhere(//调用
  'foo',
  'bar',
);

1.4 严格模式(es6)

1.4.1 规则

只要函数参数使用了默认值、解构赋值、或者扩展运算符,那么函数内部不能显式设定为严格模式,否则会报错。

// 报错
function doSomething(a, b = a) {
  'use strict';
  // code
}

// 报错
const doSomething = function ({a, b}) {
  'use strict';
  // code
};

// 报错
const doSomething = (...a) => {
  'use strict';
  // code
};

const obj = {
  // 报错
  doSomething({a, b}) {
    'use strict';
    // code
  }
};

1.4.2 解决

  •  设定全局性的严格模式
  • 把函数包在一个无参数的立即执行函数里面
'use strict';
function doSomething(a, b = a) {
  // code
}

const doSomething = (function () {
  'use strict';
  return function(value = 42) {
    return value;
  };
}());

1.4  尾调用(Safari)

指某个函数的最后一步是调用另一个函数。

function f(x){
  return g(x);
}

2 内部属性 

2.1 arguments

  • 参数在内部是用一个数组来表示的,在函数体内可通过arguments 对象来访问参数数组。        
  • arguments 对象可以与命名参数一起使用,它们的内存空间是独立的,但它们的值会同步。没有传递值的命名参数将自动被赋予undefined 值。
  • 这个对象还有一个名叫 callee 的属性,该属性是一个指针,指向拥有这个 arguments 对象的函数
function sayHi() {
    alert("Hello " + arguments[0] + "," + arguments[1]);//方括号语法访问每一个元素
    alert(arguments.length);//length访问参数个数
}

//递归函数:在一个函数通过名字调用自身的情况下构成的(非严格)
function factorial(num){ 
    if (num <=1) { 
        return 1; 
    } else { //消除函数耦合
        return num * arguments.callee(num-1) //等价于return num * factorial(num-1)
    } 
}

//严格模式
var factorial = (function f(num){ 
    if (num <= 1){ 
        return 1; 
    } else { 
        return num * f(num-1); //码创建一个名为 f()的命名函数表达式,然后将它赋值给变量factorial
    } 
});

2.2 this

  • 隐式绑定:this 引用的是函数据以执行的环境对象
  • 显式绑定:通过 call/apply/bind 修改 this 指向
  • new绑定: 实例对象
  • 箭头函数:它的 this 是通过作用域链查到外层作用域的 this ,且指向函数定义时的 this 而非执行时
window.color = "red"; 
var o = { color: "blue" }; 
function sayColor(){ 
    alert(this.color); 
} 
sayColor(); //"red" 
o.sayColor = sayColor; 
o.sayColor(); //"blue"

2.3  rest参数(es6)

rest 参数(形式为...变量名),用于获取函数的多余参数。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。

function add(...values) {
  let sum = 0;
  for (var val of values) {
    sum += val;
  }
  return sum;
}
add(2, 5, 3) // 10

// 报错:rest 参数之后不能再有其他参数
function f(a, ...b, c) {
  // ...
}

// arguments变量的写法:使用Array.from先将其转为数组
function sortNumbers() {
  return Array.from(arguments).sort();
}

// rest参数的写法:rest 参数是一个真正的数组
const sortNumbers = (...numbers) => numbers.sort();

2.4 name属性(es6)

 函数的name属性,返回该函数的函数名。

var f = function () {};//将一个匿名函数赋值给一个变量
// ES5
f.name // ""
// ES6
f.name // "f"

const bar = function baz() {};//将一个具名函数赋值给一个变量
// ES5
bar.name // "baz"
// ES6
bar.name // "baz"

(new Function).name // "anonymous":Function构造函数返回的函数实例

function foo() {};
foo.bind({}).name // "bound foo":bind返回的函数,name属性值会加上bound前缀。

函数属性和方法

3.1 属性

  • length 属性表示函数希望接收的命名参数的个数。将返回没有指定默认值的参数个数,不包括 rest 参数。(es6)
  • prototype 是保存它们所有实例方法的真正所在,prototype 属性是不可枚举的,因此使用 for-in 无法发现
(function (a, b, c = 5) {}).length // 2
(function (a = 0, b, c) {}).length // 0:如果设置了默认值的参数不是尾参数,那么length属性也不再计入后面的参数了
(function(...a) {}).length  // 0
(function(a, ...b) {}).length  // 1

3.2 方法

  • call()和apply()第一个参数是函数的执行上下文,call()第二个参数后面传入多个参数,而apply传入数组。会立即执行。能够扩充函数赖以运行的作用域
  • bind()参数同call。返回执行上下文被改变的函数,不会立即执行
  • toString()返回函数代码本身,以前会省略注释和空格。修改后,返回一模一样的原始代码。(es6)
  • valueOf() 返回函数的代码。
    window.color = "red"; 
    var o = { color: "blue" }; 
    function sayColor(){ 
        alert(this.color); 
    } 
    sayColor(); //red 
    sayColor.call(this); //red 
    sayColor.call(window); //red 
    sayColor.call(o); //立即调用:blue
    var objectSayColor = sayColor.bind(o); 
    objectSayColor(); //没有立即调用:blue
    function sum(num1, num2){ 
        return num1 + num2; 
    } 
    function callSum(num1, num2){ 
        return sum.call(this, num1, num2); //call,传递给函数的参数必须逐个列举出来
    } 
    alert(callSum(10,10)); //20
    
    function sum(num1, num2){ 
        return num1 + num2; 
    } 
    function callSum2(num1, num2){ 
        return sum.apply(this, [num1, num2]); //apply,传入数组
    } 
    alert(callSum2(10,10)); //20
    function /* foo comment */ foo () {}
    foo.toString()
    // function foo() {}:es5
    
    function /* foo comment */ foo () {}
    foo.toString()
    // "function /* foo comment */ foo () {}":es6

    4 函数类型

    4.1普通函数

    调用方式:直接调用

    作用:创建对象需要return

    this:window,内部的this指向函数运行时所在的对象

    // 作为普通函数调用
    Person("Greg", 27, "Doctor"); // 添加到window
    window.sayName(); //"Greg"
    
    // 在另一个对象的作用域中调用
    var o = new Object();
    Person.call(o, "Kristen", 25, "Nurse");
    o.sayName(); //"Kristen":这里是在对象o 的作用域中调用的,调用后o 就拥有了所有属性sayName()方法

    4.2 构造函数 

    调用方式:new 操作符来调用

    作用:创建实例对象无需return

    this:实例对象

    // 当作构造函数使用
    var person = new Person("Nicholas", 29, "Software Engineer");
    person.sayName(); //"Nicholas"

    4.3 箭头函数

    4.3.1 基本用法

    var f = v => v;//一个参数不加圆括号,没有花括号无需return
    // 等同于
    var f = function (v) {
      return v;
    };
    
    var sum = (num1, num2) =>{ return num1 + num2};//多个参数加圆括号,多条语句加花括号需return
    // 等同于
    var sum = function(num1, num2) {
      return num1 + num2;
    };
    
    let getTempItem = id => ({ id: id, name: "Temp" });//直接返回一个对象,必须在对象外面加上括号
    
    

    4.3.2 注意点

    • 箭头函数没有自己的this对象,内部的this就是定义时上层作用域中的this
    • 不可以对箭头函数使用new命令,不可以使用arguments对象。
    • 箭头函数没有自己的this,所以当然也就不能用call()apply()bind()这些方法去改变this的指向
    var handler = {
      id: '123456',
    
      init: function() {//箭头函数里面的this,总是指向handler对象。如果回调函数是普通函数,此时this指向document对象。
        document.addEventListener('click',
          event => this.doSomething(event.type), false);
      },
    
      doSomething: function(type) {
        console.log('Handling ' + type  + ' for ' + this.id);
      }
    };
    
    const cat = {//对象的属性建议使用传统的写法定义,不要用箭头函数定义
      lives: 9,
      jumps: () => {//普通函数,该方法内部的this指向cat;箭头函数,使得this指向全局对象
        this.lives--;
      }
    }
    
    var button = document.getElementById('press');
    button.addEventListener('click', () => {//需要动态this的时候,也不应使用箭头函数
      this.classList.toggle('on');
    });
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值