js关于异步、微任务、原型链笔试题

文章详细解释了JavaScript中的宏任务和微任务执行顺序,Promise的工作原理,以及如何通过构造函数和原型链实现对象的继承。特别关注了setTimeout、Promise.then()和原型链中的this指向问题。
摘要由CSDN通过智能技术生成

2024年6月7日 太羞耻了,被原原本本问了这些问题,答不上来,答错了、。。。面试前一定会这些内容好吗
——————————————
之前遇到了就草草看过,却没有真的理解,过一段时间就忘记了,故做一个系统整理
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

宏任务、微任务

1.输出顺序
setTimeout(function () {
    console.log(1)}, 0);
new Promise(function executor(resolve) {
    console.log(2);
    for (var i = 0; i < 10000; i++) {
        i == 9999 && resolve();
     }
    console.log(3); 
}).then(function () {
    console.log(4); 
});
console.log(5);

解答:
在这段代码中,输出顺序如下:

  1. console.log(2);
  2. console.log(3);
  3. console.log(5);
  4. console.log(4);
  5. console.log(1);

这是因为在 JavaScript 中,Promise 中的 executor 函数是同步执行的,而 then() 方法中的回调函数是异步执行的。因此,先输出 Promise 中的内容,然后输出 Promise 的 then() 方法中的内容,最后输出 setTimeout 中的内容。

2.输出顺序
console.log('1. Start');

setTimeout(() => {
  console.log('2. setTimeout 1');
}, 0);

Promise.resolve().then(() => {
  console.log('3. Promise 1');
  setTimeout(() => {
    console.log('4. setTimeout 2');
  }, 0);
});

Promise.resolve().then(() => {
  console.log('5. Promise 2');
});
console.log('6. End');

解答:

1. Start
6. End
3. Promise 1
5. Promise 2
2. setTimeout 1
4. setTimeout 2
3.宏任务、微任务有哪些(海康威视)

宏任务(macrotasks) 包括:

  1. 整体代码块(script)
  2. setTimeout
  3. setInterval
  4. I/O 操作
  5. UI 渲染

微任务(microtasks) 包括:

  1. Promise 的回调函数(.then())
  2. MutationObserver
  3. process.nextTick (Node.js 环境)
4.输出顺序
async function foo() {
  console.log('foo');
  return Promise.resolve('resolved');
}

async function bar() {
  console.log('bar');
  return foo();
}

bar().then(res => {
  console.log(res);
});

console.log('end');

打印结果:

bar
foo
end
resolved

原型链

1.alert输出结果
var color = 'green';
var test4399 = {
    color: 'blue',
    getColor: function(){
        var color = "red";
        alert(this.color);
    }
}
var getColor = test4399.getColor;
getColor();
test4399.getColor();

解答:
当一个函数作为对象的方法调用时,this 指向调用该方法的对象。
getColor(); :
由于 this 指向全局对象 window,所以 alert(this.color); 显示的会是 ‘green’。
test4399.getColor(); :
由于 this 指向 test4399,因此 alert(this.color); 显示的会是 ‘blue’。

2.现有如下代码:function Person(){};const person=new Person();如下正确的选项是

A.person.__ proto__===Person.prototype 结果是:true

B.person.__ proto__.constructor===Person.prototype.constructor 结果是:true

C.person.constructor===Person 结果是:true

D.person.constructor===Person.constructor 结果是:tue

E.Person.prototype.constructor===person.constructor 结果是true

解答:
ABCE

  • person.constructor === Person
  • person._ proto _ === Person.prototype
  • Person.prototype.constructor === Person
  • A:person 实例的原型指向 Person 构造函数的原型,所以结果为 true
  • B:person 实例的原型的构造函数指向 Person 构造函数的原型的构造函数,也就是 Person.prototype.constructor,所以结果为 true
  • C:person 实例的构造函数指向 Person 构造函数本身,所以结果为 true
  • D:构造函数没有constructor属性,constructor属性存在于原型对象或实例对象上。因此,你不能直接通过构造函数来访问constructor属性。
  • E:Person.prototype.constructor 指向 Person 构造函数本身,而 person.constructor 指向 Person 构造函数本身,所以结果为 true

在 JavaScript 中,每个对象都有一个原型对象和一个构造函数。下面是它们的定义:

  1. 构造函数:构造函数是用来创建对象的函数。当使用关键字 new 来调用构造函数时,会创建一个新的对象实例并将其返回。构造函数通常用大写字母开头来区分普通函数。
function Person(name) {
    this.name = name;
}

const person = new Person('Alice');
  1. 原型对象:每个函数都有一个 prototype 属性,这个属性指向一个对象,这个对象就是原型对象。原型对象包含共享的属性和方法,可以被该函数创建的所有对象实例访问。
Person.prototype.sayHello = function() {
    console.log('Hello, my name is ' + this.name);
};
  1. 实例对象:实例对象是通过构造函数创建的对象。它继承了构造函数的原型对象的属性和方法,并可以访问这些属性和方法。
const person = new Person('Alice');
person.sayHello(); // 输出:Hello, my name is Alice

总结:构造函数用于创建对象实例,每个构造函数都有一个原型对象,实例对象可以访问构造函数原型对象中的属性和方法。

3.输出结果
function Animal(name) {
  this.name = name;
}

Animal.prototype.getName = function() {
  return this.name;
};

function Dog(name, breed) {
  Animal.call(this, name);
  this.breed = breed;
}

Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;

