关于前端:阿里前端一面必会面试题合集

什么是 DOM 和 BOM?

  • DOM 指的是文档对象模型,它指的是把文档当做一个对象,这个对象次要定义了解决网页内容的办法和接口。
  • BOM 指的是浏览器对象模型,它指的是把浏览器当做一个对象来看待,这个对象次要定义了与浏览器进行交互的法和接口。BOM的外围是 window,而 window 对象具备双重角色,它既是通过 js 拜访浏览器窗口的一个接口,又是一个 Global(全局)对象。这意味着在网页中定义的任何对象,变量和函数,都作为全局对象的一个属性或者办法存在。window 对象含有 location 对象、navigator 对象、screen 对象等子对象,并且 DOM 的最基本的对象 document 对象也是 BOM 的 window 对象的子对象。

代码输入后果

var myObject = {
    foo: "bar",
    func: function() {
        var self = this;
        console.log(this.foo);  
        console.log(self.foo);  
        (function() {
            console.log(this.foo);  
            console.log(self.foo);  
        }());
    }
};
myObject.func();

输入后果:bar bar undefined bar

解析:

  1. 首先func是由myObject调用的,this指向myObject。又因为var self = this;所以self指向myObject。
  2. 这个立刻执行匿名函数表达式是由window调用的,this指向window 。立刻执行匿名函数的作用域处于myObject.func的作用域中,在这个作用域找不到self变量,沿着作用域链向上查找self变量,找到了指向 myObject对象的self。

代码输入后果

const p1 = new Promise((resolve) => {
  setTimeout(() => {
    resolve('resolve3');
    console.log('timer1')
  }, 0)
  resolve('resovle1');
  resolve('resolve2');
}).then(res => {
  console.log(res)  // resolve1
  setTimeout(() => {
    console.log(p1)
  }, 1000)
}).finally(res => {
  console.log('finally', res)
})

执行后果为如下:

resolve1
finally  undefined
timer1
Promise{<resolved>: undefined}

须要留神的是最初一个定时器打印出的p1其实是.finally的返回值,咱们晓得.finally的返回值如果在没有抛出谬误的状况下默认会是上一个Promise的返回值,而这道题中.finally上一个Promise是.then(),然而这个.then()并没有返回值,所以p1打印进去的Promise的值会是undefined,如果在定时器的上面加上一个return 1,则值就会变成1。

代码输入后果

// a
function Foo () {
 getName = function () {
   console.log(1);
 }
 return this;
}
// b
Foo.getName = function () {
 console.log(2);
}
// c
Foo.prototype.getName = function () {
 console.log(3);
}
// d
var getName = function () {
 console.log(4);
}
// e
function getName () {
 console.log(5);
}

Foo.getName();           // 2
getName();               // 4
Foo().getName();         // 1
getName();               // 1 
new Foo.getName();       // 2
new Foo().getName();     // 3
new new Foo().getName(); // 3

输入后果:2 4 1 1 2 3 3

解析:

  1. Foo.getName(), Foo为一个函数对象,对象都能够有属性,b 处定义Foo的getName属性为函数,输入2;
  2. getName(), 这里看d、e处,d为函数表达式,e为函数申明,两者区别在于变量晋升,函数申明的 5 会被后边函数表达式的 4 笼罩;
  3. Foo().getName(), 这里要看a处,在Foo外部将全局的getName从新赋值为 console.log(1) 的函数,执行Foo()返回 this,这个this指向window,Foo().getName() 即为window.getName(),输入 1;
  4. getName(), 下面3中,全局的getName曾经被从新赋值,所以这里仍然输入 1;
  5. new Foo.getName(), 这里等价于 new (Foo.getName()),先执行 Foo.getName(),输入 2,而后new一个实例;
  6. new Foo().getName(), 这 里等价于 (new Foo()).getName(), 先new一个Foo的实例,再执行这个实例的getName办法,然而这个实例自身没有这个办法,所以去原型链__protot__上边找,实例.protot === Foo.prototype,所以输入 3;
  7. new new Foo().getName(), 这里等价于new (new Foo().getName()),如上述6,先输入 3,而后new 一个 new Foo().getName() 的实例。

抉择排序–工夫复杂度 n^2

题目形容:实现一个抉择排序

实现代码如下:

