灏天阁

函数

· Yin灏

参数的默认值

参数默认值表达式

function defaultValue() {
  return 1;
}

function foo(x = defaultValue()) {
  console.log(x);
}

foo(); // 1

foo(undefined) // 1

使用参数作为默认值

  • 还可以使用前一个参数的值作为默认值。
function foo(x, y = x) {
  console.log(x, y);
}

foo(2); // 2 2

使用参数值作为默认值表达式的参数

function defaultValue(x) {
    return x + 3;
}

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

foo(1);

剩余参数

ES6 之前通过 arguments 来访问。

function foo(x) {
  console.log(arguments[1]);
  console.log(arguments[2]);
  console.log(arguments);
  console.log(arguments.callee);
  console.log(arguments.length);
}
foo(1, 2, 3); // 2 

/*
    ƒ foo(x) {
      console.log(arguments[1]);
      console.log(arguments[2]);
      console.log(arguments);
      console.log(arguments.callee);
      console.log(arguments.length);
    }
*/

ES6 中,通过剩余项来获取参数

function foo(x, ...args) {
  console.log(args);
}
foo(1,2,3); // [2, 3]

name属性

声明函数

对于声明函数,它的名称就是函数的名称。

function foo() {
    console.log(foo.name); // 'foo'
}

foo();

函数表达式

对于函数表达式,函数的名称就是变量的名称

let foo = function() {
    console.log(foo.name); // 'foo'
}

foo();

对象的方法

对于对象的方法,会以方法名作为名称,比较特殊的是 getter 和 setter 方法,会为他们加上 get 和 set 来标记和区分方法是 getter 还是 setter 方法。

let obj = {
    get color() {},
    set color(color) {},
    method() {}
};

console.log(Object.getOwnPropertyDescriptor(obj, 'color').get.name); // 'get color'
console.log(Object.getOwnPropertyDescriptor(obj, 'color').set.name); // 'set color'
console.log(obj.method.name); // 'method'

bind 方法创建的函数

对于使用 bind 方法创建的函数,会在方法名前添加 bound 作为名称。

let fn = function() {};
console.log(fn.bind().name); // 'bound fn'

new Function 创建的函数

对于 new Function 方式创建的函数,它的名称是 anonymous(匿名的)

console.log(new Function().name); // 'anonymous'

实例

对于实例,可以使用构造函数来检查名称。

function Foo() {}

let foo = new Foo();

console.log(foo.constructor.name); // "Foo"

如果想通过构造函数的名称来判断是否创建可某个类,一定要注意,经过脚本压缩后,类名 Foo 可能会变成 a,这样构造函数的名称就变成了 a,而不是 Foo,这样代码可能就会出问题。

new.target 属性

new.target 只能在实例对象中有用。

要检查函数或构造方法是否是通过 new 运算符调用过的,在 ES6 之前,最常用的方式使用 instanceof 运算符来实现,但是该方法有时候会失灵。

function Foo() {
    // this = new Foo()  是实例化之后的对象
    console.log(this instanceof Foo);
}

let foo = new Foo(); // true

Foo.call(foo); // true 改变 this 的指向

为了解决这个问题,在 ES6 中添加了 new.target 属性:

function Foo() {
    console.log(new.target);
}

let foo = new Foo(); // [Function: Foo]

Foo.call(foo); // undefined

console.log([] instanceof Array); // true
console.log([] instanceof Object);  // true 这里要注意
console.log({} instanceof Object); // true

从结果看,new.target 属性可以很清楚的区分函数或构造方法是否通过 new 元素夫来调用的。

箭头函数

不绑定 this

不使用箭头函数定义的函数都会有它的自己的 this 值,但是 this 的值稍不注意就会出现错误。

class Counter {
    constructor() {
        this.counter = 1;
        setTimeout(function() { // 匿名函数中的 this 指向的是 window
            console.log(this); // window
            this.counter++;
            console.log(this.counter);
        }, 1000)
    }
}

let counter = new Counter(); // NaN

要修正以上错误,将 this 对象转换为本地变量就行了,

class Counter {
    constructor() {
        let me = this;
        console.log(this, typeof this); // 实例对象 counter, 'object'
        me.counter = 1;
        setTimeout(function() {
            // console.log(this); // window 匿名函数中的 this 指向 window
            me.counter++;
            console.log(me.counter);
        }, 1000)
    }
}

let counter = new Counter(); // 2

还有一种方式就是使用 bind 方法将 this 绑定到函数,如下:

class Counter {
    constructor() {
        this.counter = 1;
        setTimeout(function() {
            this.counter++;
            console.log(this.counter);
        }.bind(this), 1000)
    }
}

let counter = new Counter(); // 2

但是将回调函数替换为箭头函数,就不会出现这种问题,因为它会从自己的作用域链的上一层继承 this

class Counter {
    constructor() {
        this.counter = 1;
        setTimeout(() => {
            // 箭头函数使用的是外层函数中的 this
            this.counter++;
            console.log(this.counter);
        }, 1000)
    }
}

let counter = new Counter(); // 2

由于箭头函数继承了上一层的 this 指针,因此使用 call 或 apply 方法调用箭头函数并传递 this 参数时,是没有效果的。

没有 arguments 对象

在箭头函数内是没有 arguments 对象的,因此不要尝试在箭头函数内使用 arguments 对象来获取参数,如果要获取未知参数,要使用剩余参数来获取。

let fn = (x, ...y) => console.log(y[0], y[1]);
fn(1, 2, 3, 4); // 2 3

不能作为构造函数

由于箭头函数不能作为构造器,因此不能使用 new 操作符来调用,不然会抛出错误。

没有原型

箭头函数是没有原型的,

let fn = () => {};
console.log(fn.prototype); // undefined
let fn = () => {}
console.log(fn.prototype); // undefined


let fn1 = function() {}
console.log(fn1.prototype); // {constructor: ƒ}

不能作为生成器

由于在箭头函数中不能使用 yield 关键字,因而不能将箭头函数作为生成器使用。

- Book Lists -