关于javascript:金九银十前端面试题总结附答案

50次阅读

共计 11381 个字符,预计需要花费 29 分钟才能阅读完成。

代码输入后果

async function async1() {console.log("async1 start");
  await async2();
  console.log("async1 end");
}
async function async2() {console.log("async2");
}
async1();
console.log('start')

输入后果如下:

async1 start
async2
start
async1 end

代码的执行过程如下:

  1. 首先执行函数中的同步代码 async1 start,之后遇到了await,它会阻塞async1 前面代码的执行,因而会先去执行 async2 中的同步代码async2,而后跳出async1
  2. 跳出 async1 函数后,执行同步代码start
  3. 在一轮宏工作全副执行完之后,再来执行 await 前面的内容async1 end

这里能够了解为 await 前面的语句相当于放到了 new Promise 中,下一行及之后的语句相当于放在 Promise.then 中。

介绍 Loader

罕用 Loader:

  • file-loader: 加载文件资源,如 字体 / 图片 等,具备挪动 / 复制 / 命名等性能;
  • url-loader: 通常用于加载图片,能够将小图片间接转换为 Date Url,缩小申请;
  • babel-loader: 加载 js / jsx 文件,将 ES6 / ES7 代码转换成 ES5,抹平兼容性问题;
  • ts-loader: 加载 ts / tsx 文件,编译 TypeScript;
  • style-loader: 将 css 代码以 <style> 标签的模式插入到 html 中;
  • css-loader: 剖析 @import 和 url(),援用 css 文件与对应的资源;
  • postcss-loader: 用于 css 的兼容性解决,具备泛滥性能,例如 增加前缀,单位转换 等;
  • less-loader / sass-loader: css 预处理器,在 css 中新增了许多语法,进步了开发效率;

编写准则:

  • 繁多准则: 每个 Loader 只做一件事;
  • 链式调用: Webpack 会按程序链式调用每个 Loader;
  • 对立准则: 遵循 Webpack 制订的设计规定和构造,输出与输入均为字符串,各个 Loader 齐全独立,即插即用;

响应式设计的概念及基本原理

响应式网站设计(Responsive Web design)是一个网站可能兼容多个终端,而不是为每一个终端做一个特定的版本。

对于原理:基本原理是通过媒体查问 (@media) 查问检测不同的设施屏幕尺寸做解决。
对于兼容:页面头部必须有 mate 申明的viewport

<meta name="’viewport’" content="”width=device-width," initial-scale="1." maximum-scale="1,user-scalable=no”"/>

margin 和 padding 的应用场景

  • 须要在 border 外侧增加空白,且空白处不须要背景(色)时,应用 margin;
  • 须要在 border 内测增加空白,且空白处须要背景(色)时,应用 padding。

Canvas 和 SVG 的区别

(1)SVG: SVG 可缩放矢量图形(Scalable Vector Graphics)是基于可扩大标记语言 XML 形容的 2D 图形的语言,SVG 基于 XML 就意味着 SVG DOM 中的每个元素都是可用的,能够为某个元素附加 Javascript 事件处理器。在 SVG 中,每个被绘制的图形均被视为对象。如果 SVG 对象的属性发生变化,那么浏览器可能主动重现图形。

其特点如下:

  • 不依赖分辨率
  • 反对事件处理器
  • 最适宜带有大型渲染区域的应用程序(比方谷歌地图)
  • 复杂度高会减慢渲染速度(任何适度应用 DOM 的利用都不快)
  • 不适宜游戏利用

(2)Canvas: Canvas 是画布,通过 Javascript 来绘制 2D 图形,是逐像素进行渲染的。其地位产生扭转,就会从新进行绘制。

其特点如下:

  • 依赖分辨率
  • 不反对事件处理器
  • 弱的文本渲染能力
  • 可能以 .png 或 .jpg 格局保留后果图像
  • 最适宜图像密集型的游戏,其中的许多对象会被频繁重绘

注:矢量图,也称为面向对象的图像或绘图图像,在数学上定义为一系列由线连贯的点。矢量文件中的图形元素称为对象。每个对象都是一个自成一体的实体,它具备色彩、形态、轮廓、大小和屏幕地位等属性。

