前端面试题每日一学_18

今日一问:

下面的JS代码执行后,最终输出的结果是什么?
 function Person(name) {
     this.name = name;
     this.getName = function() {
       return this.name;
    };
 }
 
 const person1 = new Person('Alice');
 const person2 = { name: 'Bob' };
 person2.getName = person1.getName;
 console.log(person2.getName());

​ 答案和解析可在文章底部查看。

今日面试题:

1、JS中调用构造函数时,new操作符具体做了哪些操作?

① 创建新的空对象

​ JS引擎首先会创建一个新的空对象,作为构造函数创建的实例。这个对象的类型是由构造函数决定的。

② 设置新对象的原型属性

​ 新对象的内部原型[[Prototype]](浏览器环境中通过__proto__属性访问)属性会被设置为构造函数的prototype属性的值。这意味着新对象可以访问到构造函数原型上的属性和方法。

③ 绑定 this 关键字

​ 在构造函数内部,将this关键字指向新对象,从而可以通过 操作this 向新对象添加属性和方法。

④ 执行构造函数

​ 执行构造函数中的代码,初始化新对象的属性和方法,包括赋值语句、函数调用等等。

⑤ 返回对象

​ 如果构造函数中显式的返回了一个对象类型(Object,包含对象、数组等,以及一种特殊的对象类型-函数)的值,则将该值作为new操作符的最终结果;如果构造函数显式的返回了一个非对象类型的(字符串、数字等)值或者干脆没有返回值,则会将新对象作为new操作符的最终结果。

案例代码:

function Person(name) {
    this.name = name;
    // 返回了一个非对象类型的值
    return '30';
}
// p的值是一个有name属性的Person对象
let p = new Person("John");

function Person2(name) {
    this.name = name;
    // 返回了一个对象类型的值
    return {age: 30};
}
// p2的值是{age: 30},而不是一个有name属性的Person2对象
let p2 = new Person2("John");

2、请用JS模拟 new 操作符的实现

// 一个构造函数
function Person(name) {
    this.name = name;
    this.sayHello = function() {
    	console.log(`Hello, my name is ${this.name}`);
  	};
}
// 模拟new操作符的实现
// constructor-表示构造函数 args-表示要传递的参数 
function myNew(constructor, ...args) {
    // 1、创建一个新的空对象
    const obj = {};
    // 2、设置新对象的原型属性
    Object.setPrototypeOf(obj, constructor.prototype);
    // 3、绑定 this 关键字 并执行构造函数
    const res = constructor.apply(obj, args);
    // 4、根据构造函数的返回值 决定new操作的返回值
    // 如果构造函数的返回值是对象类型 则返回该对象 否则返回创建的新对象
    if(res!==null && (typeof res==="object" || typeof res==="function")) {
        return res;
    } else {
        return obj;
    }
}

// 测试自定义的new函数
const p = myNew(Person, 'Bob');
p.sayHello(); // 输出:Hello, my name is Bob

3、前端异步加载JS的方法有哪些?

① async

​ 在<script>标签中添加async属性,浏览器会在解析HTML文档的同时异步下载引用的JS文件,并在文件下载完成后,立即执行。

<script async src="***/***/***/test.js"></script>
② defer

​ 在<script>标签中添加defer属性,浏览器会在解析HTML文档的同时异步下载引用的JS文件,文件下载完成后,会继续等待DOM解析完成,再执行。

<script defer src="***/***/test.js"></script>
③ 动态创建脚本元素

​ 在JS中,通过在特定的时机动态创建<script>元素,设置src属性引用JS文件,并添加到页面中,此操作类似于设置async属性,浏览器会在解析HTML文档的同时异步下载引用的JS文件,并在文件下载完成后,立即执行。

<script>
  // 创建异步脚本
  const script = document.createElement("script");
  script.src = "***/***/test.js";
  document.body.appendChild(script);
</script>
④ XMLHttpRequest API 或 fetch API

​ 通过XMLHttpRequestfetch发起ajax请求,异步获取JS脚本内容,然后通过eval()或动态函数来执行脚本内容。

注: eval()存在很大的安全风险,更推荐通过动态函数来执行脚本。

<script>
  // 发起fetch请求
  fetch('***/***/test.js')
  .then(response => response.text())
  .then(code => {
    // 不推荐
    // eval(code);
    // 推荐
    const scriptFunction = new Function(code);
    scriptFunction();
  });
</script>

⑤ import 动态导入

​ 在ES2020中,引入了import()动态导入的功能,它会返回一个Promise。当遇到import()导入JS的语句时,浏览器会异步的加载指定的JS脚本,并在加载和解析完成后,将结果传递给then方法中的回调函数。

<script>
 import('***/***/test.js').then(module => {
    // 使用导入的模块
    module.myFunction();
 }).catch(err => {
    console.error('模块加载失败:', err);
 });
</script>
⑥ 模块化加载

​ 使用ES6的模块化,在 <script> 标签中添加 type="module"属性,浏览器会在解析HTML文档的同时异步下载引用的JS文件,并且会等待DOM解析完成后再执行,类似于defer属性。

​ 在模块内部,如果存在import语句来导入其他模块,浏览器会同样以异步方式去加载这些依赖模块。这个过程是递归的,浏览器会先解析和加载最底层的依赖模块,然后逐步向上执行。只有当一个模块的所有依赖都加载完成后,这个模块才会被执行。

<script type="module" src="***/***/test.js"></script>
⑦ Web Worker

​ 严格来说,Web Worker 本身不是专门用于异步加载 JavaScript 的技术,但可以通过它来间接实现类似的效果。主线程先创建一个 Web Worker,然后让它在浏览器后台线程中加载和执行JS脚本,不会阻塞主线程。但是Web Worker 无法直接操作DOM,需要借助消息传递机制来间接操作DOM。

今日一问答案:‘Bob’

解析:

​ 这是一道考察JS中this指向的问题,我在之前的文章中仔细讲过相关内容,感兴趣的同学可以去翻翻看一下。

​ 代码结构很简单,首先声明了一个Person的构造函数,包含一个name属性和一个getName方法,getName方法中返回了this.name的值。然后通过new构造了一个实例对象person1,并设置name属性值为Alice。又通过字面量创建了一个对象person2,并且也设置一个name属性,属性值为Bob。再然后给person2对象设置一个getName方法,使其指向person1getName方法。最后调用person2.getName(),问输出结果是什么。

​ 对于this指向这一类问题,简单来说,遵循下面的原则即可:this 的指向取决于函数的调用者(箭头函数除外)。在当前代码中,getName()方法是一个普通函数(非箭头函数),因此虽然person2getName()方法指向的是person1getName()方法,但最终函数的调用者,是person2对象。所以函数中的 this指向person2this.name访问到的是person2.name,也就是Bob。最终,输出的结果为Bob

请关注公众号,优先查看更多优质资源:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

努力的小朱同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值