img 的 srcset 属性的作⽤?
响应式页面中常常用到依据屏幕密度设置不同的图片。这时就用到了 img 标签的 srcset 属性。srcset 属性用于设置不同屏幕密度下,img 会主动加载不同的图片。用法如下:
<img src="image-128.png" srcset="image-256.png 2x" />
应用下面的代码,就能实现在屏幕密度为 1x 的状况下加载 image-128.png, 屏幕密度为 2x 时加载 image-256.png。
依照下面的实现,不同的屏幕密度都要设置图片地址,目前的屏幕密度有 1x,2x,3x,4x 四种,如果每一个图片都设置 4 张图片,加载就会很慢。所以就有了新的 srcset 规范。代码如下:
<img src="image-128.png"
srcset="image-128.png 128w, image-256.png 256w, image-512.png 512w"
sizes="(max-width: 360px) 340px, 128px" />
其中 srcset 指定图片的地址和对应的图片品质。sizes 用来设置图片的尺寸零界点。对于 srcset 中的 w 单位,能够了解成图片品质。如果可视区域小于这个品质的值,就能够应用。浏览器会主动抉择一个最小的可用图片。
sizes 语法如下:
sizes="[media query] [length], [media query] [length] ..."
sizes 就是指默认显示 128px, 如果视区宽度大于 360px, 则显示 340px。
左右居中计划
- 行内元素:
text-align: center
- 定宽块状元素: 左右
margin
值为auto
- 不定宽块状元素:
table
布局,position + transform
/* 计划 1 */
.wrap {text-align: center}
.center {
display: inline;
/* or */
/* display: inline-block; */
}
/* 计划 2 */
.center {
width: 100px;
margin: 0 auto;
}
/* 计划 2 */
.wrap {position: relative;}
.center {
position: absulote;
left: 50%;
transform: translateX(-50%);
}
Loader 和 Plugin 有什么区别
Loader:直译为 ” 加载器 ”。Webpack 将所有文件视为模块,然而 webpack 原生是只能解析 js 文件,如果想将其余文件也打包的话,就会用到loader
。所以 Loader 的作用是让 webpack 领有了加载和解析非 JavaScript 文件的能力。Plugin:直译为 ” 插件 ”。Plugin 能够扩大 webpack 的性能,让 webpack 具备更多的灵活性。在 Webpack 运行的生命周期中会播送出许多事件,Plugin 能够监听这些事件,在适合的机会通过 Webpack 提供的 API 扭转输入后果。
什么是 JavaScript 中的包装类型?
在 JavaScript 中,根本类型是没有属性和办法的,然而为了便于操作根本类型的值,在调用根本类型的属性或办法时 JavaScript 会在后盾隐式地将根本类型的值转换为对象,如:
const a = "abc";
a.length; // 3
a.toUpperCase(); // "ABC"
在拜访 'abc'.length
时,JavaScript 将 'abc'
在后盾转换成 String('abc')
,而后再拜访其length
属性。
JavaScript 也能够应用 Object
函数显式地将根本类型转换为包装类型:
var a = 'abc'
Object(a) // String {"abc"}
也能够应用 valueOf
办法将包装类型倒转成根本类型:
var a = 'abc'
var b = Object(a)
var c = b.valueOf() // 'abc'
看看如下代码会打印出什么:
var a = new Boolean(false);
if (!a) {console.log( "Oops"); // never runs
}
答案是什么都不会打印,因为尽管包裹的根本类型是 false
,然而false
被包裹成包装类型后就成了对象,所以其非值为false
,所以循环体中的内容不会运行。
为什么函数的 arguments 参数是类数组而不是数组?如何遍历类数组?
arguments
是一个对象,它的属性是从 0 开始顺次递增的数字,还有 callee
和length
等属性,与数组类似;然而它却没有数组常见的办法属性,如 forEach
, reduce
等,所以叫它们类数组。
要遍历类数组,有三个办法:
(1)将数组的办法利用到类数组上,这时候就能够应用 call
和apply
办法,如:
function foo(){Array.prototype.forEach.call(arguments, a => console.log(a))
}
(2)应用 Array.from 办法将类数组转化成数组:
function foo(){const arrArgs = Array.from(arguments)
arrArgs.forEach(a => console.log(a))
}
(3)应用开展运算符将类数组转化成数组
function foo(){const arrArgs = [...arguments]
arrArgs.forEach(a => console.log(a))
}
代码输入后果
function runAsync(x) {
const p = new Promise(r =>
setTimeout(() => r(x, console.log(x)), 1000)
);
return p;
}
function runReject(x) {const p = new Promise((res, rej) =>
setTimeout(() => rej(`Error: ${x}`, console.log(x)), 1000 * x)
);
return p;
}
Promise.race([runReject(0), runAsync(1), runAsync(2), runAsync(3)])
.then(res => console.log("result:", res))
.catch(err => console.log(err));
输入后果如下:
0
Error: 0
1
2
3
能够看到在 catch 捕捉到第一个谬误之后,前面的代码还不执行,不过不会再被捕捉了。
留神:all
和 race
传入的数组中如果有会抛出异样的异步工作,那么只有最先抛出的谬误会被捕捉,并且是被 then 的第二个参数或者前面的 catch 捕捉;但并不会影响数组中其它的异步工作的执行。
参考 前端进阶面试题具体解答
事件是什么?事件模型?
事件是用户操作网页时产生的交互动作,比方 click/move,事件除了用户触发的动作外,还能够是文档加载,窗口滚动和大小调整。事件被封装成一个 event 对象,蕴含了该事件产生时的所有相干信息(event 的属性)以及能够对事件进行的操作(event 的办法)。
事件是用户操作网页时产生的交互动作或者网页自身的一些操作,古代浏览器一共有三种事件模型:
- DOM0 级事件模型,这种模型不会流传,所以没有事件流的概念,然而当初有的浏览器反对以冒泡的形式实现,它能够在网页中间接定义监听函数,也能够通过 js 属性来指定监听函数。所有浏览器都兼容这种形式。间接在 dom 对象上注册事件名称,就是 DOM0 写法。
- IE 事件模型,在该事件模型中,一次事件共有两个过程,事件处理阶段和事件冒泡阶段。事件处理阶段会首先执行指标元素绑定的监听事件。而后是事件冒泡阶段,冒泡指的是事件从指标元素冒泡到 document,顺次查看通过的节点是否绑定了事件监听函数,如果有则执行。这种模型通过 attachEvent 来增加监听函数,能够增加多个监听函数,会按程序顺次执行。
- DOM2 级事件模型,在该事件模型中,一次事件共有三个过程,第一个过程是事件捕捉阶段。捕捉指的是事件从 document 始终向下流传到指标元素,顺次查看通过的节点是否绑定了事件监听函数,如果有则执行。前面两个阶段和 IE 事件模型的两个阶段雷同。这种事件模型,事件绑定的函数是 addEventListener,其中第三个参数能够指定事件是否在捕捉阶段执行。
== 操作符的强制类型转换规定?
对于 ==
来说,如果比照单方的类型 不一样 ,就会进行 类型转换。如果比照 x
和 y
是否雷同,就会进行如下判断流程:
- 首先会判断两者类型是否 雷同,雷同的话就比拟两者的大小;
- 类型不雷同的话,就会进行类型转换;
- 会先判断是否在比照
null
和undefined
,是的话就会返回true
- 判断两者类型是否为
string
和number
,是的话就会将字符串转换为number
1 == '1'
↓
1 == 1
- 判断其中一方是否为
boolean
,是的话就会把boolean
转为number
再进行判断
'1' == true
↓
'1' == 1
↓
1 == 1
- 判断其中一方是否为
object
且另一方为string
、number
或者symbol
,是的话就会把object
转为原始类型再进行判断
'1' == {name: 'js'} ↓'1' == '[object Object]'
原型批改、重写
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
对 rest 参数的了解
扩大运算符被用在函数形参上时,它还能够把一个拆散的参数序列整合成一个数组:
function mutiple(...args) {
let result = 1;
for (var val of args) {result *= val;}
return result;
}
mutiple(1, 2, 3, 4) // 24
这里,传入 mutiple 的是四个拆散的参数,然而如果在 mutiple 函数里尝试输入 args 的值,会发现它是一个数组:
function mutiple(...args) {console.log(args)
}
mutiple(1, 2, 3, 4) // [1, 2, 3, 4]
这就是 … rest 运算符的又一层威力了,它能够把函数的多个入参收敛进一个数组里。这一点 常常用于获取函数的多余参数,或者像下面这样解决函数参数个数不确定的状况。
JS 整数是怎么示意的?
- 通过 Number 类型来示意,遵循 IEEE754 规范,通过 64 位来示意一个数字,(1 + 11 + 52),最大平安数字是 Math.pow(2, 53) – 1,对于 16 位十进制。(符号位 + 指数位 + 小数局部无效位)
代码输入后果
function runAsync (x) {const p = new Promise(r => setTimeout(() => r(x, console.log(x)), 1000))
return p
}
function runReject (x) {const p = new Promise((res, rej) => setTimeout(() => rej(`Error: ${x}`, console.log(x)), 1000 * x))
return p
}
Promise.all([runAsync(1), runReject(4), runAsync(3), runReject(2)])
.then(res => console.log(res))
.catch(err => console.log(err))
输入后果如下:
// 1s 后输入
1
3
// 2s 后输入
2
Error: 2
// 4s 后输入
4
能够看到。catch 捕捉到了第一个谬误,在这道题目中最先的谬误就是 runReject(2)
的后果。如果一组异步操作中有一个异样都不会进入 .then()
的第一个回调函数参数中。会被 .then()
的第二个回调函数捕捉。
new 操作符的实现原理
new 操作符的执行过程:
(1)首先创立了一个新的空对象
(2)设置原型,将对象的原型设置为函数的 prototype 对象。
(3)让函数的 this 指向这个对象,执行构造函数的代码(为这个新对象增加属性)
(4)判断函数的返回值类型,如果是值类型,返回创立的对象。如果是援用类型,就返回这个援用类型的对象。
具体实现:
function objectFactory() {
let newObject = null;
let constructor = Array.prototype.shift.call(arguments);
let result = null;
// 判断参数是否是一个函数
if (typeof constructor !== "function") {console.error("type error");
return;
}
// 新建一个空对象,对象的原型为构造函数的 prototype 对象
newObject = Object.create(constructor.prototype);
// 将 this 指向新建对象,并执行函数
result = constructor.apply(newObject, arguments);
// 判断返回对象
let flag = result && (typeof result === "object" || typeof result === "function");
// 判断返回后果
return flag ? result : newObject;
}
// 应用办法
objectFactory(构造函数, 初始化参数);
常见的 DOM 操作有哪些
1)DOM 节点的获取
DOM 节点的获取的 API 及应用:
getElementById // 依照 id 查问
getElementsByTagName // 依照标签名查问
getElementsByClassName // 依照类名查问
querySelectorAll // 依照 css 选择器查问
// 依照 id 查问
var imooc = document.getElementById('imooc') // 查问到 id 为 imooc 的元素
// 依照标签名查问
var pList = document.getElementsByTagName('p') // 查问到标签为 p 的汇合
console.log(divList.length)
console.log(divList[0])
// 依照类名查问
var moocList = document.getElementsByClassName('mooc') // 查问到类名为 mooc 的汇合
// 依照 css 选择器查问
var pList = document.querySelectorAll('.mooc') // 查问到类名为 mooc 的汇合
2)DOM 节点的创立
创立一个新节点,并把它增加到指定节点的前面。 已知的 HTML 构造如下:
<html>
<head>
<title>DEMO</title>
</head>
<body>
<div id="container">
<h1 id="title"> 我是题目 </h1>
</div>
</body>
</html>
要求增加一个有内容的 span 节点到 id 为 title 的节点前面,做法就是:
// 首先获取父节点
var container = document.getElementById('container')
// 创立新节点
var targetSpan = document.createElement('span')
// 设置 span 节点的内容
targetSpan.innerHTML = 'hello world'
// 把新创建的元素塞进父节点里去
container.appendChild(targetSpan)
3)DOM 节点的删除
删除指定的 DOM 节点, 已知的 HTML 构造如下:
<html>
<head>
<title>DEMO</title>
</head>
<body>
<div id="container"> <h1 id="title"> 我是题目 </h1>
</div> </body>
</html>
须要删除 id 为 title 的元素,做法是:
// 获取指标元素的父元素
var container = document.getElementById('container')
// 获取指标元素
var targetNode = document.getElementById('title')
// 删除指标元素
container.removeChild(targetNode)
或者通过子节点数组来实现删除:
// 获取指标元素的父元素 var container = document.getElementById('container')// 获取指标元素 var targetNode = container.childNodes[1]// 删除指标元素 container.removeChild(targetNode)
4)批改 DOM 元素
批改 DOM 元素这个动作能够分很多维度,比如说挪动 DOM 元素的地位,批改 DOM 元素的属性等。
将指定的两个 DOM 元素替换地位, 已知的 HTML 构造如下:
<html>
<head>
<title>DEMO</title>
</head>
<body>
<div id="container"> <h1 id="title"> 我是题目 </h1>
<p id="content"> 我是内容 </p>
</div> </body>
</html>
当初须要调换 title 和 content 的地位,能够思考 insertBefore 或者 appendChild:
// 获取父元素
var container = document.getElementById('container')
// 获取两个须要被替换的元素
var title = document.getElementById('title')
var content = document.getElementById('content')
// 替换两个元素,把 content 置于 title 后面
container.insertBefore(content, title)
Promise.resolve
Promise.resolve = function(value) {
// 1. 如果 value 参数是一个 Promise 对象,则一成不变返回该对象
if(value instanceof Promise) return value;
// 2. 如果 value 参数是一个具备 then 办法的对象,则将这个对象转为 Promise 对象,并立刻执行它的 then 办法
if(typeof value === "object" && 'then' in value) {return new Promise((resolve, reject) => {value.then(resolve, reject);
});
}
// 3. 否则返回一个新的 Promise 对象,状态为 fulfilled
return new Promise(resolve => resolve(value));
}
JavaScript 有哪些内置对象
全局的对象(global objects)或称规范内置对象,不要和 “ 全局对象(global object)” 混同。这里说的全局的对象是说在
全局作用域里的对象。全局作用域中的其余对象能够由用户的脚本创立或由宿主程序提供。
规范内置对象的分类:
(1)值属性,这些全局属性返回一个简略值,这些值没有本人的属性和办法。例如 Infinity、NaN、undefined、null 字面量
(2)函数属性,全局函数能够间接调用,不须要在调用时指定所属对象,执行完结后会将后果间接返回给调用者。例如 eval()、parseFloat()、parseInt() 等
(3)根本对象,根本对象是定义或应用其余对象的根底。根本对象包含个别对象、函数对象和谬误对象。例如 Object、Function、Boolean、Symbol、Error 等
(4)数字和日期对象,用来示意数字、日期和执行数学计算的对象。例如 Number、Math、Date
(5)字符串,用来示意和操作字符串的对象。例如 String、RegExp
(6)可索引的汇合对象,这些对象示意依照索引值来排序的数据汇合,包含数组和类型数组,以及类数组构造的对象。例如 Array
(7)应用键的汇合对象,这些汇合对象在存储数据时会应用到键,反对依照插入程序来迭代元素。
例如 Map、Set、WeakMap、WeakSet
(8)矢量汇合,SIMD 矢量汇合中的数据会被组织为一个数据序列。
例如 SIMD 等
(9)结构化数据,这些对象用来示意和操作结构化的缓冲区数据,或应用 JSON 编码的数据。例如 JSON 等
(10)管制形象对象
例如 Promise、Generator 等
(11)反射。例如 Reflect、Proxy
(12)国际化,为了反对多语言解决而退出 ECMAScript 的对象。例如 Intl、Intl.Collator 等
(13)WebAssembly
(14)其余。例如 arguments
总结: js 中的内置对象次要指的是在程序执行前存在全局作用域里的由 js 定义的一些全局值属性、函数和用来实例化其余对象的构造函数对象。个别常常用到的如全局变量值 NaN、undefined,全局函数如 parseInt()、parseFloat() 用来实例化对象的构造函数如 Date、Object 等,还有提供数学计算的单体内置对象如 Math 对象。
JavaScript 类数组对象的定义?
一个领有 length 属性和若干索引属性的对象就能够被称为类数组对象,类数组对象和数组相似,然而不能调用数组的办法。常见的类数组对象有 arguments 和 DOM 办法的返回后果,还有一个函数也能够被看作是类数组对象,因为它含有 length 属性值,代表可接管的参数个数。
常见的类数组转换为数组的办法有这样几种:
(1)通过 call 调用数组的 slice 办法来实现转换
Array.prototype.slice.call(arrayLike);
(2)通过 call 调用数组的 splice 办法来实现转换
Array.prototype.splice.call(arrayLike, 0);
(3)通过 apply 调用数组的 concat 办法来实现转换
Array.prototype.concat.apply([], arrayLike);
(4)通过 Array.from 办法来实现转换
Array.from(arrayLike);
对类数组对象的了解,如何转化为数组
一个领有 length 属性和若干索引属性的对象就能够被称为类数组对象,类数组对象和数组相似,然而不能调用数组的办法。常见的类数组对象有 arguments 和 DOM 办法的返回后果,函数参数也能够被看作是类数组对象,因为它含有 length 属性值,代表可接管的参数个数。
常见的类数组转换为数组的办法有这样几种:
- 通过 call 调用数组的 slice 办法来实现转换
Array.prototype.slice.call(arrayLike);
- 通过 call 调用数组的 splice 办法来实现转换
Array.prototype.splice.call(arrayLike, 0);
- 通过 apply 调用数组的 concat 办法来实现转换
Array.prototype.concat.apply([], arrayLike);
- 通过 Array.from 办法来实现转换
Array.from(arrayLike);
说一下 JSON.stringify 有什么毛病?
1. 如果 obj 外面有工夫对象,则 JSON.stringify 后再 JSON.parse 的后果,工夫将只是字符串的模式,而不是对象的模式
2. 如果 obj 里有 RegExp(正则表达式的缩写)、Error 对象,则序列化的后果将只失去空对象;3、如果 obj 里有函数,undefined,则序列化的后果会把函数或 undefined 失落;4、如果 obj 里有 NaN、Infinity 和 -Infinity,则序列化的后果会变成 null
5、JSON.stringify()只能序列化对象的可枚举的自有属性,例如 如果 obj 中的对象是有构造函数生成的,则应用 JSON.parse(JSON.stringify(obj))深拷贝后,会抛弃对象的 constructor;6、如果对象中存在循环援用的状况也无奈正确实现深拷贝;
CSS 预处理器 / 后处理器是什么?为什么要应用它们?
预处理器, 如:less
,sass
,stylus
,用来预编译 sass
或者 less
,减少了css
代码的复用性。层级,mixin
,变量,循环,函数等对编写以及开发 UI 组件都极为不便。
后处理器, 如:postCss
,通常是在实现的样式表中依据 css
标准解决 css
,让其更加无效。目前最常做的是给css
属性增加浏览器公有前缀,实现跨浏览器兼容性的问题。
css
预处理器为 css
减少一些编程个性,无需思考浏览器的兼容问题,能够在 CSS
中应用变量,简略的逻辑程序,函数等在编程语言中的一些根本的性能,能够让 css
更加的简洁,减少适应性以及可读性,可维护性等。
其它 css
预处理器语言:Sass(Scss)
, Less
, Stylus
, Turbine
, Swithch css
, CSS Cacheer
, DT Css
。
应用起因:
- 构造清晰,便于扩大
- 能够很不便的屏蔽浏览器公有语法的差别
- 能够轻松实现多重继承
- 完满的兼容了
CSS
代码,能够利用到老我的项目中