display:none 与 visibility:hidden 的区别

这两个属性都是让元素暗藏,不可见。两者区别如下:

(1)在渲染树中

  • display:none会让元素齐全从渲染树中隐没,渲染时不会占据任何空间;
  • visibility:hidden不会让元素从渲染树中隐没,渲染的元素还会占据相应的空间,只是内容不可见。

(2)是否是继承属性

  • display:none是非继承属性,子孙节点会随着父节点从渲染树隐没,通过批改子孙节点的属性也无奈显示;
  • visibility:hidden是继承属性,子孙节点隐没是因为继承了 hidden,通过设置visibility:visible 能够让子孙节点显示;
    (3)批改惯例文档流中元素的 display 通常会造成文档的重排,然而批改 visibility 属性只会造成本元素的重绘;

(4)如果应用读屏器,设置为 display:none 的内容不会被读取,设置为 visibility:hidden 的内容会被读取。

::before 和 :after 的双冒号和单冒号有什么区别?

(1)冒号 (:) 用于 CSS3 伪类,双冒号 (::) 用于 CSS3 伪元素。
(2)::before就是以一个子元素的存在,定义在元素主体内容之前的一个伪元素。并不存在于 dom 之中,只存在在页面之中。

留神: :before:after 这两个伪元素,是在 CSS2.1 里新呈现的。起初,伪元素的前缀应用的是单冒号语法,但随着 Web 的进化,在 CSS3 的标准里,伪元素的语法被批改成应用双冒号,成为::before::after

display:inline-block 什么时候会显示间隙?

  • 有空格时会有间隙,能够删除空格解决;
  • margin正值时,能够让 margin 应用负值解决;
  • 应用 font-size 时,可通过设置 font-size:0letter-spacingword-spacing 解决;

对盒模型的了解

CSS3 中的盒模型有以下两种:规范盒子模型、IE 盒子模型 盒模型都是由四个局部组成的,别离是 margin、border、padding 和 content。

规范盒模型和 IE 盒模型的区别在于设置 width 和 height 时,所对应的范畴不同:

  • 规范盒模型的 width 和 height 属性的范畴只蕴含了 content,
  • IE 盒模型的 width 和 height 属性的范畴蕴含了 border、padding 和 content。

能够通过批改元素的 box-sizing 属性来扭转元素的盒模型:

  • box-sizeing: content-box示意规范盒模型(默认值)
  • box-sizeing: border-box示意 IE 盒模型(怪异盒模型)

参考:前端进阶面试题具体解答

箭头函数的 this 指向哪⾥?

箭头函数不同于传统 JavaScript 中的函数,箭头函数并没有属于⾃⼰的 this,它所谓的 this 是捕捉其所在高低⽂的 this 值,作为⾃⼰的 this 值,并且因为没有属于⾃⼰的 this,所以是不会被 new 调⽤的,这个所谓的 this 也不会被扭转。

能够⽤ Babel 了解⼀下箭头函数:

// ES6 
const obj = {getArrow() {return () => {console.log(this === obj); 
    }; 
  } 
}

转化后:

// ES5,由 Babel 转译
var obj = {getArrow: function getArrow() { 
     var _this = this; 
     return function () {console.log(_this === obj); 
     }; 
   } 
};

常见的 CSS 布局单位

罕用的布局单位包含像素(px),百分比(%),emremvw/vh

(1)像素px)是页面布局的根底,一个像素示意终端(电脑、手机、平板等)屏幕所能显示的最小的区域,像素分为两种类型:CSS 像素和物理像素:

  • CSS 像素:为 web 开发者提供,在 CSS 中应用的一个形象单位;
  • 物理像素:只与设施的硬件密度无关,任何设施的物理像素都是固定的。

(2)百分比%),当浏览器的宽度或者高度发生变化时,通过百分比单位能够使得浏览器中的组件的宽和高随着浏览器的变动而变动,从而实现响应式的成果。个别认为子元素的百分比绝对于间接父元素。