function selectSort(arr) {
  // 缓存数组长度
  const len = arr.length;
  // 定义 minIndex,缓存以后区间最小值的索引,留神是索引
  let minIndex;
  // i 是以后排序区间的终点
  for (let i = 0; i < len - 1; i++) {
    // 初始化 minIndex 为以后区间第一个元素
    minIndex = i;
    // i、j别离定义以后区间的上下界,i是左边界,j是右边界
    for (let j = i; j < len; j++) {
      // 若 j 处的数据项比以后最小值还要小,则更新最小值索引为 j
      if (arr[j] < arr[minIndex]) {
        minIndex = j;
      }
    }
    // 如果 minIndex 对应元素不是目前的头部元素,则替换两者
    if (minIndex !== i) {
      [arr[i], arr[minIndex]] = [arr[minIndex], arr[i]];
    }
  }
  return arr;
}
// console.log(quickSort([3, 6, 2, 4, 1]));

代码输入后果

var friendName = 'World';
(function() {
  if (typeof friendName === 'undefined') {
    var friendName = 'Jack';
    console.log('Goodbye ' + friendName);
  } else {
    console.log('Hello ' + friendName);
  }
})();

输入后果:Goodbye Jack

咱们晓得,在 JavaScript中, Function 和 var 都会被晋升(变量晋升),所以下面的代码就相当于:

var name = 'World!';
(function () {
    var name;
    if (typeof name === 'undefined') {
        name = 'Jack';
        console.log('Goodbye ' + name);
    } else {
        console.log('Hello ' + name);
    }
})();

这样,答案就高深莫测了。

如何对我的项目中的图片进行优化?

  1. 不必图片。很多时候会应用到很多润饰类图片,其实这类润饰图片齐全能够用 CSS 去代替。
  2. 对于挪动端来说,屏幕宽度就那么点,齐全没有必要去加载原图节约带宽。个别图片都用 CDN 加载,能够计算出适配屏幕的宽度,而后去申请相应裁剪好的图片。
  3. 小图应用 base64 格局
  4. 将多个图标文件整合到一张图片中(雪碧图)
  5. 抉择正确的图片格式:

    • 对于可能显示 WebP 格局的浏览器尽量应用 WebP 格局。因为 WebP 格局具备更好的图像数据压缩算法,能带来更小的图片体积,而且领有肉眼辨认无差别的图像品质,毛病就是兼容性并不好
    • 小图应用 PNG,其实对于大部分图标这类图片,齐全能够应用 SVG 代替
    • 照片应用 JPEG

代码输入后果

console.log('1');

setTimeout(function() {
    console.log('2');
    process.nextTick(function() {
        console.log('3');
    })
    new Promise(function(resolve) {
        console.log('4');
        resolve();
    }).then(function() {
        console.log('5')
    })
})
process.nextTick(function() {
    console.log('6');
})
new Promise(function(resolve) {
    console.log('7');
    resolve();
}).then(function() {
    console.log('8')
})

setTimeout(function() {
    console.log('9');
    process.nextTick(function() {
        console.log('10');
    })
    new Promise(function(resolve) {
        console.log('11');
        resolve();
    }).then(function() {
        console.log('12')
    })
})

输入后果如下:

1
7
6
8
2
4
3
5
9
11
10
12

(1)第一轮事件循环流程剖析如下:

  • 整体script作为第一个宏工作进入主线程,遇到console.log,输入1。
  • 遇到setTimeout,其回调函数被散发到宏工作Event Queue中。暂且记为setTimeout1
  • 遇到process.nextTick(),其回调函数被散发到微工作Event Queue中。记为process1
  • 遇到Promisenew Promise间接执行,输入7。then被散发到微工作Event Queue中。记为then1
  • 又遇到了setTimeout,其回调函数被散发到宏工作Event Queue中,记为setTimeout2
宏工作Event Queue 微工作Event Queue
setTimeout1 process1
setTimeout2 then1

上表是第一轮事件循环宏工作完结时各Event Queue的状况,此时曾经输入了1和7。发现了process1then1两个微工作:

  • 执行process1,输入6。
  • 执行then1,输入8。

第一轮事件循环正式完结,这一轮的后果是输入1,7,6,8。

(2)第二轮工夫循环从**setTimeout1**宏工作开始:

  • 首先输入2。接下来遇到了process.nextTick(),同样将其散发到微工作Event Queue中,记为process2
  • new Promise立刻执行输入4,then也散发到微工作Event Queue中,记为then2
宏工作Event Queue 微工作Event Queue
setTimeout2 process2
then2

第二轮事件循环宏工作完结,发现有process2then2两个微工作能够执行:

  • 输入3。
  • 输入5。

第二轮事件循环完结,第二轮输入2,4,3,5。

(3)第三轮事件循环开始,此时只剩setTimeout2了,执行。

  • 间接输入9。
  • process.nextTick()散发到微工作Event Queue中。记为process3
  • 间接执行new Promise,输入11。
  • then散发到微工作Event Queue中,记为then3
