javascript进阶知识11-arguments、new.target与this

131 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第2天,点击查看活动详情

函数内部有三个重要的特殊属性:argumentsthisnew.target.

1、arguments

arguments是一个类数组对象,里面的值是调用函数时传递的参数。这个属性只有使用function关键字定义的才有,也就是使用箭头函数创建的没有。

arguments对象有一个属性叫callee,它是一个指向arguments对象所在函数的指针。它通常用来让函数逻辑与函数名解耦

2、new.target

我们知道对象可以使用new关键字创建,也就是使用构造函数方式创建,也就是任何一个函数都可以实例化一个新对象。但是在使用继承的时候,有时候我们不想让父类可以被实例化(抽象基类),我们只能实例化子类,这时候,我们就可以使用new.target

function Father() {
    if (new.target === Father) {
        throw new Error('cant use Father to create object')
    }
}

new Father()  

//  Error: cant use Father to create object

3、this

下面就是this的关键字this了。this在标准函数和箭头函数中有不同的行为。

在标准函数中,this引用的是把函数当成方法调用的上下文对象,这时候通常称其为this值。

var name = 'window';
let o = {
    name: 'ly'
}

function sayName() {
    console.log(this.name);
}

sayName(); // window

o.sayName = sayName;
o.sayName(); //ly

我们知道var在全局声明的变量,会自动添加到全局对象window上,而在全局上下文中调用sayName(),这时候会输出window,因为this指向的是window,而this.name就相当于window.name。而在把sayName赋值给o对象后再调用o.sayName()this也就指向了o,而this.name也就相当于o.name.

在箭头函数中,this引用的是定义箭头函数的上下文。

var name = 'window';
let o = {
    name: 'ly'
}

sayName = () => {
    console.log(this.name);
}

sayName(); // window

o.sayName = sayName;
o.sayName(); //window

这是因为sayName()在全局上下文中定义的,所以sayName的this就指向了window.哪怕将它赋值给o对象this的值也不会改变。

注意:函数名只不过是保存指针的变量,sayName()函数和o.sayName()是同一个函数,只不过执行的上下文不同。

4、call、apply和bind

call、apply个bind都是会改变函数内部this的值。

apply接收两个参数:函数内this的值和参数数组。第二个参数是Array的实例,也可以是arguments对象。

function sum(a, b) {
    return a + b;
}

function callSum1(a, b) {
    return sum.apply(this, arguments)
}

function callSum2(a, b) {
    return sum.apply(this, [a, b])
}

console.log(callSum1(1, 2)); // 3
console.log(callSum2(1, 2)); // 3

call的使用方法和apply基本一样,唯一的区别就是传参是形式不同,call的第一个参数和apply一样,而剩下的参数就是要传给被调用函数的参数。

function sum(a, b) {
    return a + b;
}

function callSum(a, b) {
    return sum.call(this, a, b)
}

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

bind方法与call和bind的方式略有不同,我们通过上面的例子发现call和apply是立即执行函数,而bind则不是立即执行函数,而是返回一个新函数。

function sum(a, b) {
    return a + b;
}

function callSum(a, b) {
    return sum.bind(this, a, b)
}

let result = callSum(10, 10);
console.log(result()); // 20
console.log(callSum(10, 10)); // [Function: bound sum]

但是call、apply和bind真正厉害的地方其实是改变this值和实现继承。

function add(c, d) {
    return this.a + this.b + c + d;
}

var s = { a: 1, b: 2 };
var ss = { a: 10, b: 20 };

let result = add.bind(ss, 3, 4)
console.log(add.call(s, 3, 4)); // 1+2+3+4 = 10
console.log(add.apply(s, [5, 6])); // 1+2+5+6 = 14
console.log(result()); //10+20+3+4 = 37

我们可以看出,call、apply和bind都改变了函数add中的this的值。