(3)em 和 rem绝对于 px 更具灵活性,它们都是绝对长度单位,它们之间的区别:em 绝对于父元素,rem 绝对于根元素。

  • em: 文本绝对长度单位。绝对于以后对象内文本的字体尺寸。如果以后行内文本的字体尺寸未被人为设置,则绝对于浏览器的默认字体尺寸(默认 16px)。(绝对父元素的字体大小倍数)。
  • rem: rem 是 CSS3 新增的一个绝对单位,绝对于根元素(html 元素)的 font-size 的倍数。作用:利用 rem 能够实现简略的响应式布局,能够利用 html 元素中字体的大小与屏幕间的比值来设置 font-size 的值,以此实现当屏幕分辨率变动时让元素也随之变动。

(4)vw/vh是与视图窗口无关的单位,vw 示意绝对于视图窗口的宽度,vh 示意绝对于视图窗口高度,除了 vw 和 vh 外,还有 vmin 和 vmax 两个相干的单位。

  • vw:绝对于视窗的宽度,视窗宽度是 100vw;
  • vh:绝对于视窗的高度,视窗高度是 100vh;
  • vmin:vw 和 vh 中的较小值;
  • vmax:vw 和 vh 中的较大值;

vw/vh 和百分比很相似,两者的区别:

  • 百分比(%):大部分绝对于先人元素,也有绝对于本身的状况比方(border-radius、translate 等)
  • vw/vm:绝对于视窗的尺寸

原型批改、重写

function Person(name) {this.name = name}
// 批改原型
Person.prototype.getName = function() {}
var p = new Person('hello')
console.log(p.__proto__ === Person.prototype) // true
console.log(p.__proto__ === p.constructor.prototype) // true
// 重写原型
Person.prototype = {getName: function() {}}
var p = new Person('hello')
console.log(p.__proto__ === Person.prototype)        // true
console.log(p.__proto__ === p.constructor.prototype) // false

能够看到批改原型的时候 p 的构造函数不是指向 Person 了,因为间接给 Person 的原型对象间接用对象赋值时,它的构造函数指向的了根构造函数 Object,所以这时候p.constructor === Object,而不是p.constructor === Person。要想成立,就要用 constructor 指回来:

Person.prototype = {getName: function() {}}
var p = new Person('hello')
p.constructor = Person
console.log(p.__proto__ === Person.prototype)        // true
console.log(p.__proto__ === p.constructor.prototype) // true

强类型语言和弱类型语言的区别

  • 强类型语言:强类型语言也称为强类型定义语言,是一种总是强制类型定义的语言,要求变量的应用要严格合乎定义,所有变量都必须先定义后应用。Java 和 C ++ 等语言都是强制类型定义的,也就是说,一旦一个变量被指定了某个数据类型,如果不通过强制转换,那么它就永远是这个数据类型了。例如你有一个整数,如果不显式地进行转换,你不能将其视为一个字符串。
  • 弱类型语言:弱类型语言也称为弱类型定义语言,与强类型定义相同。JavaScript 语言就属于弱类型语言。简略了解就是一种变量类型能够被疏忽的语言。比方 JavaScript 是弱类型定义的,在 JavaScript 中就能够将字符串 ’12’ 和整数 3 进行连贯失去字符串 ’123’,在相加的时候会进行强制类型转换。

两者比照:强类型语言在速度上可能略逊色于弱类型语言,然而强类型语言带来的严谨性能够无效地帮忙防止许多谬误。

实现 call、apply 及 bind 函数

(1)call 函数的实现步骤:

  • 判断调用对象是否为函数,即便是定义在函数的原型上的,然而可能呈现应用 call 等形式调用的状况。
  • 判断传入上下文对象是否存在,如果不存在,则设置为 window。
  • 解决传入的参数,截取第一个参数后的所有参数。
  • 将函数作为上下文对象的一个属性。
  • 应用上下文对象来调用这个办法,并保留返回后果。
  • 删除方才新增的属性。
  • 返回后果。
