this题目
以下代码中有几个this?
function foo() {
return () => {
return () => {
return () => {
console.log('id:', this.id);
};
};
};
}
var f = foo.call({id: 1});
var t1 = f.call({id: 2})()(); // id: 1
var t2 = f().call({id: 3})(); // id: 1
var t3 = f()().call({id: 4}); // id: 1
上面代码之中,只有一个this,就是函数foo的this,所以t1、t2、t3都输出同样的结果。因为所有的内层函数都是箭头函数,都没有自己的this,它们的this其实都是最外层foo函数的this。
优缺点
由于箭头函数在定义的时候被绑定,下面两个场合不应该使用箭头函数。
const cat = {
lives: 9,
jumps: () => {
console.log("this是什么", this);
this.lives--;
},
};
cat.jumps();
可以发现this是window对象。
如果是普通函数,该方法内部的this指向cat;如果写成上面那样的箭头函数,使得this指向全局对象,因此不会得到预期结果。这是因为对象不构成单独的作用域,导致jumps箭头函数定义时的作用域就是全局作用域。
这和我们之前举的例子的不同:
var handler = {
id: "123456",
init: function () {
document.addEventListener(
"click",
(event) => this.fun(event.type),
false
);
},
fun: function (type) {
console.log("this指向谁", this);
},
arrow: () => {
console.log("我是箭头函数,this指向谁", this);
},
};
handler.init();
handler.arrow();
在之前的例子中,箭头函数的this实际上是外层函数的this,他的外层函数是谁呢?(也就是init)的this。而init是一个普通函数,所以谁调用它,this就指向谁。所以this就是handler对象。
第二个场合是需要动态this的时候,也不应使用箭头函数。
var button = document.getElementById('button');
button.addEventListener('click', () => {
this.classList.add('on');
});
上面代码运行时,点击按钮会报错,因为button的监听函数是一个箭头函数,箭头函数不会绑定自己的 this 值,它会捕获其所在上下文的 this 值。如果你使用箭头函数指向上一级上下文的 this,而上一层的this就是全局对象window。window没classList属性,所以自然会报错。我们希望this指向dom对象,也就是这个按钮,此时必须使用普通函数。
优点
在函数有多重嵌套时,可以使得函数简洁易懂
function insert(value) {
return {into: function (array) {
return {after: function (afterValue) {
array.splice(array.indexOf(afterValue) + 1, 0, value);
return array;
}};
}};
}
insert(2).into([1, 3]).after(1); //[1, 2, 3]
如果用箭头函数改写:
let insert = (value) => ({into: (array) => ({after: (afterValue) => {
array.splice(array.indexOf(afterValue) + 1, 0, value);
return array;
}})});
insert(2).into([1, 3]).after(1); //[1, 2, 3]
其他特性
除了this,以下三个变量在箭头函数之中也是不存在的,指向外层函数的对应变量:arguments、super、new.target。
function foo() {
setTimeout(() => {
console.log('args:', arguments);
}, 100);
}
foo(2, 4, 6, 8)
// args: [2, 4, 6, 8]
上面代码中,箭头函数内部的变量arguments,其实是函数foo的arguments变量。
super的作用
在 JavaScript 中,super 是一个关键字,用于在子类中调用父类的方法或访问父类的属性。它主要用于继承和类之间的关系,使子类能够重用和扩展父类的功能。
在使用 super 时,通常有两个主要用途:
调用父类的构造函数:在子类的构造函数中使用 super() 可以调用父类的构造函数,从而初始化父类的属性。这是确保子类实例拥有父类属性的一种方式。
访问父类的方法:在子类的方法中,可以使用 super 关键字调用父类中的同名方法,以便在子类中扩展父类的方法。
以下是 super 关键字的两种用法的示例:
1.调用父类的构造函数:
class Animal {
constructor(name) {
this.name = name;
}
makeSound() {
console.log("Some generic sound");
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name); // 调用父类的构造函数来初始化父类属性
this.breed = breed;
}
makeSound() {
super.makeSound(); // 调用父类的方法
console.log("Woof! Woof!");
}
}
const myDog = new Dog("Buddy", "Golden Retriever");
console.log(myDog.name); // 输出: Buddy
console.log(myDog.breed); // 输出: Golden Retriever
myDog.makeSound(); // 输出: Some generic sound Woof! Woof!
在上面的例子中,Dog 是 Animal 的子类。在 Dog 类的构造函数中,使用 super(name) 来调用父类 Animal 的构造函数,确保在创建 Dog 实例时会初始化 name 属性。在 Dog 类的 makeSound() 方法中,使用 super.makeSound() 来调用父类 Animal 的 makeSound() 方法,以便在子类中扩展父类的功能。
2.访问父类方法
class Vehicle {
constructor(type) {
this.type = type;
}
start() {
console.log(`Starting ${this.type}`);
}
}
class Car extends Vehicle {
constructor(type, brand) {
super(type);
this.brand = brand;
}
start() {
super.start(); // 调用父类的方法
console.log(`Driving the ${this.brand} car`);
}
}
const myCar = new Car("car", "Toyota");
myCar.start(); // 输出: Starting car Driving the Toyota car
在上面的例子中,Car 是 Vehicle 的子类。在 Car 类的 start() 方法中,使用 super.start() 来调用父类 Vehicle 的 start() 方法,以便在子类中扩展父类的方法。
总结:super 是用于在 JavaScript 类中访问和调用父类方法和构造函数的关键字,它在继承和类之间的关系中起着重要的作用。
如果不用super会放生什么?
https://www.imqianduan.com/javascript/405.html
什么是 new.target?
https://blog.csdn.net/Hoshizola/article/details/119462690