Dog.prototype.getBreed = function() {
  return this.breed;
};

const dog1 = new Dog("Buddy", "Golden Retriever");

console.log(dog1.getName()); // 输出1
console.log(dog1.getBreed()); // 输出2

const logName = dog1.getName;
console.log(logName()); // 输出3

const logBreed = dog1.getBreed.bind(dog1);
console.log(logBreed()); // 输出4

解答:
在给定的代码中,定义了 Animal 和 Dog 两个构造函数,并设置了它们的原型链关系。接着创建了一个名为 dog1 的 Dog 实例,并调用了其方法。

根据代码逐行分析,输出结果如下:

  1. console.log(dog1.getName()); // 输出1

    • 输出为 “Buddy”,因为 dog1 是一个 Dog 实例,继承了 Animal 的 getName 方法,返回实例的 name 属性值 “Buddy”。
  2. console.log(dog1.getBreed()); // 输出2

    • 输出为 “Golden Retriever”,因为 dog1 是一个 Dog 实例,具有自己的 breed 属性,getBreed 方法返回该属性值。
  3. const logName = dog1.getName; console.log(logName()); // 输出3

    • 输出为 undefined,因为在这里将 getName 方法赋值给 logName 时,实际上是将该方法提取出来,与 dog1 实例的上下文关联丢失,导致 this 指向丢失,所以返回 undefined。
  4. const logBreed = dog1.getBreed.bind(dog1); console.log(logBreed()); // 输出4

    • 输出为 “Golden Retriever”,通过使用 bind 方法将 dog1 实例绑定到 getBreed 方法中,确保了方法执行时 this 指向正确的对象,因此返回 “Golden Retriever”。

因此,四个输出分别是:
5. Buddy
6. Golden Retriever
7. undefined
8. Golden Retriever

在这段代码中,Animal.call(this, name);的作用是调用Animal构造函数,并将this指向Dog构造函数内部创建的对象,同时传入name作为参数。

具体解释如下:

  • Animal.call(this, name);: 这行代码实际上是在Dog构造函数内部调用Animal构造函数。通过使用call方法,我们可以指定函数内部的this指向哪个对象,这里我们将this指向了Dog构造函数内部创建的对象,以确保Animal构造函数中的属性和方法能够正确地被继承。

  • name: 这是作为参数传递给Animal构造函数的值,用来初始化Animal对象的name属性。通过将name参数传递给Animal构造函数,我们可以在创建Dog对象时同时初始化Animal对象的属性。

总的来说,Animal.call(this, name);这段代码的作用是在Dog构造函数内部调用Animal构造函数,并将this指向Dog对象,以实现一种继承关系,确保Dog对象能够正确地继承Animal对象的属性和方法。

这段代码中的两行:

Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;

是实现继承的关键部分。让我们来解释一下:

  1. Dog.prototype = Object.create(Animal.prototype);: 这一行代码创建了一个新的对象,这个新对象的原型是Animal.prototype。这样一来,Dog.prototype就继承了Animal.prototype上的方法和属性。这是一种常见的原型继承方式,通过创建一个空的中间对象来实现继承。初次之外我们还会将Dog的原型指向一个新创建的Animal实例Dog.prototype = new Animal();Dog.prototype.constructor = Dog;来实现继承。

  2. Dog.prototype.constructor = Dog;: 这一行代码将Dog.prototype对象的constructor属性指向Dog构造函数本身。在上一行代码中,我们将Dog.prototype设置为一个新对象,这个新对象不再具有默认的constructor属性,因此我们需要手动将constructor属性指回Dog构造函数,以确保对象的constructor属性指向正确的构造函数。

关于const logBreed = dog1.getBreed.bind(dog1);这行代码的意思是,它创建了一个新的函数logBreed,这个新函数是dog1.getBreed的一个绑定版本,其中this被永久绑定到dog1对象。这样,在调用logBreed()时,无论在什么上下文中调用,getBreed()方法中的this都会指向dog1对象。

原型链继承和对象属性方法的继承是不同的概念,它们涉及到JavaScript中的原型机制和继承方式。

  1. 原型链

    • 在JavaScript中,每个对象都有一个指向另一个对象的引用,这个对象就是它的原型。当我们访问一个对象的属性或方法时,如果该对象本身没有这个属性或方法,JavaScript会沿着原型链向上查找,直到找到对应的属性或方法为止。
    • 原型链是由对象之间的原型引用构成的链式结构,通过原型链,子对象可以继承父对象的属性和方法。
  2. 对象属性方法的继承

    • 通过构造函数和原型链的组合,我们可以实现对象之间属性和方法的共享。在JavaScript中,通过在构造函数中调用父类构造函数来继承父类的属性,通过设置子类的原型为父类的实例来继承父类的方法。
    function Dog(name, breed) {
     Animal.call(this, name); // 在子类构造函数中调用父类构造函数
     this.breed = breed;
     }
     
     Dog.prototype = new Animal(); // 设置子类的原型为父类的实例
    
    
    • 对象属性方法的继承主要涉及到构造函数和原型之间的关系,确保子类对象能够正确地继承父类对象的属性和方法。

总的来说,原型链是JavaScript中用来实现对象之间继承关系的机制,而对象属性方法的继承是通过构造函数和原型链的方式来实现对属性和方法的继承。原型链继承和对象属性方法的继承是紧密相关的概念,都是为了实现对象之间的属性和方法的共享和继承。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值