Function.prototype.myCall = function(context) {
  // 判断调用对象
  if (typeof this !== "function") {console.error("type error");
  }
  // 获取参数
  let args = [...arguments].slice(1),
    result = null;
  // 判断 context 是否传入,如果未传入则设置为 window
  context = context || window;
  // 将调用函数设为对象的办法
  context.fn = this;
  // 调用函数
  result = context.fn(...args);
  // 将属性删除
  delete context.fn;
  return result;
};

(2)apply 函数的实现步骤:

  • 判断调用对象是否为函数,即便是定义在函数的原型上的,然而可能呈现应用 call 等形式调用的状况。
  • 判断传入上下文对象是否存在,如果不存在,则设置为 window。
  • 将函数作为上下文对象的一个属性。
  • 判断参数值是否传入
  • 应用上下文对象来调用这个办法,并保留返回后果。
  • 删除方才新增的属性
  • 返回后果
Function.prototype.myApply = function(context) {
  // 判断调用对象是否为函数
  if (typeof this !== "function") {throw new TypeError("Error");
  }
  let result = null;
  // 判断 context 是否存在,如果未传入则为 window
  context = context || window;
  // 将函数设为对象的办法
  context.fn = this;
  // 调用办法
  if (arguments[1]) {result = context.fn(...arguments[1]);
  } else {result = context.fn();
  }
  // 将属性删除
  delete context.fn;
  return result;
};

(3)bind 函数的实现步骤:

  • 判断调用对象是否为函数,即便是定义在函数的原型上的,然而可能呈现应用 call 等形式调用的状况。
  • 保留以后函数的援用,获取其余传入参数值。
  • 创立一个函数返回
  • 函数外部应用 apply 来绑定函数调用,须要判断函数作为构造函数的状况,这个时候须要传入以后函数的 this 给 apply 调用,其余状况都传入指定的上下文对象。
Function.prototype.myBind = function(context) {
  // 判断调用对象是否为函数
  if (typeof this !== "function") {throw new TypeError("Error");
  }
  // 获取参数
  var args = [...arguments].slice(1),
    fn = this;
  return function Fn() {
    // 依据调用形式,传入不同绑定值
    return fn.apply(
      this instanceof Fn ? this : context,
      args.concat(...arguments)
    );
  };
};

如何判断元素是否达到可视区域

以图片显示为例:

  • window.innerHeight 是浏览器可视区的高度;
  • document.body.scrollTop || document.documentElement.scrollTop 是浏览器滚动的过的间隔;
  • imgs.offsetTop 是元素顶部间隔文档顶部的高度(包含滚动条的间隔);
  • 内容达到显示区域的:img.offsetTop < window.innerHeight + document.body.scrollTop;

并发与并行的区别?

  • 并发是宏观概念,我别离有工作 A 和工作 B,在一段时间内通过工作间的切换实现了这两个工作,这种状况就能够称之为并发。
  • 并行是宏观概念,假如 CPU 中存在两个外围,那么我就能够同时实现工作 A、B。同时实现多个工作的状况就能够称之为并行。

label 的作用是什么?如何应用?

label 标签来定义表单控件的关系:当用户抉择 label 标签时,浏览器会主动将焦点转到和 label 标签相干的表单控件上。

  • 应用办法 1:
<label for="mobile">Number:</label>
<input type="text" id="mobile"/>
  • 应用办法 2:
<label>Date:<input type="text"/></label>

对 CSS 工程化的了解

CSS 工程化是为了解决以下问题:

  1. 宏观设计:CSS 代码如何组织、如何拆分、模块构造怎么设计?
  2. 编码优化:怎么写出更好的 CSS?
  3. 构建:如何解决我的 CSS,能力让它的打包后果最优?
  4. 可维护性:代码写完了,如何最小化它后续的变更老本?如何确保任何一个共事都能轻松接手?

以下三个方向都是时下比拟风行的、普适性十分好的 CSS 工程化实际:

  • 预处理器:Less、Sass 等;
  • 重要的工程化插件:PostCss;
  • Webpack loader 等。

基于这三个方向,能够衍生出一些具备典型意义的子问题,这里咱们一一来看:

(1)预处理器:为什么要用预处理器?它的呈现是为了解决什么问题?