宏工作Event Queue 微工作Event Queue
process3
then3

第三轮事件循环宏工作执行完结,执行两个微工作process3then3

  • 输入10。
  • 输入12。

第三轮事件循环完结,第三轮输入9,11,10,12。

整段代码,共进行了三次事件循环,残缺的输入为1,7,6,8,2,4,3,5,9,11,10,12。

NaN 是什么,用 typeof 会输入什么?

Not a Number,示意非数字,typeof NaN === ‘number’

如何优化动画?

对于如何优化动画,咱们晓得,个别状况下,动画须要频繁的操作DOM,就就会导致页面的性能问题,咱们能够将动画的position属性设置为absolute或者fixed,将动画脱离文档流,这样他的回流就不会影响到页面了。

回流与重绘的概念及触发条件

(1)回流

当渲染树中局部或者全副元素的尺寸、构造或者属性发生变化时,浏览器会从新渲染局部或者全副文档的过程就称为回流

上面这些操作会导致回流:

  • 页面的首次渲染
  • 浏览器的窗口大小发生变化
  • 元素的内容发生变化
  • 元素的尺寸或者地位发生变化
  • 元素的字体大小发生变化
  • 激活CSS伪类
  • 查问某些属性或者调用某些办法
  • 增加或者删除可见的DOM元素

在触发回流(重排)的时候,因为浏览器渲染页面是基于流式布局的,所以当触发回流时,会导致四周的DOM元素重新排列,它的影响范畴有两种:

  • 全局范畴:从根节点开始,对整个渲染树进行从新布局
  • 部分范畴:对渲染树的某局部或者一个渲染对象进行从新布局

(2)重绘

当页面中某些元素的款式发生变化,然而不会影响其在文档流中的地位时,浏览器就会对元素进行从新绘制,这个过程就是重绘

上面这些操作会导致回流:

  • color、background 相干属性:background-color、background-image 等
  • outline 相干属性:outline-color、outline-width 、text-decoration
  • border-radius、visibility、box-shadow

留神: 当触发回流时,肯定会触发重绘,然而重绘不肯定会引发回流。

函数柯里化

什么叫函数柯里化?其实就是将应用多个参数的函数转换成一系列应用一个参数的函数的技术。还不懂?来举个例子。

function add(a, b, c) {
    return a + b + c
}
add(1, 2, 3)
let addCurry = curry(add)
addCurry(1)(2)(3)

当初就是要实现 curry 这个函数,使函数从一次调用传入多个参数变成屡次调用每次传一个参数。

function curry(fn) {
    let judge = (...args) => {
        if (args.length == fn.length) return fn(...args)
        return (...arg) => judge(...args, ...arg)
    }
    return judge
}

代码输入后果

const first = () => (new Promise((resolve, reject) => {
    console.log(3);
    let p = new Promise((resolve, reject) => {
        console.log(7);
        setTimeout(() => {
            console.log(5);
            resolve(6);
            console.log(p)
        }, 0)
        resolve(1);
    });
    resolve(2);
    p.then((arg) => {
        console.log(arg);
    });
}));
first().then((arg) => {
    console.log(arg);
});
console.log(4);

输入后果如下:

3
7
4
1
2
5
Promise{<resolved>: 1}

代码的执行过程如下:

  1. 首先会进入Promise,打印出3,之后进入上面的Promise,打印出7;
  2. 遇到了定时器,将其退出宏工作队列;
  3. 执行Promise p中的resolve,状态变为resolved,返回值为1;
  4. 执行Promise first中的resolve,状态变为resolved,返回值为2;
  5. 遇到p.then,将其退出微工作队列,遇到first().then,将其退出工作队列;
  6. 执行里面的代码,打印出4;
  7. 这样第一轮宏工作就执行完了,开始执行微工作队列中的工作,先后打印出1和2;
  8. 这样微工作就执行完了,开始执行下一轮宏工作,宏工作队列中有一个定时器,执行它,打印出5,因为执行曾经变为resolved状态,所以resolve(6)不会再执行;
  9. 最初console.log(p)打印出Promise{<resolved>: 1}

symbol 有什么用途

能够用来示意一个举世无双的变量避免命名抵触。然而面试官问还有吗?我没想出其余的用途就间接答我不晓得了,还能够利用 symbol 不会被惯例的办法(除了 Object.getOwnPropertySymbols 外)遍历到,所以能够用来模仿公有变量。

