乐趣区

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

什么是 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);
});
退出移动版