预处理器,其实就是 CSS 世界的“轮子”。预处理器反对咱们写一种相似 CSS、但理论并不是 CSS 的语言,而后把它编译成 CSS 代码:那为什么写 CSS 代码写得好好的,偏偏要转去写“类 CSS”呢?这就和原本用 JS 也能够实现所有性能,但最初却写 React 的 jsx 或者 Vue 的模板语法一样——为了爽!要想晓得有了预处理器有多爽,首先要晓得的是传统 CSS 有多不爽。随着前端业务复杂度的进步,前端工程中对 CSS 提出了以下的诉求:

  1. 宏观设计上:咱们心愿能优化 CSS 文件的目录构造,对现有的 CSS 文件实现复用;
  2. 编码优化上:咱们心愿能写出构造清晰、扼要易懂的 CSS,须要它具备高深莫测的嵌套层级关系,而不是无差别的一铺到底写法;咱们心愿它具备变量特色、计算能力、循环能力等等更强的可编程性,这样咱们能够少写一些无用的代码;
  3. 可维护性上:更强的可编程性意味着更优质的代码构造,实现复用意味着更简略的目录构造和更强的拓展能力,这两点如果能做到,天然会带来更强的可维护性。

这三点是传统 CSS 所做不到的,也正是预处理器所解决掉的问题。预处理器广泛会具备这样的个性:

  • 嵌套代码的能力,通过嵌套来反映不同 css 属性之间的层级关系;
  • 反对定义 css 变量;
  • 提供计算函数;
  • 容许对代码片段进行 extend 和 mixin;
  • 反对循环语句的应用;
  • 反对将 CSS 文件模块化,实现复用。

(2)PostCss:PostCss 是如何工作的?咱们在什么场景下会应用 PostCss?

它和预处理器的不同就在于,预处理器解决的是 类 CSS,而 PostCss 解决的就是 CSS 自身。Babel 能够将高版本的 JS 代码转换为低版本的 JS 代码。PostCss 做的是相似的事件:它能够编译尚未被浏览器广泛支持的先进的 CSS 语法,还能够主动为一些须要额定兼容的语法减少前缀。更强的是,因为 PostCss 有着弱小的插件机制,反对各种各样的扩大,极大地强化了 CSS 的能力。

PostCss 在业务中的应用场景十分多:

  • 进步 CSS 代码的可读性:PostCss 其实能够做相似预处理器能做的工作;
  • 当咱们的 CSS 代码须要适配低版本浏览器时,PostCss 的 Autoprefixer 插件能够帮忙咱们主动减少浏览器前缀;
  • 容许咱们编写面向未来的 CSS:PostCss 可能帮忙咱们编译 CSS next 代码;

(3)Webpack 能解决 CSS 吗?如何实现? Webpack 能解决 CSS 吗:

  • Webpack 在裸奔的状态下,是不能解决 CSS 的,Webpack 自身是一个面向 JavaScript 且只能解决 JavaScript 代码的模块化打包工具;
  • Webpack 在 loader 的辅助下,是能够解决 CSS 的。

如何用 Webpack 实现对 CSS 的解决:

  • Webpack 中操作 CSS 须要应用的两个要害的 loader:css-loader 和 style-loader
  • 留神,答出“用什么”有时候可能还不够,面试官会狐疑你是不是在背答案,所以你还须要理解每个 loader 都做了什么事件:

    • css-loader:导入 CSS 模块,对 CSS 代码进行编译解决;
    • style-loader:创立 style 标签,把 CSS 内容写入标签。

在理论应用中,css-loader 的执行程序肯定要安顿在 style-loader 的后面。因为只有实现了编译过程,才能够对 css 代码进行插入;若提前插入了未编译的代码,那么 webpack 是无奈了解这坨货色的,它会无情报错。

继承

原型继承

核心思想:子类的原型成为父类的实例

实现

function SuperType() {this.colors = ['red', 'green'];
}
function SubType() {}
// 原型继承要害: 子类的原型成为父类的实例
SubType.prototype = new SuperType();