次要用来提供遍历接口,安排了 symbol.iterator 的对象才能够应用 for···of 循环,能够对立解决数据结构。调用之后回返回一个遍历器对象,蕴含有一个 next 办法,应用 next 办法后有两个返回值 value 和 done 别离示意函数以后执行地位的值和是否遍历结束。

Symbol.for() 能够在全局拜访 symbol

箭头函数和一般函数有啥区别?箭头函数能当构造函数吗?

  • 一般函数通过 function 关键字定义, this 无奈联合词法作用域应用,在运行时绑定,只取决于函数的调用形式,在哪里被调用,调用地位。(取决于调用者,和是否独立运行)
  • 箭头函数应用被称为 “胖箭头” 的操作 => 定义,箭头函数不利用一般函数 this 绑定的四种规定,而是依据外层(函数或全局)的作用域来决定 this,且箭头函数的绑定无奈被批改(new 也不行)。

    • 箭头函数罕用于回调函数中,包含事件处理器或定时器
    • 箭头函数和 var self = this,都试图取代传统的 this 运行机制,将 this 的绑定拉回到词法作用域
    • 没有原型、没有 this、没有 super,没有 arguments,没有 new.target
    • 不能通过 new 关键字调用

      • 一个函数外部有两个办法:[[Call]] 和 [[Construct]],在通过 new 进行函数调用时,会执行 [[construct]] 办法,创立一个实例对象,而后再执行这个函数体,将函数的 this 绑定在这个实例对象上
      • 当间接调用时,执行 [[Call]] 办法,间接执行函数体
      • 箭头函数没有 [[Construct]] 办法,不能被用作结构函数调用,当应用 new 进行函数调用时会报错。
function foo() {
  return (a) => {
    console.log(this.a);
  }
}

var obj1 = {
  a: 2
}

var obj2 = {
  a: 3 
}

var bar = foo.call(obj1);
bar.call(obj2);

代码输入问题

function fun(n, o) {
  console.log(o)
  return {
    fun: function(m){
      return fun(m, n);
    }
  };
}
var a = fun(0);  a.fun(1);  a.fun(2);  a.fun(3);
var b = fun(0).fun(1).fun(2).fun(3);
var c = fun(0).fun(1);  c.fun(2);  c.fun(3);

输入后果:

undefined  0  0  0
undefined  0  1  2
undefined  0  1  1

这是一道对于闭包的题目,对于fun办法,调用之后返回的是一个对象。咱们晓得,当调用函数的时候传入的实参比函数申明时指定的形参个数要少,剩下的形参都将设置为undefined值。所以 console.log(o); 会输入undefined。而a就是是fun(0)返回的那个对象。也就是说,函数fun中参数 n 的值是0,而返回的那个对象中,须要一个参数n,而这个对象的作用域中没有n,它就持续沿着作用域向上一级的作用域中寻找n,最初在函数fun中找到了n,n的值是0。理解了这一点,其余运算就很简略了,以此类推。

PWA应用过吗?serviceWorker的应用原理是啥?

渐进式网络应用(PWA)是谷歌在2015年底提出的概念。基本上算是web应用程序,但在外观和感觉上与原生app相似。反对PWA的网站能够提供脱机工作、推送告诉和设施硬件拜访等性能。

Service Worker是浏览器在后盾独立于网页运行的脚本,它关上了通向不须要网页或用户交互的性能的大门。 当初,它们已包含如推送告诉和后盾同步等性能。 未来,Service Worker将会反对如定期同步或天文围栏等其余性能。 本教程探讨的外围性能是拦挡和解决网络申请,包含通过程序来治理缓存中的响应。

10 个 Ajax 同时发动申请,全副返回展现后果,并且至少容许三次失败,说出设计思路

这个问题置信很多人会第一工夫想到 Promise.all ,然而这个函数有一个局限在于如果失败一次就返回了,间接这样实现会有点问题,须要变通下。以下是两种实现思路

// 以下是不残缺代码,着重于思路 非 Promise 写法
let successCount = 0
let errorCount = 0
let datas = []
ajax(url, (res) => {
     if (success) {
         success++
         if (success + errorCount === 10) {
             console.log(datas)
         } else {
             datas.push(res.data)
         }
     } else {
         errorCount++
         if (errorCount > 3) {
            // 失败次数大于3次就应该报错了
             throw Error('失败三次')
         }
     }
})
// Promise 写法
let errorCount = 0
let p = new Promise((resolve, reject) => {
    if (success) {
         resolve(res.data)
     } else {
         errorCount++
         if (errorCount > 3) {
            // 失败次数大于3次就应该报错了
            reject(error)
         } else {
             resolve(error)
         }
     }
})
Promise.all([p]).then(v => {
  console.log(v);
});

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理