// 测试
let instance1 = new SubType();
instance1.colors.push('blue');

let instance2 = new SubType();
console.log(instance2.colors);  // ['red', 'green', 'blue']

原型继承存在的问题

  1. 原型中蕴含的援用类型属性将被所有实例对象共享
  2. 子类在实例化时不能给父类构造函数传参

构造函数继承

核心思想:在子类构造函数中调用父类构造函数

实现

function SuperType(name) {
    this.name = name;
    this.colors = ['red', 'green'];
    this.getName = function() {return this.name;}
}
function SubType(name) {
    // 继承 SuperType 并传参
    SuperType.call(this, name);
}

// 测试
let instance1 = new SubType('instance1');
instance1.colors.push('blue');
console.log(instance1.colors); // ['red','green','blue']

let instance2 = new SubType('instance2');
console.log(instance2.colors);  // ['red', 'green']

构造函数继承 的呈现是为了解决了原型继承的援用值共享问题。长处是能够在子类构造函数中向父类构造函数传参。它存在的问题是:1)因为办法必须在构造函数中定义,因而办法不能重用。2)子类也不能拜访父类原型上定义的办法。

组合继承

核心思想:综合了原型链和构造函数,即,应用原型链继承原型上的办法,而通过构造函数继承实例属性。

实现

function SuperType(name) {
    this.name = name;
    this.colors = ['red', 'green'];
}
Super.prototype.sayName = function() {console.log(this.name);
}
function SubType(name, age) {
    // 继承属性
    SuperType.call(this, name);
    // 实例属性
    this.age = age;
}
// 继承办法
SubType.prototype = new SuperType();

// 测试
let instance1 = new SubType('instance1', 1);
instance1.sayName();  // "instance1"
instance1.colors.push("blue");
console.log(instance1.colors); // ['red','green','blue']

let instance2 = new SubType('instance2', 2);
instance2.sayName();  // "instance2"
console.log(instance2.colors); // ['red','green']

组合继承 存在的问题是:父类构造函数始终会被调用两次:一次是在创立子类原型时 new SuperType() 调用,另一次是在子类构造函数中 SuperType.call() 调用。

寄生式组合继承(最佳)

核心思想:通过构造函数继承属性,但应用混合式原型继承办法,即,不通过调用父类构造函数给子类原型赋值,而是获得父类原型的一个正本。

实现

function SuperType(name) {
    this.name = name;
    this.colors = ['red', 'green'];
}
Super.prototype.sayName = function() {console.log(this.name);
}
function SubType(name, age) {
    // 继承属性
    SuperType.call(this, name);
    this.age = age;
}
// 继承办法
SubType.prototype = Object.create(SuperType.prototype);
// 重写原型导致默认 constructor 失落,手动将 constructor 指回 SubType
SubType.prototype.constructor = SubType;

class 实现继承(ES6)

核心思想:通过 extends 来实现类的继承(相当于 ES5 的原型继承)。通过 super 调用父类的构造方法 (相当于 ES5 的构造函数继承)。

实现

class SuperType {constructor(name) {this.name = name;}
    sayName() {console.log(this.name);
    }
}
class SubType extends SuperType {constructor(name, age) {super(name);  // 继承属性
        this.age = age;
    }
}

// 测试
let instance = new SubType('instance', 0);
instance.sayName();  // "instance"

尽管类继承应用的是新语法,但背地仍旧应用的是原型链。

z-index 属性在什么状况下会生效

通常 z-index 的应用是在有两个重叠的标签,在肯定的状况下管制其中一个在另一个的上方或者下方呈现。z-index 值越大就越是在下层。z-index 元素的 position 属性须要是 relative,absolute 或是 fixed。

z-index 属性在下列状况下会生效:

  • 父元素 position 为 relative 时,子元素的 z -index 生效。解决:父元素 position 改为 absolute 或 static;
  • 元素没有设置 position 属性为非 static 属性。解决:设置该元素的 position 属性为 relative,absolute 或是 fixed 中的一种;
  • 元素在设置 z -index 的同时还设置了 float 浮动。解决:float 去除,改为 display:inline-block;

正文完
 0